Python: Update GUI After Instruction - python

I designed a program to solve Rubik's cubes, and now I'm building a GUI for it using PySide. My program generates a list of moves required to solve the cube, and then executes them one by one. I would like my program to show the status of the cube for a short period of time between each move.
Currently, I'm trying to use the time module to make the program wait between executing moves. Basically like this:
for move in algorithm:
executeMove()
updateDisplay()
time.sleep(0.1)
I figured this method would work just fine. However, when I run the application, it looks like it's sleeping for the sum time of each of the sleep calls, then showing the end result of the algorithm. I would ideally like to make it show a move, sleep 0.1, show a move, sleep 0.1, etc.
Is the sleep function ideal for the type of behavior I'm trying to get? Should I be using something different entirely? Thank you for your suggestions.

It'd be good to see a little more code, but it looks like you're probably blocking the main Qt thread. To accomplish what you want to do, you will need to be multi-threaded and use pyQtSignals to update the UI. Here's a (maybe buggy) template
class MainWindow(QtWidgets.QMainWindow):
updateSignal = QtCore.pyqtSignal()
def __init__(self, algorithm):
super(MainWindow, self).__init__()
self.algorithm = algorithm
self.updateSignal.connect(self.updateDisplay)
self.loopThread = None
self.startMonitoring()
def startMonitoring(self):
self.loopThread = MonitorLoop(self.updateSignal.emit, self.algorithm)
self.loopThread.start()
def updateDisplay(self):
""" Update the UI here"""
pass
class MonitorLoop(QtCore.QThread):
def __init__(self, signal, algorithm):
super(MonitorLoop, self).__init__()
self.signal = signal # needs to be a pyQtSignal if it will update the UI
self.algorithm = algorithm
def run(self):
for move in self.algorithm:
executeMove()
self.signal()
time.sleep(0.1)
If you are using Qt 4, you will need to substitute QtGui for QWidgets. Of course, I don't actually know what algorithm is, so your implementation will need to accomodate that.

Related

PyQT force update textEdit before calling other function

My question concerns PyQT5.
I try to have a dialog window with a button that when clicked
updates some text of a QTextEdit field
calls a function (which needs much time to terminate)
Something like this:
class StartDialog(QtWidgets.QWidget, start_dialog_ui.Ui_Dialog):
def __init__(self, parent):
super(self.__class__, self).__init__()
self.setupUi(self)
self.OKButton.clicked.connect(self.start)
def start(self):
self.startDialogTextEdit.append("simulation running ...")
run_lengthy_function(self)
However, when I run my GUI I notice that the text is updated only after the lengthy function has terminated, although the QTextEdit.append is called before the lengthy function. How can I enforce that the text is updated in advance?
What I tried so far (but didn't work) was to let Python wait some time before triggering the lengthy function call, i.e.
from time import sleep
class StartDialog(QtWidgets.QWidget, start_dialog_ui.Ui_Dialog):
def __init__(self, parent):
super(self.__class__, self).__init__()
self.setupUi(self)
self.OKButton.clicked.connect(self.start)
def start(self):
self.startDialogTextEdit.append("simulation running ...")
sleep(5)
run_lengthy_function(self)
The repaint is called in event loop so sleep the whole thread does not change anything.
You can call repaint manually by:
self.startDialogTextEdit.repaint()
or call static method:
QCoreApplication.processEvents()
which also call repaint internally
The solution for the case that the text is displayed in the QTextEdit is to call qApp.processEvents(), this force to the GUI update:
def start(self):
self.startDialogTextEdit.append("simulation running ...")
QtWidgets.qApp.processEvents()
[...]
On the other hand if the task is heavy it may be blocking the GUI, so maybe one solution is to run it on another thread, I can not give a proper recommendation since I do not know your function

how to send a value from one class to another using pyqt4 and python

i'm having some trouble in building my app, I have a GUI made in pyqt4 and Python, I use QThread to check the cpu load every 2 seconds and I want to diplay it on a progress bar. My GUI is in one class and my Qthread is in the other.
This is my code: pyqt classes, my code printscreen
I want to know how to pass my values collected in QThread to Qobjects in my other Class.
import sys,os,module,config_read,time,threading,datecs_print,mysql.connector as mariadb,commandList
import psutil,logging
from PyQt4 import QtGui, uic ,QtSql,QtCore
from PyQt4.QtCore import QThread, SIGNAL
import resources
import webbrowser
sys.stderr = open("errlog.txt", "w")
class systemValues(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def cpuRunValue(self):
while (1):
for x in range(2):
p = psutil.cpu_percent(1,False)
return p
def cpuProgressBarUpdate(self):
while(1):
# MyWindow.setcpuBarValue(val=77)
MyWindow.cpuBar.setValue(value=77)
def run(self):
# self.cpuRunValue()
print(self.cpuRunValue())
# self.cpuProgressBarUpdate()
class MyWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QDialog.__init__(self)
super(MyWindow, self).__init__()
file_path = os.path.abspath("ui/sales_window.ui")
uic.loadUi(file_path, self)
self.myThread = systemValues()
self.myThread.start()
def setcpuBarValue(self):
threading.Thread(target=self.cpuBar.setValue(systemValues.cpuRunValue())).start()
This is my code, I get no error. I just cant transfer my value I get from cpuRunValue() to QprogressBar from MyWindow. I'm not very experienced with this.
PS: I eliminated lots of code that's not necessary, but please let me know if you need more info.
Thank You.
If you want to use threads in Qt then you must understand them well. For instance read this page first. Certainly don't mix the QThread with the threading package from the Python standard library.
I hope you're not offended if I think that your probably fairly new to programming. I advise you to not use threads (in general and in Qt) until you have more experience. Threads are hard to use correctly, even for seasoned programmers, and therefore should only be used if necessary. For this reason the Qt documentation on threads even includes a section about alternatives.
You want to execute a function every 2 seconds, so in your case the best solution is to use a QTimer and connect its timeout signal to a slot that updates the progress bar. Like so:
#!/usr/bin/env python
import sys
import psutil
from PyQt5 import QtCore, QtWidgets
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
self.progressBar = QtWidgets.QProgressBar()
self.layout.addWidget(self.progressBar)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateCpu)
self.timer.start(100) # Every 0.1 seconds
def updateCpu(self):
cpu = psutil.cpu_percent(None, False)
#print("cpu = {}%".format(cpu))
self.progressBar.setValue(cpu)
def main():
app = QtWidgets.QApplication(sys.argv)
win = MyWidget()
win.show()
win.raise_()
app.exec_()
if __name__ == "__main__":
main()
Note that I changed the first parameter of cpu_percent to None. This makes the call return immediately instead of lasting one second (and therefore you can update the progress bar more often if you want). Setting it to None causes the first call of cpu_percent to return a meaningless value, but in your cause that's not an issue I think. See the documention.
Finally, even though you removed a lot of unnecessary code, your code example was not yet minimal (e.g. the import datecs_print is not necessary to run it, and I don't have this module). I was also not complete (e.g. the "ui/sales_window.ui" file was missing so I couldn't run it). Please read the page on how to make an MCVE. You will get a lot more help next time if you include a small program that we can just copy-paste-execute.
Hope this helps

PyQt4 - Running functions that use python 2.7.11's builtin modules in separate threads

i'm making an application for my grad paper at the university and i'm stuck with threading.
i'm trying to make an audio player that loads files into a table and plays them while taking specified intervals into account (basically sleeps after each sound played). i can get the list to play in order but it's in the same thread so GUI gets stcuk while that's happening. i used PyQt4 for the GUI because it was the fastest way to make a GUI and i'm visually impaired so i don't want to waste time coding all the UI stuff.
i've seen some QThread examples but i can't seem to get my threading to work.
i'm using winsound to play the sounds and they are loaded from an internal list which correpsonds to the table that displays on GUI
the file_list is an Ui_MainWindow instance variable (basically the main application class's variable) and all the functions are also defined in that class
Here's the relevant code:
import sys
from PyQt4 import QtGui, QtCore
from winsound import PlaySound, SND_FILENAME
#some more code for the Ui_MainWindow class
def play_stop(self):
t=Create_thread(self.snd_play) t.started.connect(func)
t.start()
def snd_play(self):
if not self.is_playing:
self.is_playing=True
for e in self.file_list:
PlaySound(e, SND_FILENAME)
self.is_playing=False
class Create_thread(QtCore.QThread):
def __init__(self,function):
QtCore.QThread.__init__(self)
self.function=function
def run(self):
self.function()
def main():
app=QtGui.QApplication([])
window=Ui_MainWindow()
window.setupUi(window)
window.show()
sys.exit(app.exec_())
I made a Create_thread class because i wanted a quick way to run functions in separate threads which is why the run function executes the function given as the argument
this worked when i was testing without GUI and with the threading module but when i introduced the GUI it stopped working and crashed my program
like i said the play_stop and snd_play are functions of the Ui_Mainwindow class
Any help would be greatly appreciated because without threading my application won't work properly.
i found the issue with the threading module (it was my fault, of course)
For anyone having similar issues here's the correct class code:
class Create_thread(threading.Thread):
def __init__(self,function):
threading.Thread.__init__(self)
self.function=function
def run(self):
self.function()
so i just needed to call the init function of the Thread class.
also here's the play_stop function code:
def play_stop(self):
t=Create_thread(self.snd_play) #calls the actual function to play
t.start()
#101 thanks for your response

using 'stop button' to quit function

So I'm trying to create a timer or sorts, the start button starts the timer, i need to stop the timer with the stop button and then record the time... I can't seem to figure out how to stop the timer function once its started. Ive tried if statements, disconnect(), and many others...
I realize that there is a timer in qt already but I'm trying to figure it out this way. thanks.
import sys
import time
from PyQt4 import QtCore, QtGui, uic
form_class = uic.loadUiType("/Users/Home/Desktop/Timer/timer.ui")[0]
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.startButton.clicked.connect(self.timer)
def timer(self):
timeL = 0
self.stopButton.clicked.connect(False)
while True:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
TL;DR: You're after QElapsedTimer, not QTimer.
The operating system is already measuring the passage of time for you. You won't do any better job at it yourself.
"I'm trying to figure it out this way" - it will be less accurate than using QElapsedTimer because you're assuming that exactly as much time has passed as you wished to sleep for. This is almost never true: the actual amount of time that passed is different than the argument to sleep. Worse yet, the errors are usually of a systematic kind too, so your time accumulation will have a bias and will get less accurate as time passes. So, don't do it. It makes no sense. Perhaps you're not telling us exactly what you're trying to do: if you're asking about a particular solution that doesn't work, it helps to say what problem the solution is supposedly to. Why are you trying to figure it out this (wrong) way?
There are conceptually three different kinds of timers in Qt:
QElapsedTimer is like a stopwatch: it is an interface to the operating system's way of measuring the passage of time. That's the class you should be using to measure how much time has passed between the button clicks.
QTime is like a wall clock: you can ask it what time it is through currentTime(), and take difference between two readings of time to obtain elapsed time. Use this class only if you need to know the absolute time, otherwise QElapsedTimer will offer better resolution for elapsed time measurements.
QTimer is a source of timeouts: it is a way to periodically call your code. This is not meant to be used in measuring time, but merely to let your code run periodically, e.g. when you wish to refresh screen display, or implement a metronome that beeps periodically. There are no guarantees that your code will be called on time, and no guarantees that some ticks won't be missed. You want it guaranteed, you need to write a kernel driver, no way around that.
Below is a complete example using PyQt4, for Python 3.5. It uses QElapsedTimer to measure the passage of time between button presses, and QTimer to keep the time display refreshed.
#!/usr/bin/env python
#https://github.com/KubaO/stackoverflown/tree/master/questions/interval-timer-38036583
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
if __name__ == "__main__":
running = False
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
layout = QtGui.QVBoxLayout(w)
label = QtGui.QLabel()
button = QtGui.QPushButton('Start')
timer = QtCore.QElapsedTimer()
updateTimer = QtCore.QTimer()
layout.addWidget(label)
layout.addWidget(button)
def onTimeout():
label.setText('Elapsed: {0}ms'.format(timer.elapsed()))
def onClicked():
global running
if running:
onTimeout()
updateTimer.stop()
button.setText('Start')
else:
timer.start()
updateTimer.start()
button.setText('Stop')
running = not running
updateTimer.setInterval(1000/25) # ~25fps update rate
updateTimer.timeout.connect(onTimeout)
button.clicked.connect(onClicked)
w.show()
sys.exit(app.exec_())
Use a flag to control the loop. Then reset the flag in the slot connected to the stop button:
self.startButton.clicked.connect(self.timer)
self.stopButton.clicked.connect(self.stop)
def stop(self):
self._timer_flag = False
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
A QTimer is better, because there is no lag in updating the ui. But you can improve your example by using an inner loop to call processEvents more frequently:
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
for _ in range(10):
# process events for up to 50 milliseconds
app.processEvents(QtCore.QEventLoop.AllEvents, 50)
time.sleep(0.1)
Sounds like you want to use QTimer on a QThread.
This answer should give you everything you need.
https://stackoverflow.com/a/18960953/5757280

Wrap a Python class in an asynchronous PyQt class

I am developing my pet project. It is a small app which downloads my inbox folder from Facebook. I want it to be available in both CLI and GUI (in PyQt) mode. My idea was that I write a class for the communication, and then the front-ends. I know that the downloading process is blocking which is not problem in CLI mode but it is in GUI.
I know that there is QNetworkAccessManager but I would have to re-implement the class that I have already written and then maintain two classes the same time.
I was searching for a while and I came up with one solutions where I subclass QObject and my FB class, implement the signals, than create a QThread object and use moveToThread() method which would be OK, but I must take care of creating and stopping the tread.
Is it possible to wrap my Python class to something to make it behave like QNetworkAccessManager? So the methods would return immediately and the object will emit signals when the data is ready.
Update
Thank you the comments. I have 2 main classes. The first is called SimpleGraph which just hides urllib2.urlopen(). It prepares the query and returns the decoded json got from Facebook. The actual work is happening in FBMDown class:
class FBMDown(object):
def __init__(self, token):
self.graph = SimpleGraph(token)
self.last_msg_count = 0
def _message_count(self, thread_id):
#TODO: Sanitize thread_id
p = {'q': 'SELECT message_count FROM thread WHERE thread_id = {0} LIMIT 1'.format(thread_id)}
self.last_msg_count = int(self.graph.call(params=p, path='fql')['data'][0]['message_count'])
return self.last_msg_count
So here when _message_count is called it returns the number of messages of the given thread id. This method works great in CLI mode, blocking is not a problem there.
I want to wrap this class into a class (if it is possible) which works asynchronous like QNetworkAccessManager, so it would not block the GUI but it would emit a signal when the data is ready.
The only technique I know now is to subclass QObject. It looks like this:
class QFBMDown(QtCore.QObject):
msg_count_signal = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
QtCore.QObject.__init__(self, parent)
def get_msg_count(self):
#Here happens the IO
time.sleep(2)
self.msg_count_signal.emit(1)
And this is my Windows class:
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.centralwidget = QtGui.QWidget(self)
self.button_getmsgcount = QtGui.QPushButton(self.centralwidget)
self.setCentralWidget(self.centralwidget)
self.i = 0
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.block_indicator)
self.timer.start(20)
#self.worker = QtCore.QThread()
self.fbobj = QFBMDown()
#self.fbobj.moveToThread(self.worker)
#self.worker.start()
self.button_getmsgcount.clicked.connect(self.fbobj.get_msg_count)
self.fbobj.msg_count_signal.connect(self.show_msg_count)
def block_indicator(self):
self.button_getmsgcount.setText(str(self.i))
self.i += 1
def show_msg_count(self, data):
print 'Got {0} msgs in the thread'.format(data)
Now when I press the button the GUI blocks. If I de-comment line 13, 15, 16 (where I create a thread, move fbobj into this thread then start it) it does not block, it works nearly as I want but I have to do everything by hand every time and I have to take care of shutting down the thread (now I implement QMainWindow's closeEvent method where I shut down the thread before quit but I'm sure that this is not the right way to do).
Is it even possible what I want to do? I would not want to implement a new class and then maintain two classes which are doing the same thing.
The reason I am so insisted to do it like this is to make my app work without Qt but give a nice gui for those who don't want to type command line arguments.

Categories

Resources