How to connect a signal between classes in PyQt5 - python

I have very common question but it is not easy for me.
I'm trying to connect a PyQt signal from the MainWindow class to the CheckExcel class, but It doesn't work. I think some of people know how to do this, but some of them do not. Most people who doesn't know pointed out the code... but please don't do it. My point is how could I connect this?
Here is some code below:
class CheckExcel(QtCore.QThread):
updated = QtCore.pyqtSignal(int)
updateLab = QtCore.pyqtSignal(str)
running = False
def __init__(self, parent=None):
super(CheckExcel, self).__init__(parent)
self.progPercent = 0
self.running = True
def run(self):
pythoncom.CoInitialize()
try:
while self.running == True:
self.updated.emit(int(self.progPercent))
self.updateLab.emit(str(va_TC))
print(self.progPercent)
except :
print('Excel is not executed')
CheckExcel().run()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
def startBtnClicked(self):
self.chk_excel = CheckExcel(self)
self.chk_excel.start()
self.chk_excel.updated.connect(self.updateValue)
self.chk_excel.updateLab.connect(self.updateLabel)
def updateValue(self, data):
self.progressBar.setValue(data)
def updateLabel(self, text):
self.label.setText(text)
def stop(self):
self.event.set()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Related

How can I hide the QMovie gif?

I'm trying to make the loading ui. I want to show the loading ui when the operation starts in the thread, and when the thread operations is completed, I want to hide the loading ui.
class TableThread(QThread):
set_signal = pyqtSignal(int)
stop_signal = pyqtSignal()
def __init__(self, parent):
super().__init__(parent)
def run(self):
for i in range(10):
time.sleep(0.5)
self.set_signal.emit(i)
self.stop_signal.emit()
class Test(QDialog, form_settings_ui):
def __init__(self):
super().__init__()
self.setupUi(self)
self.tableWidget.setRowCount(0)
self.movie = QMovie('loading.gif', QByteArray(), self)
self.movie.setCacheMode(QMovie.CacheAll)
self.label.setMovie(self.movie)
self.pushButton.clicked.connect(self.clicked_push_button)
def set_table_list(self, i):
self.tableWidget.insertRow(i)
self.tableWidget.setItem(i, 0, QTableWidgetItem(str(i)))
self.tableWidget.setItem(i, 1, QTableWidgetItem(str(i)))
self.tableWidget.resizeColumnsToContents()
def clicked_push_button(self):
self.table_thread = TableThread(self)
self.table_thread.set_signal.connect(self.set_table_list)
self.table_thread.stop_signal.connect(self.stop_loading)
self.table_thread.start()
self.movie.start()
def stop_loading(self):
self.movie.stop()
if __name__ == "__main__":
app = QApplication(sys.argv)
perforce_ui_window = Test()
perforce_ui_window.show()
app.exec_()
This is the test code.
I put in the self.movie.stop () code, but it just only stopped moving, it didn't disappear.

PyQT: How to call a function on main thread and get the result (not store the result beforehand)? [duplicate]

I have the following code but it's complaining that I cannot access the UI data from my thread. In my example code below, What is the best way I can access the userInputString value so my threading can run?
self.nameField is a PyQt QLineEdit.
QObject::setParent: Cannot set parent, new parent is in a different thread
QPixmap: It is not safe to use pixmaps outside the GUI thread
QWidget::repaint: Recursive repaint detected
import myUI
class MainUIClass(QtGui.QMainWindow, myUI.Ui_MainWindow):
def __init__(self, parent=None):
super(MainUIClass, self).__init__(parent)
self.setupUi(self)
self.startbutton.clicked.connect(self.do_work)
self.workerThread = WorkerThread()
self.connect(self.workerThread, SIGNAL("myThreading()"), self.myThreading, Qt.DirectConnection)
def do_work(self):
self.userInputString = self.nameField.Text()
self.workerThread.start()
def myThreading(self):
if userInputString is not None:
#Do something
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
self.emit(SIGNAL("myThreading()"))
if __name__ == '__main__':
a = QtGui.QApplication(sys.argv)
app = MainUIClass()
app.show()
a.exec_()
Not sure if it's what you need but here is a working QThread exemple using Qt5
import time
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.worker_thread = WorkerThread()
self.worker_thread.job_done.connect(self.on_job_done)
self.create_ui()
def create_ui(self):
self.button = QtWidgets.QPushButton('Test', self)
self.button.clicked.connect(self.start_thread)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
def start_thread(self):
self.worker_thread.gui_text = self.button.text()
self.worker_thread.start()
def on_job_done(self, generated_str):
print("Generated string : ", generated_str)
self.button.setText(generated_str)
class WorkerThread(QtCore.QThread):
job_done = QtCore.pyqtSignal('QString')
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self.gui_text = None
def do_work(self):
for i in range(0, 1000):
print(self.gui_text)
self.job_done.emit(self.gui_text + str(i))
time.sleep(0.5)
def run(self):
self.do_work()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
test = MainWindow()
test.show()
app.exec_()

Python PyQt5 Add List Item from another Py-File

when clicking a button in the PyQt5 Ui iam starting a mitmproxy. When the proxy is started, i try to change a listWidget with Data from the Proxy.
main.py
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainWindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.pushButton.clicked.connect(self.Start)
def Start(self):
x = threading.Thread(target=self.StartMITM)
x.start()
def StartMITM(self):
os.system("mitmweb -s mitmproxy.py -q --no-web-open-browser")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
mitmproxy.py - this is the error part
from mitmproxy import http
from main import MainWindow
def response(flow):
MainWindow.listWidget.addItem(flow.request.pretty_url)
Can I connect to the Widgets from another File?
It has 2 independent processes: The GUI and the mitmproxy script. And communicating both processes does not imply importing modules since you would be creating another widget, also objects should not be accessed through classes (I recommend you check your basic python notes).
In this the solution is to use some Inter process communication (IPC), in this case you can use Qt Remote Objects (QtRO):
main.py
from functools import cached_property
from PyQt5 import QtCore, QtRemoteObjects, QtWidgets
class Bridge(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
#QtCore.pyqtSlot(str)
def add_message(self, message):
self.messageChanged.emit(message)
class MitmwebManager(QtCore.QObject):
logChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
# self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.handle_log)
self.process.setProgram("mitmweb")
#cached_property
def process(self):
return QtCore.QProcess()
def start(self, arguments):
self.process.setArguments(arguments)
self.process.start()
def stop(self):
self.process.kill()
def handle_log(self):
data = self.process.readAllStandardOutput()
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(data)
self.logChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Start", checkable=True)
self.listwidget = QtWidgets.QListWidget()
self.logview = QtWidgets.QPlainTextEdit(readOnly=True)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.button, 0, 0, 1, 2)
lay.addWidget(self.listwidget, 1, 0)
lay.addWidget(self.logview, 1, 1)
self.register_node = QtRemoteObjects.QRemoteObjectRegistryHost(
QtCore.QUrl("local:registry")
)
self.source_node = QtRemoteObjects.QRemoteObjectHost(
QtCore.QUrl("local:replica"), QtCore.QUrl("local:registry")
)
self.source_node.enableRemoting(self.bridge, "bridge")
self.button.toggled.connect(self.handle_toggled)
self.mitmweb.logChanged.connect(self.handle_log_changed)
self.bridge.messageChanged.connect(self.handle_message_changed)
self.resize(640, 480)
#cached_property
def mitmweb(self):
return MitmwebManager()
#cached_property
def bridge(self):
return Bridge()
def handle_toggled(self, checked):
if checked:
self.mitmweb.start(["-s", "script.py", "--no-web-open-browser"])
self.button.setText("Stop")
else:
self.mitmweb.stop()
self.button.setText("Start")
def handle_log_changed(self, message):
self.logview.insertPlainText(message)
def closeEvent(self, event):
super().closeEvent(event)
self.mitmweb.stop()
def handle_message_changed(self, message):
item = QtWidgets.QListWidgetItem(message)
self.listwidget.addItem(item)
def main():
app = QtWidgets.QApplication([])
view = MainWindow()
view.show()
app.exec_()
if __name__ == "__main__":
main()
script.py
from mitmproxy import http
from PyQt5 import QtCore, QtRemoteObjects
class Bridge(QtCore.QObject):
def __init__(self, bridge, parent=None):
super().__init__(parent)
self._message = ""
self._bridge = bridge
self.bridge.initialized.connect(self.handle_initialized)
#property
def bridge(self):
return self._bridge
#property
def message(self):
return self._message
def send_message(self, message):
self._message = message
#QtCore.pyqtSlot()
def handle_initialized(self):
self.bridge.add_message(self._message)
QtCore.QTimer.singleShot(0, QtCore.QCoreApplication.quit)
def send_qt(message):
qt_app = QtCore.QCoreApplication([])
replica_node = QtRemoteObjects.QRemoteObjectNode(QtCore.QUrl("local:registry"))
replica_bridge = replica_node.acquireDynamic("bridge")
bridge = Bridge(replica_bridge)
bridge.send_message(message)
qt_app.exec_()
def response(flow):
send_qt(flow.request.pretty_url)

What's the correct pattern for threading in a python qt5 application?

I'm trying to write a pyqt5 application with a long running, but not CPU intensive process. I'd like to be able to run it without hanging the UI, so I'm trying to use threading, but since it doesn't seem like I can just run a thread and have it stop after its gone through its code so that it can be run again, I've tried setting up the thread to wait for a variable to change before running.
I know this can't be the correct pattern for running long processes in a pyqt app.
import time
import threading
from PyQt5 import QtWidgets, uic
class MyApp(QtWidgets.QMainWindow):
_run_thread = False
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi('myapp.ui', self)
self.ui.start_thread_button.clicked.connect(self._run_thread_function)
self._thread = threading.Thread(target=self._run_thread_callback)
self._thread.daemon = True
self._thread.start()
self.ui.show()
def _run_thread_callback(self):
while True:
if self._run_thread:
print("running thread code...")
time.sleep(10)
print("thread code finished")
self._run_thread = False
def _run_thread_function(self):
print("starting thread...")
self._run_thread = True
def main():
app = QtWidgets.QApplication(sys.argv)
MyApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a simple demo showing how to start and stop a worker thread, and safely comminucate with the gui thread.
import sys
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QThread):
dataSent = QtCore.pyqtSignal(dict)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self._stopped = True
self._mutex = QtCore.QMutex()
def stop(self):
self._mutex.lock()
self._stopped = True
self._mutex.unlock()
def run(self):
self._stopped = False
for count in range(10):
if self._stopped:
break
self.sleep(1)
data = {
'message':'running %d [%d]' % (
count, QtCore.QThread.currentThreadId()),
'time': QtCore.QTime.currentTime(),
'items': [1, 2, 3],
}
self.dataSent.emit(data)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.edit = QtWidgets.QPlainTextEdit()
self.edit.setReadOnly(True)
self.button = QtWidgets.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.edit)
layout.addWidget(self.button)
self._worker = Worker()
self._worker.started.connect(self.handleThreadStarted)
self._worker.finished.connect(self.handleThreadFinished)
self._worker.dataSent.connect(self.handleDataSent)
def handleThreadStarted(self):
self.edit.clear()
self.button.setText('Stop')
self.edit.appendPlainText('started')
def handleThreadFinished(self):
self.button.setText('Start')
self.edit.appendPlainText('stopped')
def handleDataSent(self, data):
self.edit.appendPlainText('message [%d]' %
QtCore.QThread.currentThreadId())
self.edit.appendPlainText(data['message'])
self.edit.appendPlainText(data['time'].toString())
self.edit.appendPlainText(repr(data['items']))
def handleButton(self):
if self._worker.isRunning():
self._worker.stop()
else:
self._worker.start()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 100, 400, 400)
window.show()
sys.exit(app.exec_())

How to perform action when QCalendarWidget popup closes?

I am using a QDateEdit widget with QDateEdit.setCalendarPopup(True). I am trying to connect a slot to the event when the calendar popup closes. See my example below for my attempts so far, found in MyCalendarWidget. None of my attempts so far have worked. What can I do to perform an action every time the calendar widget popup closes, not only when the date is changed?
from PyQt4 import QtGui, QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args):
super(MainWindow,self).__init__(*args)
self._date = QtGui.QDateEdit()
self._date.setCalendarPopup(True)
self._date.setCalendarWidget(MyCalendarWidget())
self.setCentralWidget(self._date)
class App(QtGui.QApplication):
def __init__(self, *args):
super(App,self).__init__(*args)
self.main = MainWindow()
self.connect(self, QtCore.SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
class MyCalendarWidget(QtGui.QCalendarWidget):
def __init__(self, parent=None):
print("mycal initialized")
super(MyCalendarWidget, self).__init__(parent)
self.installEventFilter(self)
self._many = 2
self._many2 = 2
def focusInEvent(self, event):
print('-'*self._many + 'focus in')
if self._many == 2:
self._many = 4
else:
self._many = 2
super(MyCalendarWidget, self).focusInEvent(event)
def focusOutEvent(self, event):
print('-'*self._many2+'focus out')
if self._many2 == 2:
self._many2 = 4
else:
self._many2 = 2
super(MyCalendarWidget, self).focusOutEvent(event)
def closeEvent(self, event):
print('close')
super(MyCalendarWidget, self).closeEvent(event)
def mouseReleaseEvent(self, event):
print('mouse')
super(MyCalendarWidget, self).mouseReleaseEvent(event)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)
Figured it out - turns out I need to use the clicked signal in QCalendarWidget. This removes the need to sub-class QCalendarWidget as well.
from PyQt4 import QtGui, QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, *args):
super(MainWindow,self).__init__(*args)
self._date = QtGui.QDateEdit()
self._date.setCalendarPopup(True)
calendar = self._date.calendarWidget()
calendar.clicked.connect(self._clicked)
self.setCentralWidget(self._date)
def _clicked(self, date):
print('clicked')
class App(QtGui.QApplication):
def __init__(self, *args):
super(App,self).__init__(*args)
self.main = MainWindow()
self.connect(self, QtCore.SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)

Categories

Resources