Just a simple problem (not for me): when I close the window, the program is still running. Here is the code:
from PyQt4 import QtCore, QtGui
from PyQt4.Qt import QString
import sys
import sensors
from sensors import *
import threading
class MainWindow(QtGui.QWidget):
signalUpdate = QtCore.pyqtSignal() # 1 - define a new signal in mainwindow class
# 2 -connect this signal to the update() function
#emit signal
#main window
def __init__(self):
#vars for core temp and name
self.tempValue = 0
self.name = '0'
super(MainWindow, self).__init__()
self.setGeometry(50, 50, 250, 150)
self.setWindowTitle("Title here")
self.setFixedSize(250, 150)
self.home()
#make widgets (progressBar and labe)
def home(self):
self.prgB = QtGui.QProgressBar(self)
self.prgB.setGeometry(20, 20, 210, 20)
#self.prgB.setOrientation(QtCore.Qt.Vertical)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("motif"))#stles -> motif, Plastique, Windows
self.lbl = QtGui.QLabel(self)
self.lbl.setGeometry(60, 40, 210, 20)
self.signalUpdate.connect(self.update) #connect this signal to the update() function
lay = QtGui.QVBoxLayout()
lay.addWidget(self.prgB)
lay.addWidget(self.lbl)
self.setLayout(lay)
self.tmp()
self.show()
#update() to update label and progressbar values
def update(self):
textas = ('%s : %.1f' % (self.name, self.tempValue))
self.lbl.setText(str(textas + ' C'))
self.prgB.setFormat(QString.number(self.tempValue)+ ' C')
self.prgB.setValue(self.tempValue)
#temp() to get chip data from sensors (temp, name etc)
def tmp(self):
sensors.init()
try:
for chip in sensors.iter_detected_chips():
#print (chip)
#print('Adapter:', chip.adapter_name)
for feature in chip:
if feature.label == 'Physical id 0':
self.tempValue = feature.get_value()
self.name = feature.label
#print ('%s (%r): %.1f' % (feature.name, feature.label, feature.get_value()))
threading.Timer(2.0, self.tmp).start()
self.signalUpdate.emit() #emit signal
#print
finally:
sensors.cleanup()
def run():
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
app = QtGui.QApplication(sys.argv)
GUI = MainWindow()
sys.exit(app.exec_())
run()
Why is that happening, and how to fix it? (I was trying to research on google and yes there are many forums with same question but I get no luck so far to fix it).
EDIT: problem is still not fixed, can someone show/tell how to stop threading.Time on program exit? Please :)
Call the timer's cancel() method in your widget's (overridden) closeEvent() method:
def tmp(self):
...
self.timer = threading.Timer(2.0, self.tmp)
self.timer.start()
self.signalUpdate.emit() #emit signal
def closeEvent(self):
self.timer.cancel()
I've tested that this works:
without the threading, app exits;
with the threading.Timer, but without the timer cancel, app never exits;
with the timer cancel, app exits
Related
My main objective is to show a constantly evolving value on a Qt-window textEdit. (this window contains only a checkBox and a textEdit).
Sadly, I cannot click on the checkbox and the window is frozen until I shutdown the terminal.
import sys
from threading import Thread
from random import randint
import time
from PyQt4 import QtGui,uic
class MyThread(Thread):
def __init__(self):
Thread.__init__(self)
#function to continually change the targeted value
def run(self):
for i in range(1, 20):
self.a = randint (1, 10)
secondsToSleep = 1
time.sleep(secondsToSleep)
class MyWindow(QtGui.QMainWindow,Thread):
def __init__(self):
Thread.__init__(self)
super(MyWindow,self).__init__()
uic.loadUi('mywindow.ui',self)
self.checkBox.stateChanged.connect(self.checkeven)
self.show()
#i show the value only if the checkbox is checked
def checkeven(self):
while self.checkBox.isChecked():
self.textEdit.setText(str(myThreadOb1.a))
# Run following code when the program starts
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# Declare objects of MyThread class
myThreadOb1 = MyThread()
myThreadOb2 = MyWindow()
# Start running the threads!
myThreadOb1.start()
myThreadOb2.start()
sys.exit(app.exec_())
At the moment I'm using a thread to set a random value to a, but at the end it is supposed to be a bit more complex as I will have to take the values from an automation.
Do you have any clue why my code is acting like this?
Thank you very much for your help.
The problem is that the while self.checkBox.isChecked() is blocking, preventing the GUI from handling other events.
Also you should not run a PyQt GUI on another thread than the main one.
If you want to send data from one thread to another, a good option is to use the signals.
Doing all these considerations we have the following:
import sys
from threading import Thread
from random import randint
import time
from PyQt4 import QtGui, uic, QtCore
class MyThread(Thread, QtCore.QObject):
aChanged = QtCore.pyqtSignal(int)
def __init__(self):
Thread.__init__(self)
QtCore.QObject.__init__(self)
#function to continually change the targeted value
def run(self):
for i in range(1, 20):
self.aChanged.emit(randint(1, 10))
secondsToSleep = 1
time.sleep(secondsToSleep)
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow,self).__init__()
uic.loadUi('mywindow.ui',self)
self.thread = MyThread()
self.thread.aChanged.connect(self.on_a_changed, QtCore.Qt.QueuedConnection)
self.thread.start()
self.show()
#i show the value only if the checkbox is checked
#QtCore.pyqtSlot(int)
def on_a_changed(self, a):
if self.checkBox.isChecked():
self.textEdit.setText(str(a))
# Run following code when the program starts
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# Declare objects of MyThread class
w = MyWindow()
sys.exit(app.exec_())
An option more to the style of Qt would be to use QThread since it is a class that handles a thread and is a QObject so it handles the signals easily.
import sys
from random import randint
from PyQt4 import QtGui, uic, QtCore
class MyThread(QtCore.QThread):
aChanged = QtCore.pyqtSignal(int)
def run(self):
for i in range(1, 20):
self.aChanged.emit(randint(1, 10))
secondsToSleep = 1
QtCore.QThread.sleep(secondsToSleep)
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow,self).__init__()
uic.loadUi('mywindow.ui',self)
self.thread = MyThread()
self.thread.aChanged.connect(self.on_a_changed, QtCore.Qt.QueuedConnection)
self.thread.start()
self.show()
#i show the value only if the checkbox is checked
#QtCore.pyqtSlot(int)
def on_a_changed(self, a):
if self.checkBox.isChecked():
self.textEdit.setText(str(a))
# Run following code when the program starts
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# Declare objects of MyThread class
w = MyWindow()
sys.exit(app.exec_())
I would like use a Qmessagebox in order to display some info about a running computation and as a stop function when I click on the OK button.
However when I use the signal buttonClicked nothing is happenning and hte function connect with it is never called
Here a code to illustrate my issue:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SenderObject(QObject):
something_happened = pyqtSignal( )
class myfunc():
updateTime = SenderObject()
def __init__(self):
self.i = 0
self.stop = True
def run(self):
while self.stop :
self.i+=1
if self.i%100 == 0:
self.updateTime.something_happened.emit()
print('infinit loop',self.i)
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.setFixedWidth(200)
self.setFixedHeight(200)
self.wid = QWidget()
self.setCentralWidget(self.wid)
self.groups = QHBoxLayout() ####
self.Run = QPushButton('Run')
self.groups.addWidget(self.Run)
self.wid.setLayout(self.groups)
self.Run.clicked.connect(self.run)
self.myfunc = myfunc()
self.myfunc.updateTime.something_happened.connect(self.updateTime)
def run(self):
self.msg = QMessageBox()
self.msg.setText('Click Ok to stop the loop')
self.msg.setWindowTitle(" ")
self.msg.setModal(False)
self.msg.show()
self.myfunc.run()
self.msg.buttonClicked.connect(self.Okpressed)
def Okpressed(self):
self.myfunc.stop = False
#pyqtSlot( )
def updateTime(self ):
self.msg.setText('Click Ok to stop the loop\ni = '+str(self.myfunc.i))
self.parent.processEvents()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.setWindowTitle('window')
ex.show()
sys.exit(app.exec_( ))
So theself.msg.buttonClicked.connect(self.Okpressed) line never call the function Okpressed. Therefore, myfunc.run is never stopped.
Somebody could help on this?
write
self.msg.buttonClicked.connect(self.Okpressed)
before
self.myfunc.run()
If you call run function before subscribing click event, curse will stuck into infinite while loop. so your click event never subscribed.
First subscribe click event and then call "run" function of "myfunc"
And yes never do this -
from PyQt4.QtGui import *
from PyQt4.QtCore import *
Its vbad programming practice. You can write like
from PyQt4 import QtGui
And use into code like
QtGui.QMessagebox
I have a simple dialog with three progress bars that I want to continually update (displaying system resource usage). From reading around the docs, QTimer is the right way to fire a function every x milliseconds (which would update the progress bars). However, I am not able to get it to work and I don't quite know why. It seems relatively simple to connect up the timer timeout signal to an update function, but it never seems to fire.
Here's my code:
import sys
from PyQt4 import QtGui, QtCore
import psutil
class Tiny_System_Monitor(QtGui.QWidget):
def __init__(self):
super(Tiny_System_Monitor, self).__init__()
self.initUI()
def initUI(self):
mainLayout = QtGui.QHBoxLayout()
self.cpu_progressBar = QtGui.QProgressBar()
self.cpu_progressBar.setTextVisible(False)
self.cpu_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.cpu_progressBar)
self.vm_progressBar = QtGui.QProgressBar()
self.vm_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.vm_progressBar)
self.swap_progressBar = QtGui.QProgressBar()
self.swap_progressBar.setOrientation(QtCore.Qt.Vertical)
mainLayout.addWidget(self.swap_progressBar)
self.setLayout(mainLayout)
timer = QtCore.QTimer()
timer.timeout.connect(self.updateMeters)
timer.start(1000)
def updateMeters(self):
cpuPercent = psutil.cpu_percent()
vmPercent = getattr(psutil.virtual_memory(), "percent")
swapPercent = getattr(psutil.swap_memory(), "percent")
self.cpu_progressBar.setValue(cpuPercent)
self.vm_progressBar.setValue(vmPercent)
self.swap_progressBar.setValue(swapPercent)
print "updated meters"
def main():
app = QtGui.QApplication(sys.argv)
ex = Tiny_System_Monitor()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You must keep a reference to the timer object, otherwise it will be immediately garbage-collected when initUI returns:
class Tiny_System_Monitor(QtGui.QWidget):
...
def initUI(self):
...
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateMeters)
self.timer.start(1000)
I am new to Python and PyQt. When I start my program, after a few seconds, the progress bar and label disappear. The progress bar starts appearing and disappearing (the label is gone) when the mouse hovers over the progress bar, showing up once more before disappearing. But if I comment the line where I set up the progress bar value, the label does not disappear.
Here is the code:
from PyQt4 import QtCore, QtGui, Qt
from PyQt4.Qt import QDialog, QApplication
import sys
import sensors
from sensors import *
import threading
class tmp():
def main(self):
global test
global name
sensors.init()
try:
for chip in sensors.iter_detected_chips():
#print (chip)
#print('Adapter:', chip.adapter_name)
for feature in chip:
if feature.label == 'Physical id 0':
test = feature.get_value()
name = feature.label
#print ('%s (%r): %.1f' % (feature.name, feature.label, feature.get_value()))
threading.Timer(5.0, self.main).start()
return test
print
finally:
sensors.cleanup()
zz = tmp()
zz.main()
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.setGeometry(50, 50, 250, 150)
self.setWindowTitle("Title here")
#lay = QtGui.QVBoxLayout()
#lay.addWidget(self.prgB)
#lay.addWidget(self.lbl)
#self.setLayout(lay)
self.home()
def home(self):
self.prgB = QtGui.QProgressBar(self)
self.prgB.setGeometry(20, 20, 210, 20)
self.lbl = QtGui.QLabel(self)
self.lbl.setGeometry(20, 40, 210, 20)
lay = QtGui.QVBoxLayout()
lay.addWidget(self.prgB)
lay.addWidget(self.lbl)
self.setLayout(lay)
self.update()
def update(self):
textas = ('%s : %.1f' % (name, test))
self.lbl.setText(str(textas))
self.prgB.setValue(test)
threading.Timer(5.0, self.update).start()
QtGui.QApplication.processEvents()
self.show()
def run():
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads)
app = QtGui.QApplication(sys.argv)
GUI = MainWindow()
sys.exit(app.exec_())
run()
What I am trying to do is just get the temp value (pysensors) and pass it to the label text and progress bar value. It is working, just as I said, but after a few seconds the label is gone and the progress bar disappears.
I know (or I guess) there is something wrong with the update function. I just can't find out whats wrong.
First of all you don't need the separate class tmp(). Delete it and just move the main() function in MainWindow class. After doing this name,test variables should not be global any more. Define them in your init (for example self.test = 0, self.name='something') and refer to them in the rest of the code as self.test and self.name.
Now the most important mistake in your code is that you are trying to update GUI components from a different thread. GUI components should be handled only by the main thread using the signal/slot mechanism that pyqt provides.
The steps for doing this in your case are
Define a new signal in MainWindow class
Connect this signal to the update() function
Emit this signal from main() function
In the end your code should look like this
class MainWindow(QtGui.QWidget):
signalUpdateBar = QtCore.pyqtSignal()
def __init__(self):
...
self.test = 0
self.name = "0"
...
def home(self):
...
self.signalUpdateBar.connect(self.update)
self.main()
self.show()
def main():
try:
...
self.test = feature.get_value()
self.name = feature.label
threading.Timer(5.0, self.main).start()
self.signalUpdateBar.emit()
finally:
...
Moreover in your update() function
self.prgB.setValue(self.test)
should be the last statement. Anything below that is not necessary.
I'm running into a very strange error while using QThread in PySide. As I run my code, when I click on the button that is supposed to launch the thread and send Signal, I get :
AttributeError: 'builtin_function_or_method' object has no attribute 'displayText'
at line :
self.displayMessage.connect(self.window.displayText)
NB : displayText is a method that I defined in my MainWindow(QWidget) class, while displayMessage is the signal to be emitted.
This error seems, according to the Internet, to occur in various situations, and I couldn't find one that suits my case yet. Therefore I have 2 questions :
Have you guys ever met this error before in a similar situation, and could you (if yes) give me some tips ?
Am I doing it right ? I can't post my code directly down here, so I created a short verifiable example in which I used the same process. Unfortunately, it seems to work perfectly. Please tell me if at least my construction is correct.
Thank you very much.
EDIT : I eventually figured out that I have forgotten a self somewhere in my big code, but the actual error was hidden to me behind this one that I didn't know. I'm still interested in whether or not this code is reliable, and am fully open to suggestions of improvement.
Without thread
When you click "GO !" and try to move the window, you can see that it freezes for a second.
#!/usr/bin/env python
# -*- encoding : utf-8 -*-
import sys
import time
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QWidget):
def __init__(self, qt_app):
QWidget.__init__(self)
worker = Worker(self)
self.qt_app = qt_app
self.setGeometry(100, 100, 220, 40)
self.infoPanel = QTextEdit(self)
self.infoPanel.setReadOnly(True)
self.goButton = QPushButton('GO !', self)
self.goButton.clicked.connect(lambda: worker.displayOnWindow())
self.layout = QVBoxLayout()
self.layout.addWidget(self.infoPanel)
self.layout.addWidget(self.goButton)
self.setLayout(self.layout)
def launch(self):
self.show()
self.qt_app.exec_()
class Worker():
def __init__(self, window):
self.counter = 0
self.window = window
def displayOnWindow(self):
time.sleep(1)
self.window.infoPanel.append(str(self.counter))
self.counter += 1
if __name__=='__main__':
qt_app = QApplication(sys.argv)
mw = MainWindow(qt_app)
mw.launch()
With thread
Now you can move the window without trouble, since the sleeping thread is not the one that displays the window. I've written a sub-class for my thread because in the original code there are some functions that will be called by several buttons.
#!/usr/bin/env python
# -*- encoding : utf-8 -*-
import sys
import time
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QWidget):
def __init__(self, qt_app):
QWidget.__init__(self)
worker = SubWorker(self)
self.qt_app = qt_app
self.setGeometry(100, 100, 220, 40)
self.infoPanel = QTextEdit(self)
self.infoPanel.setReadOnly(True)
self.goButton = QPushButton('GO !', self)
self.goButton.clicked.connect(lambda: worker.start())
self.layout = QVBoxLayout()
self.layout.addWidget(self.infoPanel)
self.layout.addWidget(self.goButton)
self.setLayout(self.layout)
def launch(self):
self.show()
self.qt_app.exec_()
#Slot(int)
def displayOnWindow(self, i):
self.infoPanel.append(str(i))
class Worker(QThread):
displayMessage = Signal(int)
def __init__(self, window):
QThread.__init__(self)
self.counter = 0
self.window = window
self.displayMessage.connect(self.window.displayOnWindow)
def run(self, *args, **kwargs):
return QThread.run(self, *args, **kwargs)
class SubWorker(Worker):
def __init__(self, window):
Worker.__init__(self, window)
def run(self):
time.sleep(1)
self.displayMessage.emit(self.counter)
self.counter += 1
if __name__=='__main__':
qt_app = QApplication(sys.argv)
mw = MainWindow(qt_app)
mw.launch()