Reputation: 119
I was going through QThread() document where it is mentioned to create a new class worker
and then create QThread
in main function and afterwards use movingToThread
My question is I have a QMainWindow
, inside that function called update_table_live
which is basically updating my table widget cell value in while loop. I want this function to be a part of QThread
?
If I go with example of realpython then it is making me confused as my QMainWindow
contains all object as self
So how should I make them separate in worker
class
just like shown in realpython website or is there any other way to create QThread
for function inside mainWindow?
Trying to make a clean version of code from original.
import utility
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QThread, QObject, pyqtSignal
import sys
class mainUI(QWidget):
def __init__(self, parent=None):
""" This module setup entire widgets for UI
with setup of some basic functionality.
Args:
parent: None
"""
QWidget.__init__(self, parent, Qt.WindowStaysOnTopHint)
self.setMinimumSize(1000, 500)
self.table_live = QTableWidget()
self.table_live.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table_live.setColumnCount(6)
self.table_live.setHorizontalHeaderLabels(
['Name', 'Quantity', 'Discount Price', 'Market Price', 'Change', 'P/L']
)
self.layout = QHBoxLayout()
self.layout.addWidget(self.table_live)
self.setLayout(self.layout)
#: This Function to be in thread
self.update_table_live()
def update_table_live(self):
""" This method will update live table
on current market value.
Returns:
None
"""
while True:
market = utility.get_live_data() #: This function get live data.
self.table_live.setRowCount(0)
for name, product_data in market.items():
row_count = self.table_live.rowCount()
self.table_live.insertRow(row_count)
item_name = QTableWidgetItem(name)
item_quantity = QTableWidgetItem(product_data['quantity'])
item_discount = QTableWidgetItem(product_data['discount'])
item_current = QTableWidgetItem(product_data['price'])
item_change = QTableWidgetItem(product_data['change'])
item_pl = QTableWidgetItem(product_data['p_l'])
self.table_live.setItem(row_count, 0, item_name)
self.table_live.setItem(row_count, 1, item_quantity)
self.table_live.setItem(row_count, 2, item_discount)
self.table_live.setItem(row_count, 3, item_current)
self.table_live.setItem(row_count, 4, item_change)
self.table_live.setItem(row_count, 5, item_pl)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = mainUI()
win.show()
app.exec_()
Upvotes: 1
Views: 1181
Reputation: 243945
The main problem in your code is the while True since this in itself blocks the eventloop so you have 2 options:
NOTE: If the get_live_data()
task is time consuming then you should use threads.
You have to be clear about the following:
Considering the above, the solution is:
class Worker(QObject):
dataChanged = pyqtSignal(dict)
def task(self):
while True:
market = utility.get_live_data()
self.dataChanged.emit(market)
class mainUI(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent, Qt.WindowStaysOnTopHint)
self.setMinimumSize(1000, 500)
self.table_live = QTableWidget()
self.table_live.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table_live.setColumnCount(6)
self.table_live.setHorizontalHeaderLabels(
["Name", "Quantity", "Discount Price", "Market Price", "Change", "P/L"]
)
layout = QHBoxLayout(self)
layout.addWidget(self.table_live)
self.worker = Worker()
thread = QThread(self)
self.worker.moveToThread(thread)
self.worker.dataChanged.connect(self.update_table_live)
thread.started.connect(self.worker.task)
thread.start()
def update_table_live(self, market):
for name, product_data in market.items():
row_count = self.table_live.rowCount()
self.table_live.insertRow(row_count)
item_name = QTableWidgetItem(name)
item_quantity = QTableWidgetItem(product_data["quantity"])
item_discount = QTableWidgetItem(product_data["discount"])
item_current = QTableWidgetItem(product_data["price"])
item_change = QTableWidgetItem(product_data["change"])
item_pl = QTableWidgetItem(product_data["p_l"])
self.table_live.setItem(row_count, 0, item_name)
self.table_live.setItem(row_count, 1, item_quantity)
self.table_live.setItem(row_count, 2, item_discount)
self.table_live.setItem(row_count, 3, item_current)
self.table_live.setItem(row_count, 4, item_change)
self.table_live.setItem(row_count, 5, item_pl)
NOTE: If get_live_data
is not very time consuming then it is better to use QTimer to avoid blocking loop.
class mainUI(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent, Qt.WindowStaysOnTopHint)
self.setMinimumSize(1000, 500)
self.table_live = QTableWidget()
self.table_live.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.table_live.setColumnCount(6)
self.table_live.setHorizontalHeaderLabels(
["Name", "Quantity", "Discount Price", "Market Price", "Change", "P/L"]
)
layout = QHBoxLayout(self)
layout.addWidget(self.table_live)
self.timer = QTimer(interval=100, timeout=self.handle_timeout)
self.timer.start()
def handle_timeout(self):
market = utility.get_live_data()
self.update_table_live(market)
def update_table_live(self, market):
for name, product_data in market.items():
row_count = self.table_live.rowCount()
self.table_live.insertRow(row_count)
item_name = QTableWidgetItem(name)
item_quantity = QTableWidgetItem(product_data["quantity"])
item_discount = QTableWidgetItem(product_data["discount"])
item_current = QTableWidgetItem(product_data["price"])
item_change = QTableWidgetItem(product_data["change"])
item_pl = QTableWidgetItem(product_data["p_l"])
self.table_live.setItem(row_count, 0, item_name)
self.table_live.setItem(row_count, 1, item_quantity)
self.table_live.setItem(row_count, 2, item_discount)
self.table_live.setItem(row_count, 3, item_current)
self.table_live.setItem(row_count, 4, item_change)
self.table_live.setItem(row_count, 5, item_pl)
Upvotes: 3