My python script need to change one object lcd_p1 every time the function wait_thread_v1 is call every second by a thread t1, but how do this? I don't know how to access this object inside the function? Anyone can help?
vazao1 = 12
global pulses_v1
pulses_v1 = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(vazao1, GPIO.IN)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60,320)
def count_pulses_v1(channel):
global pulses_v1
pulses_v1 += 1
def wait_thread_v1():
global pulses_v1
while True:
time.sleep(1)
print("Pulsos total de vazão 1: "+str(pulses_v1))
#Window.initUI.self.lcd_p1.display(100)
pulses_v1 = 0
GPIO.add_event_detect(vazao1, GPIO.FALLING, callback=count_pulses_v1)
t1 = Thread(target=wait_thread_v1, name='thread_01', args=())
t1.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
You cannot and should not modify the GUI from a secondary thread as indicated by the docs since Qt does not guarantee that it works besides that it is unnecessary to create a new thread. In this case it is better to create a QObject that emits the signal when the callback is invoked since it is thread-safe, and then connect that signal to a slot that increases the pulses, then use a QTimer to implement the logic of the thread.
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer
from PyQt5.QtWidgets import QApplication, QLCDNumber, QWidget
import RPi.GPIO as GPIO
class Window(QWidget):
def __init__(self):
super().__init__()
self.pulses = 0
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
timer = QTimer(self, timeout=self.on_timeout, interval=1000)
timer.start()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60, 320)
#pyqtSlot()
def falling_slot(self):
self.pulses += 1
#pyqtSlot()
def on_timeout(self):
self.lcd_v1.display(self.pulses)
self.pulses = 0
class GPIOManager(QObject):
fallingSignal = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
pin = 12
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=self.falling_callback)
def falling_callback(self, channel):
# This method is called in the thread where GPIOs are monitored.
self.fallingSignal.emit()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
manager = GPIOManager()
manager.fallingSignal.connect(window.falling_slot)
sys.exit(app.exec())
Related
My app is freezing because i need to create a new QThread, but am a little confused how to create it when i call widgets that exists on the main class.
here is my code so far ...
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.uic import loadUi
import time
##Main Class
class HWindow(QtWidgets.QWidget):
def __init__(self):
super(HWindow, self).__init__()
loadUi("Hw.ui",self) ##load ui
#Create a QThread object
self.thread = QtCore.QThread()
#Create a QThread object
self.workerMain = WorkerMain()
#Move worker to the thread
self.workerMain.moveToThread(self.thread)
#Start the thread
self.thread.start()
self.runningMain = False
def activateMain(self):
if self.chkb_main.isChecked():
self.lbl_disena_main.setText("Activated")
self.lbl_disena_main.setStyleSheet('color: green;')
#Change variable and call the function
runningMain = True
self.myFunction()
else:
self.lbl_disena_main.setText("Disabled")
self.lbl_disena_main.setStyleSheet('color: red;')
#Change variable and call the function
runningMain = False
def myFunction(self):
while self.runningMain == True:
if self.gpb_main.isChecked() and self.chkb_main.isChecked():
print("running ...")
time.sleep(3)
##Worker Class
class WorkerMain(QtCore.QObject):
threadRunning = QtCore.pyqtSignal()
def __init__(self):
super(WorkerMain, self).__init__()
def run(self):
print("Thread Running ...")
#i cant call my widgets from my main class from here.
'''
while self.runningMain == True:
if self.gpb_main.isChecked() and self.chkb_main.isChecked():
print("running ...")
time.sleep(3)
'''
def stop(self):
print("Thread Stopped ...")
self.terminate()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
hWindow = QtWidgets.QWidget()
hWindow = HWindow()
hWindow.show()
sys.exit(app.exec_())
Based on pyqt5 documentation and examples, you create a loop on the run method, but it is complicated when i have to create that loop based on what the user select (GUI widgets).
i just figure it out, here is my code ...
class HWindow(QtWidgets.QWidget):
def __init__(self):
super(HWindow, self).__init__()
loadUi("Hw.ui",self) ##load ui
#Create a QThread object
self.thread = QtCore.QThread()
#Create a QThread object
self.workerMain = WorkerMain(self)
#Move worker to the thread
self.workerMain.moveToThread(self.thread)
#Connect the start signal to the run slot
self.workerMain.threadRunning.connect(self.workerMain.run)
#Connect the activateMain signal to the start slot
self.chkb_main.toggled.connect(self.activateMain)
#Start the thread
self.thread.start()
def activateMain(self):
if self.chkb_main.isChecked():
self.lbl_disena_main.setText("Activated")
self.lbl_disena_main.setStyleSheet('color: green;')
self.workerMain.runningMain = True
self.workerMain.threadRunning.emit()
if not self.thread.isRunning():
self.thread.start()
else:
self.lbl_disena_main.setText("Disabled")
self.lbl_disena_main.setStyleSheet('color: red;')
self.workerMain.runningMain = False
self.workerMain.stop()
##Worker Class
class WorkerMain(QtCore.QObject):
threadRunning = QtCore.pyqtSignal()
def __init__(self, parent):
super(WorkerMain, self).__init__()
self.parent = parent
self.runningMain = False
def run(self):
print("Thread Running ...")
while self.runningMain:
if self.parent.gpb_main.isChecked() and self.parent.chkb_main.isChecked():
print("running ...")
time.sleep(3)
def stop(self):
print("Thread Stopped ...")
if self.parent.thread.isRunning():
self.parent.thread.quit()
self.parent.thread.wait()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
hWindow = QtWidgets.QWidget()
hWindow = HWindow()
hWindow.show()
sys.exit(app.exec_())
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_())
I've been writing a program that used a MyWindow(QTableWidget) with a thread. A progress bar is displayed above the sub-window(self.win) displayed as a pop-up.
I want a green bar on the status bar to be displayed consecutively, however after resetting the Spyder-kernel, the green bar does not output continuously. And I want to run the 'stop'/'continue' alternately every time I click the push button. This hasn't been resolved for almost three days.
import sys, time
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QRect
from PyQt5.QtWidgets import *
progVal = 0
class thread(QThread):
signalTh = pyqtSignal(int)
def __init__(self, *args):
super().__init__()
self.flag = True
def run(self):
global progVal
if self.flag:
self.signalTh.emit(progVal)
time.sleep(0.1)
def stop(self):
self.flag = False
self.quit()
self.wait(2)
class MyWindow(QTableWidget):
def __init__(self):
global progVal
super().__init__()
self.setupUi()
self.show()
self.test = thread(None)
self.test.signalTh.connect(self.signal_function)
self.test.run()
self.saveData()
def saveData(self):
global progVal
counts = range(1, 51)
for row in counts:
progVal = int(row/len(counts)*100)
self.test.signalTh.emit(progVal)
time.sleep(0.1)
def click1_function(self):
if self.test.flag:
self.test.stop()
self.pb_start.setText('Start!')
else:
self.test.flag = True
self.test.run()
self.pb_start.setText('Stop!')
#pyqtSlot(int)
def signal_function(self, val):
self.progress.setValue(val)
self.progress.update()
self.win.update()
self.update()
def setupUi(self):
self.resize(500, 400)
self.pb_start = QPushButton(self)
self.pb_start.setGeometry(QRect(80, 20, 100, 50))
self.pb_start.setText("Start")
self.pb_start.clicked.connect(self.click1_function)
self.win = QDialog(self)
self.win.resize(330, 100)
self.progress = QProgressBar(self.win)
self.progress.setGeometry(10, 10, 300, 30)
self.progress.setMaximum(100)
self.win.show()
def closeEvent(self, event):
quit_msg = "Are you sure you want to exit the program?"
reply = QMessageBox.question(self, 'Message', quit_msg, QMessageBox.Yes, QMessageBox.No)
if reply == QMessageBox.Yes:
self.test.stop()
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
myApp = MyWindow()
myApp.show()
app.exec_()
I can pass values between two windows using signal,
and I want the plain text change, while I press a button from another window.
but the insertPlainText and even text.show() are not working
I've tried sending the signal to the init part of the Mainwindow,
tried update, repaint, but none of them works.
appreciate for any of your help, thanks!
the search_send method in class SearchWindow to MainWindow method test_
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.tag_box.hide()
def test_(self, i): # -------problem here------------------
print(i) // <- value here were right
self.ui.tag_box.insertPlainText(i) # -------Plain Text does not change-------
self.ui.tag_box.show()# -------Plain Text does not show either--------------
class SearchWindow(QtWidgets.QMainWindow):
signal=pyqtSignal(str)
def __init__(self, endpoint=None, user=None, password=None, points_link=None):
super(SearchWindow, self).__init__()
self.ui = Ui_Search()
self.ui.setupUi(self)
self.ui.pushButton_2.clicked.connect(self.search_send)
def search_send(self): # -------problem here------------------
tag_list = [tag1,tag2, tag3]
otherClass = MainWindow()
self.signal.connect(otherClass.test_)
for k in tag_list:
self.signal.emit(k)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
sys.exit(app.exec_())
As we're unclear what Ui_Dialog contains, I can't tell what's wrong exactly, but can show you how Signal should be used.
This is example of Signal that's transferring text to Slot. Name may differ in PyQt5 - change accordingly.
from PySide2.QtWidgets import QWidget, QApplication, QPlainTextEdit, QTextEdit, QVBoxLayout
from PySide2.QtCore import Signal, Slot
import sys
class MainWindow(QWidget):
sig = Signal(str)
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Main")
self.layout = QVBoxLayout()
self.text = QTextEdit()
self.layout.addWidget(self.text)
self.setLayout(self.layout)
self.text.textChanged.connect(self.onChange)
def onChange(self):
self.sig.emit(self.text.toPlainText())
class SubWindow(QWidget):
def __init__(self, connect_target: MainWindow):
super(SubWindow, self).__init__()
self.setWindowTitle("Sub")
self.layout = QVBoxLayout()
self.text = QPlainTextEdit()
self.layout.addWidget(self.text)
self.setLayout(self.layout)
connect_target.sig.connect(self.onSignal)
#Slot(str)
def onSignal(self, text):
self.text.insertPlainText(text + '\r\n')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
window_sub = SubWindow(window)
window_sub.show()
sys.exit(app.exec_())
Anything inside Main will be inserted to QPlainTextEdit as you wanted.
I have a simple PyQt script. When I click a button, it starts a QTimer object and increments a progress bar. What I want is to change the label of my text when my progress bar reaches 100%. It worked for me once, but I can't get it to work anymore. What am I doing wrong?
Here's the main part of my code.
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QTimer(self)
#self.timerObject.destroyed.connect(lambda:self.timerButton.setText("Finished") )
self.timerObject.destroyed.connect(lambda:print("Called" ))
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(10, 20, 290, 25)
self.timerButton.move(110,150)
self.progressBar.move(10,100)
self.increment = 0
self.resize(300, 300)
self.show()
#pyqtSlot()
def headsUp(self):
if(self.increment >= 100):
self.timerObject.stop()
else:
self.increment += 1
self.progressBar.setValue(self.increment)
return
def timerStart(self):
if (self.timerObject.isActive()):
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerObject.timeout.connect(self.headsUp)
self.timerButton.setText("Pause")
self.timerObject.start(100)
destroyed is only issued when you delete the object, that a QTimer peer does not imply that it is deleted from memory, therefore it does not emit that signal, a possible solution is to create a signal for the QProgressBar when the value takes the maximum value as shown below:
import sys
from PyQt5 import QtCore, QtWidgets
class ProgressBar(QtWidgets.QProgressBar):
finished = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(ProgressBar, self).__init__(*args, *kwargs)
self.valueChanged.connect(self.on_valueChanged)
#QtCore.pyqtSlot(int)
def on_valueChanged(self, val):
if val == self.maximum():
self.finished.emit()
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QtWidgets.QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QtCore.QTimer(self)
self.progressBar = ProgressBar(self)
self.progressBar.finished.connect(lambda: print("Called" ))
self.increment = 0
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.progressBar)
lay.addWidget(self.timerButton)
self.resize(300, 300)
#QtCore.pyqtSlot()
def headsUp(self):
if self.increment >= 100:
self.timerObject.stop()
else:
self.increment += 1
self.progressBar.setValue(self.increment)
#QtCore.pyqtSlot()
def timerStart(self):
if self.timerObject.isActive():
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerObject.timeout.connect(self.headsUp)
self.timerButton.setText("Pause")
self.timerObject.start(100)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Another best option is to use QTimeLine and its finished signal:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QtWidgets.QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QtCore.QTimeLine(1000, self)
self.timerObject.setFrameRange(0, 100)
self.progressBar = QtWidgets.QProgressBar(self)
self.timerObject.frameChanged.connect(self.progressBar.setValue)
self.timerObject.finished.connect(lambda: print("Called" ))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.progressBar)
lay.addWidget(self.timerButton)
self.resize(300, 300)
#QtCore.pyqtSlot()
def timerStart(self):
if self.timerObject.state() == QtCore.QTimeLine.Running:
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerButton.setText("Pause")
self.timerObject.resume()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
This isn't working because you are connecting to the timers destoryed signal, but the timer is not being destroyed. To use this code as is, call self.timerObject.deleteLater() after you stop the timer.