Package: qt-thread-updater
Python Qt thread updater to update GUI items using a separate thread.This library allows you to efficiently update Qt GUI elements from a separate thread. Qt GUI elements are not thread
safe. Method calls likeLabel.setTextdo not work in a separate thread. This library solves that problem.UtilitiesThe ThreadUpdater offers several utilities to help with updating a widget’s value.call_latest - Call the given function with the most recent value in the main thread using the timer.It is safe to call this many times with the same function.If the given function is called multiple times it is only called once with the most recent value.call_in_main - Call the given function in the main thread using the timer.Every time you call this function the given function will be called in the main threadIf the given function is called multiple times it will be called every time in the main thread.If this function is called too many times it could slow down the main event loop.register_continuous - Register a function to be called every time theThreadUpdater.updatemethod is called.Thetimeoutvariable (in seconds) indicates how often the registered functions will be called.delay - Call a function after the given number of seconds has passed.This will not be accurate. Accuracy can be improved by lowering the timeout to increase how often the timer runs.ThreadUpdater ExamplesBelow are some examples of how the ThreadUpdater would normally be used.Simple Thread ExampleThe example below tells the update to run lbl.setText in the main thread with the latest value.importtimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updaterimportget_updaterapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])lbl=QtWidgets.QLabel("Latest Count: 0")lbl.resize(300,300)lbl.show()data={'counter':0}defrun(is_alive):is_alive.set()whileis_alive.is_set():text='Latest Count:{}'.format(data['counter'])get_updater().call_latest(lbl.setText,text)data['counter']+=1time.sleep(0.001)# Not needed (still good to have some delay to release the thread)alive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()app.exec_()alive.clear()Continuous Update ExampleThe example below continuously runs the update function every timeThreadUpdater.update()is called from the timer.
This may be inefficient if there is no new data to update the label with.importtimeimportthreadingfromqtpyimportQtWidgetsimportqt_thread_updater# from qt_thread_updater import get_updaterapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])lbl=QtWidgets.QLabel("Continuous Count: 0")lbl.resize(300,300)lbl.show()data={'counter':0}qt_thread_updater.set_updater(qt_thread_updater.ThreadUpdater(1/60))@qt_thread_updater.register_continuousdefupdate():"""Update the label with the current value."""lbl.setText('Continuous Count:{}'.format(data['counter']))# get_updater().register_continuous(update)defrun(is_alive):is_alive.set()whileis_alive.is_set():data['counter']+=1# time.sleep(0.001)  # Not needed (still good to have some delay to release the thread)alive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()qt_thread_updater.delay(5,app.quit)# Quit after 5 secondsapp.exec_()alive.clear()cleanup_app()Call In Main ExampleThe example below calls the append function every time. It may not be efficient.importtimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updaterimportget_updaterapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])text_edit=QtWidgets.QTextEdit()text_edit.resize(300,300)text_edit.setReadOnly(True)text_edit.show()data={'counter':0}defrun(is_alive):is_alive.set()whileis_alive.is_set():text='Main Count:{}'.format(data['counter'])get_updater().call_in_main(text_edit.append,text)data['counter']+=1time.sleep(0.01)# Some delay/waiting is requiredalive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()app.exec_()alive.clear()Delay ExampleThe example below calls the append function after X number of seconds has passed. The delay function will not be
accurate, but guarantees that the function is called after X number of seconds. To increase accuracy give theThreadUpdatera smaller timeout for it to run at a faster rate.importtimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updaterimportget_updaterapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])text_edit=QtWidgets.QTextEdit()text_edit.resize(300,300)text_edit.setReadOnly(True)text_edit.show()now=time.time()defupdate_text(set_time):text_edit.append('Requested{:.04f}Updated{:.04f}'.format(set_time,time.time()-now))# Lower the timeout so it runs at a faster rate.get_updater().timeout=0# 0.0001  # Qt runs in millisecondsget_updater().delay(0.5,update_text,0.5)get_updater().delay(1,update_text,1)get_updater().delay(1.5,update_text,1.5)get_updater().delay(2,update_text,2)get_updater().delay(2.5,update_text,2.5)get_updater().delay(3,update_text,3)app.exec_()WidgetsI’ve decdied to include a couple of useful Qt Widgets with this library.QuickPlainTextEdit - Used to display fast streams of dataQuickTextEdit - Display fast streams of data with color.QuickPlainTextEditQuickly display data from a separate thread.importtimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updater.widgets.quick_text_editimportQuickPlainTextEditapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])text_edit=QuickPlainTextEdit()text_edit.resize(300,300)text_edit.show()data={'counter':0}defrun(is_alive):is_alive.set()whileis_alive.is_set():text='Main Count:{}\n'.format(data['counter'])text_edit.write(text)data['counter']+=1time.sleep(0.0001)# Some delay is usually required to let the Qt event loop run (not needed if IO used)alive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()app.exec_()alive.clear()QuickTextEditQuickly display data from a separate thread using color.importtimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updater.widgets.quick_text_editimportQuickTextEditapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])text_edit=QuickTextEdit()text_edit.resize(300,300)text_edit.show()data={'counter':0}defrun(is_alive):is_alive.set()whileis_alive.is_set():text='Main Count:{}\n'.format(data['counter'])text_edit.write(text,'blue')data['counter']+=1time.sleep(0.0001)# Some delay is usually required to let the Qt event loop run (not needed if IO used)alive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()app.exec_()alive.clear()QuickTextEdit RedirectDisplay print (stdout and stderr) in a QTextEdit with color.importsysimporttimeimportthreadingfromqtpyimportQtWidgetsfromqt_thread_updater.widgets.quick_text_editimportQuickTextEditapp=QtWidgets.QApplication.instance()orQtWidgets.QApplication([])text_edit=QuickTextEdit()text_edit.resize(300,300)text_edit.show()sys.stdout=text_edit.redirect(sys.__stdout__,color='blue')sys.stderr=text_edit.redirect(sys.__stderr__,color='red')data={'counter':0}defrun(is_alive):is_alive.set()whileis_alive.is_set():stdout_text='Main Count:{}'.format(data['counter'])# Print gives \n automaticallyerror_text='Error Count:{}'.format(data['counter'])# Print gives \n automatically# Print automatically give '\n' with the "end" keyword argument.print(stdout_text)# Print will write to sys.stdout where the rediect will write to text_edit and stdoutprint(error_text,file=sys.stderr)# Print to sys.stderr. Rediect will write to text_edit and stderrdata['counter']+=1# Some delay is usually desired. print/sys.__stdout__ uses IO which gives time for Qt's event loop.# time.sleep(0.0001)alive=threading.Event()th=threading.Thread(target=run,args=(alive,))th.start()app.exec_()alive.clear()