Optimizing your PyQt applications - python

For those of you who have written fairly complex PyQt applications, what tips and tricks would you offer for speeding up your applications? I have a few examples of where my program begins to slow down as it grows larger:
I have a 'dashboard' written that is destroyed and re-created when a user clicks on an item in a TreeWidget. What would be a better way to have a modular interface where clicking an item in the TreeWidget changes the dashboard, but doesn't require destroying a widget and recreating it.
Each dashboard also loads an image from a network location. This creates some slowdown as one navigates around the application, but after it's loaded into memory, 'going back to that same dash' is faster. Is there a good method or way to run a thread on program load that maybe pre-loads the images into memory? If so, how do you implement that?
When you have a large variety of dashboard items and data that gets loaded into them, do you guys normally thread the data load and load it back in which each thread completes? Is this viable when somebody is browsing around quickly? Would implementing a kill-switch for the threads such that when a user changes dashboards, the threads die work? Or would the constant creation and killing of threads cause some sort of, well, meltdown.
Sorry for the huge barrage of questions, but they seemed similar enough to warrant bundling them together.

I'm not sure if this is exactly the same thing you are doing, but it sounds similar to something I have in some apps, where there is some list of custom widget. And it does significantly slow down when you are creating and destroying tons of widgets.
If its an issue of lesser amounts of total widgets, but just being created and deleted a lot, you can just create the widgets once, and only change the data of those widgets as information needs to be updated... as opposed to creating new widgets each time the information changes. That way you can even change the data from threads without having to worry about creating widgets.
Another situation is where you are displaying a list with custom widgets and there are a TON of results. I notice it always slows down when you have 1000's of custom widgets in a list. A trick my co-worker came up with was to have a fake kind of list where it is using a static number of slots in the display. Say, it shows 10 slots in the view. The scrollbar doesn't really scroll down across MORE widget...what it does is scrolls the DATA through the 10 visible widgets. You can a crazy performance increase doing that. But only if it is an acceptable display style for your application.

Are you using QNetworkAccessManager to load you images? It has cache support. Also it loads everything in background with finishing callbacks.
I don't really understand what your dashboard is doing. Have you think about using QWebkit? Maybe your dashboard content is easy to be implemented in HTML?
PS. I don't like threads in Python and don't think they are good idea. Deferred jobs delegated to Qt core is better.

Related

Python TKinter for real time GUIs

I have written a monitoring program for the control system at our plant. It is basically a GUI which lets the operator see the current status of the lock of the closed loop system and aware the operator in case the lock/loop breaks.
Now, the operation is heavily dependent on the responses of the GUI. My seniors told me that they prefer just the console prints instead of using TKinter based GUI as TKinter has lags while working in real time.
Can anyone please comment on this aspect?
Can this lag be checked and corrected?
Thanks in advance.
I would say that if your program is simply accessing data and not interacting with the data, then a GUI seems to be a bit of overkill. GUI's are guided user interfaces, as you know, and are made for guiding a user through an interface. If the interface is just a status, as you indicated, then I see nothing wrong with a console program.
If, however, your program also interacts with data in a way that would be difficult without a GUI, then the GUI is likely the right choice.
Have you considered a GUI in another programming language? Python is known to be a bit slow, even in console. In my experience, C++ is faster in terms of viewing data. Best of luck!
Python / tkinter in general
In a tkinter program, your code falls in one of four categories;
Initialization code that runs before the mainloop is started.
Callbacks that are run from within the mainloop.
Code running in other threads.
Code running in different processes.
In the first case, the time the code takes only influences the startup time, which for a long-running program is probably not all that relevant.
Concerning the second case, well-written callbacks should not take that long to run. In the order of tens of milliseconds, maybe up to 100 ms. If they take longer, they will render the GUI unresponsive. So unless you notice a sluggish GUI (without threads; see below) this should not be a problem.
One pitfall here are after callbacks, that is functions that will be scheduled to run after a certain time. If you launch them too often, this will also starve the GUI of time.
Another possible problem might be the manipulation of a Canvas with lots and lots of items in it.
As of Python 3.x, tkinter is thread-safe to the best of my understanding. However, in the reference implementation of Python, only one thread at a time can be executing Python bytecode. So doing heavy calculations in a second thread would slow down the GUI.
If you GUI uses multiprocessing to run calculations in another process, that should not influence the speed of your GUI much, unless you do things wrong when communicating with that other process.
Your monitoring program
What is too slow depends on the situation. In general Python is not considered a language suitable for hard real-time programs. To do hard real-time one also needs a suitable operating system.
So the question then becomes what is the acceptable lag in your system specification? Without knowing that it is impossible to precisely answer your question.
It seems that your GUI is just displaying some system status. That should not cause too much of a load, provided that you don't read/check the data too often. As described in the callbacks paragraph above it is possible to starve your GUI of CPU cycles with callbacks that run too often. From what you've written, I gather that the GUI's task is just to inform the human operator.
That leads me to believe that the task is not hugely time critical; a system that requires millisecond intervention time should not rely on a human operator.
So based on your information I would say that a competently written GUI should probably not be too slow.

Should I 'destroy' a liststore (model of treeview) when destroy treeview?

I'm programming python + Gtk3.
I have a Gtk.TreeView with a Gtk.ListStore as model.
At some point of the program I need to destroy the treeview in order to put a fresh one on it's place.
However I don't know what happens with the model. Should I destroy it, clear it, or just leve it there and let python to eat it?
I've also thinked in recycle the same model to the new treeview, but I'd prefer not: too much trouble...
Thanks!
You can leave it to Python's garbage collector, the same way it would go if you'd close the application (it will call g_object_unref on both).
That said, remember that the idea behind the separation of models and views, is that you can mix them the way you like, i.e. display the same model in different views or even alternatively displaying different models in the same view. That you need to replace both may indicate problems in the way you are designing your UI.

Synchronized Qt TreeWidgets

I'm new to Python and PyQt. What is the best way to keep 4 QtTreeWidgets synchronized so that the items are the same as well as all the attributes of all the items? These widgets appear in different dialog boxes at different times during a session. For a number of reasons, I need to keep as much of the existing code, signals and layout as intact as possible. The Model/View division would be the obvious first place to go, but I don't want to touch any of the methods that are used to access or update the tree. I'm planning to refactor the whole thing in a few months, but I need something quickly to carry me until then.
Since each QTreeWidget is a convenience class, each has its own data. The UI is maintained in Qt Designer and I don't want to keep it that way.
When each dialog is initialized, the tree appears. The application has a singleton class that all dialogs can use to reference its variables/attributes.
In the initialization of each parent dialog, couldn't I check to see if a 'locationTree' attribute exists in the singleton. If not, I would need to populate it with its initial state and have the tree in the dialog use it or a copy of it. Any time the state of the dialog tree is altered in ways that I can trap, I'd like to update the singleton 'locationTree' to mirror the change. Although there's a clone method on a QTreeWidgetItem, I didn't see a corresponding method for the entire QTreeWidget.
How can I accomplish this with the least amount of change to the existing code base and GUI layout?
John
Yes using the MVC facilities is the way to go ...
Even though you are using QTreeWidget you are still working with a class derived from QAbstractItemView therefore the model() and setModel() calls are available. Take a model from one of the widgets that you are creating and then set it in the other widgets. Whenever you change the data in one of the widgets the other widgets will follow suit as they are using the same instance of model.
If you need to maintain the same selection state in all for widgets (which parts of the tree are open or close) that might be a little bit harder but it might actually work by using the same selectionModel selectionModel() and setSelectionModel()
I'm sure you're right that using Model/View is the best approach.
But without an idea of roughly how many items your tree widgets will have, and how frequently they'll be updated, it's hard to weigh up alternative approaches. Also, what version of Qt are you using?
If the number of updates and items are not huge, one approach is to introduce a class that inherits QObject (so it has signals and slots), and make it responsible for keeping all your QTreeWidgets in sync.
By connecting signals and slots for each QTreeWidget to a single other object, you avoid the nightmare of having every tree widget know about every other one.

PyQt: reduce boilerplate for computational module GUI

I have a module for a physical process simulation. Now I want to do a GUI for it. I've used PyQt to create application, which allows to control calculation, its parameters and graphical output. But there is a problem - if I want to add some new feature to simulation, then in addition to coding it in computational module, I need to create wrapper function for calling it in GUI module, GUI element for calling it, set up GUI representation and bind it to the function I need. That's annoying, because I need to write an amount of obvious code instead of working on logic. I have a few ideas on automatization of process, but first I want to ask: are there some recommended ways to reduce this boilerplate work?
Welcome to GUI programming! :)
In PyQt, you can slightly reduce the amount of boilerplate by autoconnecting slots by name.
You can also create any GUI element at runtime, without resorting to QtDesigner, so you can do things like generate a list of checkboxes in a for loop.

How to work with threads in pygtk

I have a problem with threads in pygtk. My application consist of a program that downloads pictures off the internet and then displays it with pygtk. The problem is that in order to do this and keep the GUI responsive, I need to use threads.
So I got into a callback after the user clicked on the button "Download pictures" and I call the method to download the pictures that is within that same class.
thread.start_new_thread(self.images_download, (path,pages)
This won't work. The only way I get my program to get into the thread is by using
gtk.threads_init()
Before starting any thread. Now it downloads the pictures but the GUI remains unresponsive.
I googled this and I tried putting gtk.threads_enter and gtk.threads_leave around the threads but it just doesn't work.
Your question is a bit vague, and without a reference to your actual code it's hard to speculate what you're doing wrong.
So I'll give you some pointers to read, then speculate wildly based on experience.
First of all, you seem to think that you can only keep the GUI responsive by using threads. This is not true. You can also write your code asynchronously, and do everything in a single-threaded application. Twisted is built on this programming model. I recently made a blog post that explains how I created an asynchronous task interface, and example runners both for CLI and GTK+. You can look at those examples to see how tasks can be implemented asynchronously, and the UI still gets updated.
Second, if you prefer to use threads for some reason, you will need to understand the GTK+ threading model a little.
You should start by reading The PyGTK FAQ entry on the subject, and you might find this blog post easy to understand too.
Now, on to speculation. I am guessing that you are trying to update your GTK UI from the thread, and not handling the locking properly. If this is the case, you are better off for now deferring all your UI updates you want to do from threads to the main thread by using gobject.idle_add() This way, all UI calls will be made from the main thread. It is an easier mental model to follow in your programming.
Once you feel you really understand the threading and locking models, you could consider updating the UI from your threads, but it's easy to miss a threads_enter()/threads_leave()
You can use gtk.gdk.threads_init() in order to allow any thread modify the UI with the respecting gtk.gdk.threads_enter() and gtk.gdk.theads_leave() lock, but, the problem with this is that doesn't work well on windows. I have tested it on Linux and performs quite well, but I had no luck making this to work over win32.
=== Edit ===
I have been browsing about this, you could make use of the gobject.io_add_watch to check if there is something in your socket, grab it and then update the GUI. check my post about this:
Sockets (and some other files) and PyGTK without threads.

Categories

Resources