pyqt clicked.connect signaling more than one event - pyqt

I have a button in a pyQt gui that signals an external class method using functools.partial:
self.Valve_ON.clicked.connect(functools.partial(self.ValveControl.IO_on,6008))
I would also like to be able to display a message in the statusBar
self.statusBar().showMessage("Valve on")
How can I signal more than one event on a clicked.
Thanks

Create a slot for the signal and run your code from there, something like this:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#---------
# IMPORT
#---------
import sys
from PyQt4 import QtGui, QtCore
#---------
# DEFINE
#---------
class MyWindow(QtGui.QMainWindow):
_numberClicked = 0
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.centralwidget = QtGui.QWidget(self)
self.pushButtonClick = QtGui.QPushButton(self.centralwidget)
self.pushButtonClick.setText("Click Me!")
self.pushButtonClick.clicked.connect(self.on_pushButtonClick_clicked)
self.labelClicked = QtGui.QLabel(self)
self.layoutVertical = QtGui.QVBoxLayout(self.centralwidget)
self.layoutVertical.addWidget(self.pushButtonClick)
self.layoutVertical.addWidget(self.labelClicked)
self.statusbar = QtGui.QStatusBar(self)
self.statusbar.setObjectName("statusbar")
self.setCentralWidget(self.centralwidget)
self.setStatusBar(self.statusbar)
#QtCore.pyqtSlot()
def on_pushButtonClick_clicked(self):
self._numberClicked += 1
message = "Clicked {0} time(s)".format(self._numberClicked)
self.labelClicked.setText(message)
self.statusbar.showMessage(message, 1111)
#---------
# MAIN
#---------
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.resize(333, 111)
main.show()
sys.exit(app.exec_())

You can connect as many slots to a signal as you want:
self.Valve_ON.clicked.connect(functools.partial(self.ValveControl.IO_on,6008))
self.Valve_ON.clicked.connect(functools.partial(self.statusBar().showMessage,"Valve on"))
Both should fire when you click the button.
A signal can be connected to an arbitrary number of slots and a slot can have an arbitrary number of signals connected to it. The library will sort out all of the dispatching for you.

Related

How to use a pyqtsignal to transmit a window which retains its function?

I use a pyqt_signal to transmit a sub window, which has a button whose function is to print. I use a thread to transmit this sub window to the main window to show, however the button loses its function. I know that I should put the statement self.sub_window = SubWindow() into the __init__ function in the second class, but how can I achieve the same effect if I still put this statement here.
# -*- coding: utf-8 -*-
from threading import currentThread
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QObject, QThread
class SubWindow(QWidget):
def __init__(self):
super(SubWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
def print_(self):
print('***')
class SignalStore(QThread):
window_signal = pyqtSignal(object)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
# if i put this statement here, how can i acquire window's print button function
self.sub_window = SubWindow()
self.window_signal.emit(self.sub_window)
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.window_signal.connect(self.get_sub_window)
def send_signal(self):
self.med_signal.start()
def get_sub_window(self, para):
self.sub_window = para
self.sub_window.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
Don't create or access gui objects inside threads. Read Qt guide.
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.
This is probably what you are looking for:
import time
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
from PyQt5.QtCore import pyqtSignal, QThread, pyqtSlot
class SubWindow(QWidget):
def __init__(self, parent=None):
super(SubWindow, self).__init__(parent)
self.resize(400, 400)
self.button = QPushButton(self)
self.button.setText('push me to print ***')
self.button.move(200, 200)
self.button.clicked.connect(self.print_)
#pyqtSlot()
def print_(self):
print('hello from subwindow')
class SignalStore(QThread):
print_func = pyqtSignal(str)
def __init__(self):
super(SignalStore, self).__init__()
def run(self):
time.sleep(1) # fake working...
self.print_func.emit("hello from thread")
class MainWindow(QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.resize(400, 400)
self.subwin = SubWindow()
self.button = QPushButton(self)
self.button.setText('push me to get subwindow')
self.button.move(200, 200)
self.button.clicked.connect(self.send_signal)
self.med_signal = SignalStore()
self.med_signal.print_func.connect(self.print_from_main)
def send_signal(self):
self.subwin.show()
self.med_signal.start()
#pyqtSlot(str)
def print_from_main(self, string: str):
print(string)
self.subwin.print_()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())

Why does the label show only the last set image? (PyQt5) [duplicate]

I have got this problem. I´m trying to set text on a lineEdit object on pyqt4, then wait for a few seconds and changing the text of the same lineEdit. For this I´m using the time.sleep() function given on the python Time module. But my problem is that instead of setting the text, then waiting and finally rewrite the text on the lineEdit, it just waits the time it´s supposed to sleep and only shows the final text. My code is as follows:
from PyQt4 import QtGui
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
time.sleep(2)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.
You should probably use a QTimer and use it's timeout signal to schedule a signal for deferred delivery, or it's singleShot method.
For example (adapted your code to make it run without dependencies):
from PyQt4 import QtGui, QtCore
class Ventana(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.lineEdit = QtGui.QLineEdit(self)
self.button = QtGui.QPushButton('clickme', self)
self.layout().addWidget(self.lineEdit)
self.layout().addWidget(self.button)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
self.lineEdit.setText('Start')
QtCore.QTimer.singleShot(2000, lambda: self.lineEdit.setText('End'))
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())
Also, take a look at the QThread sleep() function, it puts the current thread to sleep and allows other threads to run. https://doc.qt.io/qt-5/qthread.html#sleep
You can't use time.sleep here because that freezes the GUI thread, so the GUI will be completely frozen during this time.You can use QtTest module rather than time.sleep().
from PyQt4 import QtTest
QtTest.QTest.qWait(msecs)
So your code should look like:
from PyQt4 import QtGui,QtTest
from gui import *
class Ventana(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.button.clicked.connect(self.testSleep)
def testSleep(self):
import time
self.lineEdit.setText('Start')
QtTest.QTest.qWait(2000)
self.lineEdit.setText('Stop')
def mainLoop(self, app ):
sys.exit( app.exec_())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Ventana()
window.show()
sys.exit(app.exec_())

Creating new window using keyboard keys [duplicate]

I am programming a simple GUI, that will open a opencv window at a specific point. This window has some very basic keyEvents to control it. I want to advance this with a few functions. Since my QtGui is my Controller, I thought doing it with the KeyPressedEvent is a good way. My Problem is, that I cannot fire the KeyEvent, if I am active on the opencv window.
So How do I fire the KeyEvent, if my Gui is out of Focus?
Do I really need to use GrabKeyboard?
The following code reproduces my Problem:
import sys
from PyQt5.QtWidgets import (QApplication, QWidget)
from PyQt5.Qt import Qt
import cv2
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.first = True
def openselect(self):
im = cv2.imread(str('.\\images\\Steine\\0a5c8e512e.jpg'))
self.r = cv2.selectROI("Image", im)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space and self.first:
self.openselect()
self.first = False
print('Key Pressed!')
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
The keyPressEvent method is only invoked if the widget has the focus so if the focus has another application then it will not be notified, so if you want to detect keyboard events then you must handle the OS libraries, but in python they already exist libraries that report those changes as pyinput(python -m pip install pyinput):
import sys
from PyQt5 import QtCore, QtWidgets
from pynput.keyboard import Key, Listener, KeyCode
class KeyMonitor(QtCore.QObject):
keyPressed = QtCore.pyqtSignal(KeyCode)
def __init__(self, parent=None):
super().__init__(parent)
self.listener = Listener(on_release=self.on_release)
def on_release(self, key):
self.keyPressed.emit(key)
def stop_monitoring(self):
self.listener.stop()
def start_monitoring(self):
self.listener.start()
class MainWindow(QtWidgets.QWidget):
pass
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
monitor = KeyMonitor()
monitor.keyPressed.connect(print)
monitor.start_monitoring()
window = MainWindow()
window.show()
sys.exit(app.exec_())

My gui doesn't appear when I use while loop as a startup function in pyqt and python

I want to use startup function which should have while loop.
but I run the code my gui doesn't appear until while loop ends.
I tried with self.show() it can make show gui but it doesn't allow to use sys.exit()
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
import time
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
self.myfunc()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
app.exec_()
If you need to perform an action again, you have several options.
For example, if each iteration takes very little time, without the possibility of blocking the main loop, you can replace the cycle with a timer (QTimer) and call the method each time, which is responsible for obtaining new data and updating the necessary interface elements in accordance with them:
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread, QTimer
import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
self.k = 0
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
self.button = QtWidgets.QPushButton('Start', self)
self.button.clicked.connect(self.read_data)
self.label_data = QtWidgets.QLabel(self, alignment=QtCore.Qt.AlignCenter)
self.label_data.setText('Pending')
layout = QtWidgets.QGridLayout(centralWidget)
layout.addWidget(self.label_data)
layout.addWidget(self.button)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.read_data_from_sensor)
#QtCore.pyqtSlot()
def read_data(self):
''' Start / Stop reading at the touch of a button '''
if not self.timer.isActive():
self.timer.start()
self.button.setText("Stop")
else:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
#QtCore.pyqtSlot()
def read_data_from_sensor(self):
dt = time.strftime("%Y-%m-%d %H:%M:%S")
self.label_data.setText(dt)
self.label_data.adjustSize()
self.k += 1
self.statusBar().showMessage('{} item(s)'.format(self.k))
if self.k > 10:
self.timer.stop()
self.button.setText("Start")
self.label_data.setText("Pending")
self.k = 0
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
myWindow.show()
app.exec_()
What you wrote may also work, but this is not very good. You can compare.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
from PyQt5.QtCore import QThread
#import time
#form_class, QMainWindow = uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow): #, form_class):
def __init__(self):
super().__init__()
# self.setupUi(self)
self.show()
self.myfunc()
def myfunc(self):
k = 0
stat = True
while stat:
k += 1
# time.sleep(1)
# self.statusMessage.append(str(k))
self.statusBar().showMessage('{} item(s)'.format(k))
QThread.msleep(1000)
QApplication.processEvents()
if k>10:
stat=False
#sys.exit()
if __name__=='__main__':
app = QApplication(sys.argv)
myWindow = MyWindow()
# myWindow.show()
app.exec_()
In your loop you are sleeping for 10 second, since you are creating a while loop on the main thread, the GUI wont show until the loop is done because it would be blocking the main thread. You can test this by removing time.sleep(1).
Without changing your code much, try this:
import sys,threading, time
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
form_class,QMainWindow=uic.loadUiType('youhua.ui')
class MyWindow(QMainWindow,form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
#self.show()
t = threading.Thread(target=self.myfunc)
t.start()
def myfunc(self):
k=1
stat=True
while stat:
k=k+1
time.sleep(1)
self.statusMessage.append(str(k))
QApplication.processEvents()
if k>10:
stat=False
#sys.exit() - if you are trying to close the window here use self.close()
if __name__=='__main__':
app=QApplication(sys.argv)
myWindow=MyWindow()
myWindow.show()
sys.exit(app.exec_())

Why there is a pythonw window generated when mine QProgressDialog window was auto closed?

I am new to both pyqt and python so I'm sure that my question would seem stupid, so thank you for reading and answer it. It is really helpful.
Here is my source code
# -*- coding: utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
QTextCodec.setCodecForTr(QTextCodec.codecForName("utf8"))
class Prog(QDialog):
def __init__(self,parent=None):
super(Prog,self).__init__(parent)
self.start_P()
def start_P(self):
progressDialog=QProgressDialog(self)
progressDialog.setWindowModality(Qt.WindowModal)
progressDialog.setMinimumDuration(5)
progressDialog.setWindowTitle(self.tr("请等待"))
progressDialog.setLabelText(self.tr("拷贝..."))
progressDialog.setCancelButtonText(self.tr("取消"))
progressDialog.setRange(0,100)
progressDialog.setAutoClose(True)
for i in range(101):
progressDialog.setValue(i)
QThread.msleep(10)
if progressDialog.wasCanceled():
return
self.connect(progressDialog,SIGNAL("closed()"))
def main():
app = QApplication(sys.argv)
pp = Prog()
pp.show()
app.exec_()
if __name__ == '__main__':
main()
There are some Chinese characters, but it's irrelevant. The strange part is when I execute this program I would get a window of progress dialog, that is what I want. But when it is auto closed a pythonw window generated automatically.
I was curious about why this pythonw window was generated and want to know how to avoid it.
That's because you are creating your QProgressDialog as child of a QDialog, checkout the line that says progressDialog=QProgressDialog(self). Checkout how this example works:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#---------
# IMPORT
#---------
from PyQt4 import QtGui, QtCore
#---------
# MAIN
#---------
class MyThread(QtCore.QThread):
progress = QtCore.pyqtSignal(int)
_stopped = False
def __init__(self, parent=None):
super(MyThread, self).__init__(parent)
def stop(self):
self._stopped = True
def start(self):
self._stopped = False
super(MyThread, self).start()
def run(self):
for progressNumber in range(101):
self.progress.emit(progressNumber)
self.msleep(22)
if self._stopped:
return
class MyWindow(QtGui.QProgressDialog):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.threadProgress = MyThread(self)
self.threadProgress.progress.connect(self.setValue)
def stop(self):
self.threadProgress.stop()
def start(self):
self.threadProgress.start()
def hideEvent(self, event):
self.close()
if __name__ == "__main__":
import sys
codec = QtCore.QTextCodec.codecForName("utf8")
QtCore.QTextCodec.setCodecForTr(codec)
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.setWindowTitle(main.tr("请等待"))
main.setLabelText(main.tr("拷贝..."))
main.setCancelButtonText(main.tr("取消"))
main.setRange(0,100)
main.canceled.connect(main.stop)
main.show()
main.start()
sys.exit(app.exec_())

Resources