PyQt4 QMessageBox text change - python

I am using this simple code to show warning boxes:
w = QWidget()
result = QMessageBox.warning(w, 'a', x, QMessageBox.Ok)
Is there any way to change MESSAGE dynamically? I want to make a popup which will inform user abut progress of a task that is running in background.
Edit:
Well I tried to do so making this script for testing:
def handleButton(self):
self.msgBox = QMessageBox(self)
self.msgBox.setWindowTitle("Title")
self.msgBox.setIcon(QMessageBox.Warning)
self.msgBox.setText("Start")
self.msgBox.show()
x = 0
for x in range (100):
x = x + 1
print (x)
self.msgBox.setText(str(x))
self.msgBox.show()
time.sleep(1)
The text only shows after finishing the 'for loop', why?

Instead of using a static method you could create an object of the class.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Widget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.msgBox = QMessageBox(self)
self.msgBox.setWindowTitle("Title")
self.msgBox.setIcon(QMessageBox.Warning)
self.msgBox.setText("Start")
self.msgBox.show()
timer = QTimer(self)
timer.timeout.connect(self.onTimeout)
timer.start(1000)
def onTimeout(self):
self.msgBox.setText("datetime: {}".format(QDateTime.currentDateTime().toString()))
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
Update:
The problem in your example is the use of time.sleep(). Qt is executed in an eventloop, this eventloop allows you to handle the events of the mouse, keyboard, redraw, etc. but the time.sleep() blocks the eventloop, this you can check trying to change the size of the window, you will see that you can not do it.
Assuming you use time.sleep() to pause, then you must use QEventLoop with QTimer that does not block the Qt eventloop.
def handleButton(self):
self.msgBox = QMessageBox(self)
self.msgBox.setWindowTitle("Title")
self.msgBox.setIcon(QMessageBox.Warning)
self.msgBox.setText("Start")
self.msgBox.show()
for x in range(100):
self.msgBox.setText(str(x+1))
loop = QEventLoop()
QTimer.singleShot(1000, loop.quit)
loop.exec_()

Related

How to update and refresh in PyQt5 at a higher frequency

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class MainWindow(QWidget):
signal = pyqtSignal()
def __init__(self):
super(MainWindow, self).__init__()
self.buttonList = list()
self.counter = 0
self.setUI()
self.signal.connect(self.updatebutton)
self.startTimer(10)
def timerEvent(self, *args, **kwargs):
self.signal.emit()
def updatebutton(self):
for button in self.buttonList: # type: QLabel or QPushButton
button.setText(str(self.counter))
self.counter += 1
def setUI(self):
gridLayout = QGridLayout()
for i in range(10):
for j in range(10):
button = QLabel(self)#QPushButton(self)
self.buttonList.append(button)
gridLayout.addWidget(button, i, j)
self.setLayout(gridLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
When I set the timer at 10 ms, all Labels can increase its value by 100 per second. However, when I change the timer to 1 ms, all Labels can not increase its value by 1000 per second.
When I use Pushbutton instead of Label, even the timer is set to 100 ms, all buttons can not increase their value by 100 per second. But when I reduce the number of buttons, e.g., totally 10 buttons instead of 100, these buttons can work well.
3.I meet with this problem recently when I used a data acquisition software called DEWESOFT, which can produce many subwidget or subwindow(very similar to QMidArea and QMidSubwindow) to draw curves and display values of high-frequency signals. Hence, I'd like to find out how and why. I wonder how to control the refresh rate of widget and how to determine the limit of UI refresh ability. If I wanna refresh more widgets at a high frequency, what should I do?
Later I tried Thread to improve, it seems to work a litter better, but I don't know why.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading
import sys
import time
class MainWindow(QWidget):
signal = pyqtSignal()
def __init__(self):
super(MainWindow, self).__init__()
self.buttonList = list()
self.counter = 0
self.setUI()
self.signal.connect(self.updatebutton)
self.thread = threading.Thread(target=self.timerevent, args=())
self.thread.setDaemon(True)
self.thread.start()
# self.startTimer(10)
def timerEvent(self, *args, **kwargs):
self.signal.emit()
def timerevent(self):
while True:
self.signal.emit()
time.sleep(0.01)
def updatebutton(self):
print(self.counter)
for button in self.buttonList: # type: QPushButton
button.setText(str(self.counter))
self.counter += 1
def setUI(self):
gridLayout = QGridLayout()
for i in range(10):
for j in range(10):
button = QPushButton(self)
self.buttonList.append(button)
gridLayout.addWidget(button, i, j)
self.setLayout(gridLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())

Break an infinit loop when button is pressed

I am currently starting to use PyQt5 and created my first GUI. Now I would like a program that does the following. When Button 'start' is pressed execute 'function' n-times until Button 'stop' is pressed (imagine a stopwatch) if I press Button 'start' again 'function' gets once again executed and so on.
What I tried so far is the following, that can not work, since we are not able to change a variable from outside the loop (mockup-script)
class Ui(QtWidgets.QDialog):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('interfaceScan.ui', self)
self.startScan = self.findChild(QtWidgets.QPushButton, 'startScan')
self.startScan.clicked.connect(self.startPressed)
self.pauseScan = self.findChild(QtWidgets.QPushButton, 'pauseScan')
self.pauseScan.clicked.connect(self.pausePressed)
self.show()
def startPressed(self):
global pauseScan
pauseScan = False
dosomething()
def pausePressed(self):
global pauseScan
pausScan = True
def dosomething():
while pauseScan == False: #not needed, but the measurement should be executed periodically until 'pause' is pressed
print('Running') #in the reals program the measurement will be executed here
time.sleep(4) #between each measurement the program needs to wait a cirtain amount of time ~1h
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
Any ideas on how I can solve this problem? I am now relatively certain that it does not work when using a while loop, so I am open to suggestions on how to change it up!
The purpose of this script will be to control a measurement setup that should run for let's say 1000 cycles, but I would like to be able to break it in between to change so parameters.
As the OP points out in the comments, the while loop with the time.sleep() only aims to perform a periodic task but this generates a problem since the time.sleep() freezes the GUI, instead a QTimer must be used:
from PyQt5 import QtCore, QtWidgets, uic
class Ui(QtWidgets.QDialog):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi("interfaceScan.ui", self)
self.startScan.clicked.connect(self.startPressed)
self.pauseScan.clicked.connect(self.pausePressed)
self.timer = QtCore.QTimer(self, interval=4 * 1000, timeout=dosomething)
#QtCore.pyqtSlot()
def startPressed(self):
QtCore.QTimer.singleShot(0, dosomething)
self.timer.start()
#QtCore.pyqtSlot()
def pausePressed(self):
self.timer.stop()
def dosomething():
print("Running")
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Ui()
window.show()
sys.exit(app.exec_())

buttonClicked signal of QMessageBox isn't working

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

PyQt4: use a QTimer to continually update progress bars

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)

Problems with the order execution functions when use PyQT4

I have a problem with PyQT4 for Python. There is a label with text and button connected to function. The function could change the text of label first, then call other function. There is a problem with it: the function is executed firts, then change text of the label.
Code:
# -*- coding: utf-8 -*-
import time
import sys
from PyQt4 import QtCore, QtGui
def timesleep():
print("start sleep")
time.sleep(5)
print("stop sleep")
class AnyWidget(QtGui.QWidget):
def __init__(self,*args):
QtGui.QWidget.__init__(self,*args)
self.setWindowTitle("PETHARD")
boxlay = QtGui.QHBoxLayout(self)
frame = QtGui.QFrame(self) # Фрейм
frame.setFrameShape(QtGui.QFrame.StyledPanel)
frame.setFrameShadow(QtGui.QFrame.Raised)
gridlay = QtGui.QGridLayout(frame) # Менеджер размещения элементов во фрейме
label = QtGui.QLabel(u"Welcome",frame) # Текстовая метка.
global glabel
glabel = label
gridlay.addWidget(label,0,0)
button1 = QtGui.QPushButton(u"Load From MC", frame)
self.connect(button1, QtCore.SIGNAL("clicked()"), self.ts)
gridlay.addWidget(button1,1,0)
boxlay.addWidget(frame)
def ts(self):
global glabel
glabel.setText(u"Waiting...")
timesleep()
if __name__=="__main__":
app = QtGui.QApplication(sys.argv)
aw = AnyWidget()
aw.show()
sys.exit(app.exec_())
Help me please to fix this problem.
It work's like that because rendering is done later in app. So your glabel.text is changed immediately but you will see changed text on screen after your ts function call, because drawing is done at the end of loop.
If you really want to call your function in new frame (after rendering of new text) then use timer:
timer = QtCore.QTimer()
QtCore.QObject.connect(
timer,
QtCore.SIGNAL("timeout()"),
self.ts
)
timer.start(10)
It should call your function ten milisecond later, so in fact probably after rendering.
You never want to tie-up your GUI with a long-running function. That the label does not update is only one manifestation of the problem. Even if you get the label to update before the function is called, it will still "freeze" your GUI -- no widget will respond to the user until the long-running function completes.
If you have to run such a function, see if there is a way to break it up into small pieces which each relinquish control to the GUI, or consider using a separate thread to run the function:
import time
import sys
from PyQt4 import QtCore, QtGui
class TimeSleep(QtCore.QThread):
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
self.parent = parent
def run(self):
print("start sleep")
time.sleep(5)
print("stop sleep")
self.parent.glabel.setText(u"Done")
class AnyWidget(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
self.setWindowTitle("PETHARD")
boxlay = QtGui.QHBoxLayout(self)
frame = QtGui.QFrame(self) # Фрейм
frame.setFrameShape(QtGui.QFrame.StyledPanel)
frame.setFrameShadow(QtGui.QFrame.Raised)
gridlay = QtGui.QGridLayout(
frame) # Менеджер размещения элементов во фрейме
label = QtGui.QLabel(u"Welcome", frame) # Текстовая метка.
self.glabel = label
gridlay.addWidget(label, 0, 0)
button1 = QtGui.QPushButton(u"Load From MC", frame)
self.connect(button1, QtCore.SIGNAL("clicked()"), self.ts)
gridlay.addWidget(button1, 1, 0)
boxlay.addWidget(frame)
def ts(self):
self.glabel.setText(u"Waiting...")
self.thread = TimeSleep(parent=self)
self.thread.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
aw = AnyWidget()
aw.show()
sys.exit(app.exec_())
Here is a wiki page on how to deal with long-running functions.
The text of the label is being changed, but because you're blocking the main thread you're not giving Qt a chance to paint it.
Use QCoreApplication::processEvents and QCoreApplication::flush:
def ts(self):
glabel.setText(u"Waiting...")
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.flush()
timesleep()

Categories