Unsolved How to notify table Widget from different thread that data is updated.
-
I have a callback function:
def data_callback(self, data): for dd in data: index = self.get_index(dd ["ind"]) val1= dd["val1"] val2 = "xx" self.tableWidget.item(index, 2).setText(str(val1)) self.tableWidget.item(index, 3).setText(str(val2)) # self.tableWidget.itemChanged(self.tableWidget.itemAt(index, 2))
This callback function get data from a websocket. The problem is that I am not able to see the updated data. I have to make some event on the table Widget (like clicking on a cell) to get the new modified data. So I am looking for a way to notify table Widget that data is modified.
CODE::
from PyQt5 import QtWidgets, uic from PyQt5.QtWidgets import QTableWidgetItem, QHeaderView class TEST(QtWidgets.QFrame): def __init__(self, api): self.api = api super(TEST, self).__init__() uic.loadUi('./UI/DATA.ui', self) self.data = [1, 2, 3, 4] self.tableWidget = self.findChild(QtWidgets.QTableWidget, 'tableWidget') self.pushButton = self.findChild(QtWidgets.QPushButton, 'pushButton') self.pushButton.clicked.connect(self.action_push_button) self.tableWidget.horizontalHeader().setStretchLastSection(True) self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tableWidget.setColumnCount(4) self.tableWidget.setRowCount(4) self.current_rows = 0 def action_push_button(self): self.api.socket.subscribe_callback(self.data_callback) for dd in self.data: data = self.api.get_data(dd) val1 = data["val1"] val2 = data["val2"] self.add_row(val1, val2, "0.0", "?") def add_row(self, val1, val2, val3, val4): self.tableWidget.setItem(self.current_rows, 0, QTableWidgetItem(str(val1))) self.tableWidget.setItem(self.current_rows, 1, QTableWidgetItem(str(val2))) self.tableWidget.setItem(self.current_rows, 2, QTableWidgetItem(str(val3))) self.tableWidget.setItem(self.current_rows, 3, QTableWidgetItem(str(val4))) self.current_rows += 1 def update_row(self, index, val1, val2): self.tableWidget.item(index, 2).setText(str(val1)) self.tableWidget.item(index, 3).setText(str(val2)) def data_callback(self, data): for val in data: ind = self.get_index(val["ind"]) val1 = val["val1"] val2 = "XX" self.update_row(ind, val1, val2)
-
@anoop1
Hi and welcome.This callback function get data from a websocket.
Your method has
self.tableWidget
. That looks likeself
is some UI widget, nothing to do with websocket.So I am looking for a way to notify table Widget that data is modified.
The above method changes
self.tableWidget
, so it does not need to notify table widget itself of anything.OK, I think I understand what you mean. The code you show is what you have in the UI which will update the table widget as you desire. You are asking how to get to call that when data has changed from the websocket, right?
So in your websocket code in another thread you need to define your own signal and
emit
that signal when it has gathered new data/wants the UI to update. In your UI code in its own thread, you will place a slot on that signal, and that can call your method above. This signal/slot approach is how we have non-UI threads inform the UI thread of something, because non-UI threads must not themselves directly call UI code. -
@JonB Hello JonB thank you for your response I have updated my code for the UI that I am using.
update_row
is the the function which I use to update data of specific cell when I run the functionupdate_row
without callback (directly from main thread) there is no problem I can see the updated data but when I am usingupdate_row
in callback I can't see the updated data but the data is updated I have to click on some cell to make some event and I get updated data at the cell.data_callback
is the callback function called from theapi
and I am getting data indata_callback
without any problem all I want to update the table widget data so I can see the update in the table (without doing some event like clicking on the cell). I know this is weird that table widget shows the updated data when I click on the cell maybe it causes some refresh notification or something. Can you link some examples to signal/slot method. -
@anoop1
I don't see you are saying anything different from above.How to notify table Widget from different thread that data is updated.
I said what you must do in my reply: create your own signal from the thread which knows there is new data and
emit
it, UI thread can place a slot on that.If you are not familiar with signals & slots --- which is at the heart of Qt -- read https://doc.qt.io/qt-5/signalsandslots.html.
-
You should not call any functions on
self.tableWidget
from any other thread but the GUI thread if these will update the view. I had my applications crash a couple of times when I accidentally got it wrong. Only the GUI thread is allowed to paint the widgets and many functions therefore need to be called in its context.One way is to connect a signal from you extra thread to a slot of an object living inside the GUI thread to hand over the data and let that slot update the widget. Here is an example (sorry, I only know C++-syntax, so I am going to mix a little):
connect(this, &MyClass::mySignal, qApp, [&](self, index, val1, val2) { self.update_row(index, val1, val2); }); ... emit self.mySignal(index, val1, vals2);
The other way is to explicitly put calls to these functions into the GUI thread:
QMetaObject::invokeMethod(qApp, [&]() { self.update_row(index, val1, val2); });
I am not sure how well these translate into Python (especially with lambdas). However, the first version can also be written without the lambda function.