why QTableWidget does not refresh after closing tab in pyqt5?

Question:

the below example is my problem which can not figure it out! i appreciate if anyone can help on this problem. i have set of data which in this example i am reading from csv file. in next step i am filling my QTableWidget with the data. The problem is when i change data and close the tab and open it, the data does not refresh.
here is my sample code:

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QAction, QTabWidget, QTableWidget, QTableWidgetItem


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setGeometry(100, 100, 400, 400)

        self.setup_ui()
        self.show()

    def setup_ui(self):
        # toolbar
        self.toolbar = self.addToolBar('Tool Bar')
        self.test_btn = QAction('Test', self)
        self.toolbar.addAction(self.test_btn)
        self.test_btn.triggered.connect(self.load_data)

        # tab
        self.tabs = QTabWidget()
        self.tab_test = QWidget()
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(lambda tab_index: self.tabs.removeTab(tab_index))
        self.setCentralWidget(self.tabs)

    def load_data(self):
        # get data
        data = []
        with open('test_data.csv', 'r') as f:
            header = next(f).replace('n', '').split(',')
            for line in f:
                data.append(line.replace('n', '').split(','))
        print(data)

        # table and button widget
        self.layout = QVBoxLayout()
        self.tabs.addTab(self.tab_test, 'Test')
        self.table = QTableWidget()
        self.layout.addWidget(self.table)
        self.tab_test.setLayout(self.layout)

        # fill headers
        self.table.setColumnCount(len(header))
        for i, field_name in enumerate(header):
            self.table.setHorizontalHeaderItem(i, QTableWidgetItem(field_name))

        # fill data
        for row, row_data in enumerate(data):
            self.table.insertRow(row)
            for col, data in enumerate(row_data):
                self.table.setItem(row, col, QTableWidgetItem(str(data)))


def main():
    app = QApplication([])
    ui = MainWindow()
    app.exec_()


if __name__ == '__main__':
    main()

and this is the sample data for test_data.csv:

col_1,col_2
1,2
10,20
100,200

before updating data:

enter image description here

but after updating 100 to 1000 and 200 to 2000 in csv file then closing tab and opening it, the data has been updated but the items of table not!

enter image description here

enter image description here

what am i missing?

Asked By: Rahman Tavakoli

||

Answers:

You’re assigning a single QWidget instance at MainWindow.setup_ui, right here:

def setup_ui(self):
    # toolbar
    self.toolbar = self.addToolBar('Tool Bar')
    self.test_btn = QAction('Test', self)
    # .
    # .

    # tab
    self.tabs = QTabWidget() # <--
    # .
    # .
    # .
    self.setCentralWidget(self.tabs)

This means that you’re always adding the same QWidget over and over to the QTabWidget. So, when you call MainWindow.load_data, this function call self.tabs.addTab(tab_test, 'Test') does nothing, because the widget is already present on the table widget.

What you should do, is to create a new QWidget inside MainWindow.load_data, just like this:

from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QAction, QTabWidget, QTableWidget, QTableWidgetItem


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setGeometry(100, 100, 400, 400)

        self.setup_ui()
        self.show()

    def setup_ui(self):
        # toolbar
        toolbar = self.addToolBar('Tool Bar')
        test_btn = QAction('Test', self)
        toolbar.addAction(test_btn)
        test_btn.triggered.connect(self.load_data)

        # tab
        self.tabs = QTabWidget()
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(lambda tab_index: self.tabs.removeTab(tab_index))
        self.setCentralWidget(self.tabs)

    def load_data(self):
        # get data
        data = []
        with open('test_data.csv', 'r') as f:
            header = next(f).replace('n', '').split(',')
            for line in f:
                data.append(line.replace('n', '').split(','))
        print(data)

        # table and button widget
        layout = QVBoxLayout()
        tab_test = QWidget()               ## MOVED THIS LINE FROM setup_ui
        self.tabs.addTab(tab_test, 'Test')
        table = QTableWidget()
        layout.addWidget(table)
        tab_test.setLayout(layout)

        # fill headers
        table.setColumnCount(len(header))
        for i, field_name in enumerate(header):
            table.setHorizontalHeaderItem(i, QTableWidgetItem(field_name))

        # fill data
        for row, row_data in enumerate(data):
            table.insertRow(row)
            for col, data in enumerate(row_data):
                table.setItem(row, col, QTableWidgetItem(str(data)))


def main():
    app = QApplication([])
    ui = MainWindow()
    app.exec_()


if __name__ == '__main__':
    main()

Note that I used local variables on setup_ui and load_data scopes. As most variables in there are not being used in other MainWindow methods, it’s not needed to store them to self, unlike self.tabs. Also, as every tab should be independent of each other, it’s not a good thing to overwrite the self.test_tab variable, unless you do need to store only the last added tab.

Answered By: Carl HR
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.