How does a PyQt intruction is proceeding? [closed] - python

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
As part of my end-of-studies' project I'm working on a GUI which basically takes input values and run calculation from them. My issue occurs when I push the button which run these calculation. I want this button to be disabled during the calculation and its text to change for showing that the GUI is busy.
Unfortunately, these changes are displayed on the GUI only when the calculation is over, not before and even if they are called before the function that runs the simulation in the code.
Therefore I was wondering how these instructions are proceeding so I can maybe understand what's going on underneath.
Here's a short extract of the code if you want it :
I call the function 'onButtonClicked' when I push it
self.pushButton_simu.clicked.connect(self.onButtonClicked)
Then this (shortened) function calls 'displayGraph' which will do the calculation and display the results
def onButtonClicked(self):
if (not self.exist and self.simu):
self.displayGraph()
Finally in this one I create a file where I put some values then try to disable and change the text of my button (which does not work immediately). Then I run the calculation with 'simu.simulation' and when it is over, my messagebox is displayed and so are my new text and my button disabled. Then when I close the messagebox the text changes immediately and my button becomes unable again.
def displayGraph(self):
with open("Input_simulation", 'wb') as dataFile:
pickle.dump([self.planete.reynolds(), self.planete.reynoldsMagnetique(), self.planete.stuart(), int(self.label_Vnbmode_2.text()), self.spinBox_pasmode.value()], dataFile)
self.pushButton_simu.setText("Simulation en cours")
self.pushButton_simu.setEnabled(False)
mode, wImMax = simu.simulation('Input_simulation')
infoBox = QtWidgets.QMessageBox()
infoBox.setIcon(QtWidgets.QMessageBox.Information)
infoBox.setWindowTitle("Information")
infoBox.setText("Simulation réussie")
infoBox.exec_()
self.pushButton_simu.setEnabled(True)
self.pushButton_simu.setText("Lancer la simulation")
I tried to add a 'time.sleep' between setText and the beginning of the simulation but it doesn't work at all so I really don't understand what's going on. It's like it's too busy by the simulation to display the changes.
EDIT : I fixed my issue by using that thanks to rbaleksandar :
QtCore.QCoreApplication.processEvents()

You need to run the calculations in a separate thread. Otherwise experience exactly what's happening in your case - your button gets blocked (in fact the whole UI is) since intensive calculations takes all the processing time of your main thread, where also the UI resides (along with all things related to it like drawing, event handling etc.). If the thread is busy calculating your stuff, it has no "time" left doing anything else.
Typically this is done by creating a QThread and pushing (using moveToThread(...)) of another object to it, where you actual calculations take place. This object is called the worker, while the thread - the worker thread. You can then connect a signal from your worker to your button or the widget that contains it and disable/enable it respectively.
Alternative to that solution you can go for QRunnable with a slight modification to enable it to actually send signals (since QRunnable is not actually a QObject-derived class hence you need to explicitly inherit from QObject in order to use slots and signals).

Related

Background process and tkinter

Looking for help on where to start with this, not too good with Python. What I trying to do is use tkinter for a gui interface but i need to be able to process recieved data and update labels widgets as information changes. I all ready have the communication portion of my program working fine in the shell but when I try to tie it to tkinter it will stop processing as soon as the interface is generated. Anyone have a simple code for me to modify to my needs or point me to a reference example somewhere. Spent days so far trying different options and I still have yet to find something that works.
Thanks for any help
Convert your working program into functions that you can register as callbacks in the tkinter UI (say buttons, or other widgets), that is, make it event-driven, and then, for background processing register some of the functions with the after widget method. The root.mainloop() will never return (only on UI close), use it as the last instruction.
So you can't just write your logic in a top-down structure, and hope that it will work well with the UI. The mainloop will be permanently looping, and will call specific funtions in your code, as appropriate to the received events from the user, or to callbacks you registered to run after some time with after.
See here for the after part
Take a look here for structuring tkinter programs. It should have enough info and links for you to study and learn how to do it in a right way.

PyQT force rendering some widget before calling other function

I am designing a GUI for a Python project that manages some simulations. As the simulations take some time, I want to have a window that tells the user to wait, popping up when the simulation is started.
Currently, the code is like
def start_simulation(self):
self.calculation_running_dialog.show()
heavy_function_call(self)
self.calculation_running_dialog.hide()
However, I face the following problem: When the simulation is started the window does appear, but it is rendered with a transparent body in Ubuntu and with an empty (white) body in Windows. Only after the heavy function call has terminated, the window is rendered properly. How can I force the window to be displayed properly before the simulation starts?
Recently, I have asked a similar question here: PyQT force update textEdit before calling other function.
For that purpose, the repaint() function turned out to be the solution. But for the current problem, neither self.calculation_running_dialog.repaint() nor self.calculation_running_dialog.update() or self.calculation_running_dialog.textLabel.repaint() does the trick.
Any suggestions?

Functions acting strange when run as thread in Kivy/Python

I am building an app that, when the user hits a 'run' button, generates a table of buttons.
Because this process takes a while, I want to add a popup or progress bar to alert the user that the function is running and not frozen. To do this I decided to create a popup and call my function using threading so that the screen will be updated when the function starts (as opposed to once it is done).
mythread = threading.Thread(target=run_function)
mythread.start()
The trouble is that when I call my function from the above code it works very strangely: the columns of my table are the wrong width, some of my buttons are arbitrarily empty, and others have the wrong fill color. To fix this, all I need to do is to remove the threading operation and simply call run_function()
Any idea why this is happening?
I am new to Python, so it is likely some dumb mistake, but I have no idea. What is different between a process running as a thread and its default operation?
Disclaimer: I haven't worked with Kivy.
Not every framework works well with multithreading.
But most of the GUI frameworks have an event loop which is responsible for managing user events (mouse clicks, keyboard) and queued drawing operations (widgets).
In your case if don't want the UI to be freezed, you should regularly give control to your framework's event loop.
I guess kivy.base.EventLoopBase.dispatch_input is what you need to call to show an added widget or to handle user events.

Reading user's input during simulation via GUI [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I'm in the process of converting a simulation based application using simpy into a GUI.
The program currently runs within the console and the simpy which is doing the simulation runs by default in a loop like syntax. And that's where my issue seems to be.
Currently, within the console version of the code, I grab user input through the raw_input() function and that is able to interrupt the code and allow the user to input the values that the simulation desires. However, despite researching it, there does not seem to be a similar and clean way of doing this through pyqt inisde the GUI I'm building.
Would the only way be to run the processes in different threads? And if I were to do that approach, how exactly would that look and truly function?
PyQt is event-based. It's running a loop continuously waiting for events, and it calls your callbacks (or signals your slots) when it gets an event you care about. So, there's no way to directly say "block until I get input".
But, before you even get to that point, if your simulation is running a loop continuously in the main thread, PyQt can't also be running a loop continuously in the main thread. So it can't respond to events from the OS like "update your window" or "quit". As far as your user is concerned, the app is just frozen; she'll see nothing but the ever-popular beachball (or other platform equivalent).
And however you choose to solve that first problem will solve most of the second one almost for free.
Why your GUI app freezes attempts to explain the whole issue, and all of the possible solutions, in general terms, using Tkinter as an example of a GUI library. If you want something more Qt-specific, I'm pretty sure there's a whole section about it in the Qt tutorial, although I'm not sure where, and you may have to translate a bit of C++ to Python in your head.
But there are two major options: Callbacks, or threads.
First, you can break your loop up into small pieces, each of which only takes a few milliseconds. Instead of running the whole loop, you run the first piece, and as its last line, it asks PyQt to schedule the next piece as soon as possible (e.g., using a QTimer with a timeout of 0). Now, Qt will get to check for events every few milliseconds, and if it's got nothing to do, it will immediately kick off the next step of your work.
If your flow control is already built around an iterator (or push-coroutine) that yields appropriately-sized chunks, this is very easy. If not, it can mean turning the flow control in your outer loop inside-out, which can be hard to understand.
So, having done this, how do you get user input? Simple:
Where you would have called raw_input, instead of scheduling the next piece of your code, instead do some appropriate GUI stuff—create a popup messagebox, unhide a text entry control and button, whatever.
Connect the next piece of your code as a handler for the button-clicking or messagebox-accepting or whatever signal.
Alternatively, you can run your work in a background thread. This doesn't require you to reorganize anything, but it does require you to be careful not to share anything between threads. Unfortunately, this includes calling methods on GUI widgets from the background thread, which you'd think would make it impossible to do anything useful. Fortunately, PyQt has mechanisms to deal with that pretty easily: signals are automatically routed between threads as necessary.
So, how do you ask for user input in this scenario?
Split off everything after the raw_input into a separate function, which you connect as a handler for a got_input signal.
In the original function, where you used to call raw_input, you instead emit a gimme_input signal.
Write a handler for that gimme_input signal to run in the main thread, which will put up the GUI widgets (as with the single-threaded example above).
Write a handler for the OK button that emits the got_input signal back to the worker thread.

Force update GUI in kivy

I am writing an app in kivy which does cpu-heavy calculations at launch. I want the app to display what it's doing at the moment along with the progress, however, since the main loop is not reached yet, it just displays empty white screen until it finishes working. Can I force kivy to update the interface?
Basically I'm looking for kivy's equivalent of Tkinter's root.update()
I could create a workaround by defining a series of functions with each calling the next one through Clock.schedule_once(nextFunction, 1), but that would be very sloppy.
Thanks in advance.
Leaving aside the question of whether you should be using threading or something instead (which possibly you should), the answer is just that you should move your cpu calculations to somewhere else. Display something simple initially (i.e. returning a simple widget from your build method), then do the calculations after that, such as by clock scheduling them.
Your calculations will still block the gui in this case. You can work around this by doing them in a thread or by manually breaking them up into small pieces that can be sequentially scheduled.
It might be possible to update the gui by manually calling something like Clock.tick(), but I'm not sure if this will work right, and even if so it won't be able to display graphics before they have been initialised.

Categories

Resources