I'm not sure if this question has been asked before or not, but I have searched for it a bunch, perhaps I'm using the wrong keywords. I've also googled for it, but I think I'd like a more specific answer.
My question relates to the following line seen in pretty much every PyQt4 class documentation
The parent argument, if not None, causes self to be owned by Qt
instead of PyQt.
Constructs a dialog with parent parent.
A dialog is always a top-level widget, but if it has a parent, its
default location is centered on top of the parent. It will also share
the parent's taskbar entry.
I want to understand exactly what this means. From what I understand it has to do with how the object lives in memory and how it interacts with Python's garbage collection.
To be a little bit more concrete, say I have a QMainWindow and I pass it as the parent to a QDialog. ie somewhere I have a function like this
def ShowFooDialog():
dlg = FooDialog()
if dlg.exec_():
--doSomeStuff--
what would be the difference between that and the following
def ShowFooDialog():
dlg = FooDialog(parent=MyMainWindow)
if dlg.exec_():
--doSomeStuff--
Thanks! If this is something that could have been found out by better reading of the PyQt4 docs I apologize in advance!
Related
I want to understand what the below code means:
class PageOne(tk.Frame):
def __init__(self, parent, controller):
What are self, parent and controller? What is the role and scope of these tools here?
I believe self is similar to this in Java, but what's the use of parent and controller?
Later in the code flow I can see:
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
There is a function already defined called show_frame, but why is the controller being used to call this function?
Roughly speaking, the original code1 attempted to use a pseudo-MVC (model, view and controller) architecture. Though without the "model" part -- there was just a "view" (some frames) and a "controller" (the main application). Hence, the reference to a controller object. The original code was actually written to show how to "stack" frames, so it's MVC implementation is very shallow and under-documented since that wasn't the point of the example.
To answer your specific questions:
self represents the current object. This is a common first parameter for any method of a class. As you suggested, it's similar to Java's this.
parent represents a widget to act as the parent of the current object. All widgets in tkinter except the root window require a parent (sometimes also called a master)
controller represents some other object that is designed to act as a common point of interaction for several pages of widgets. It is an attempt to decouple the pages. That is to say, each page doesn't need to know about the other pages. If it wants to interact with another page, such as causing it to be visible, it can ask the controller to make it visible.
You asked "There is a function already defined called show_frame, but why is the controller being used to call this function?" Notice that show_frame is defined in a separate class, in this case the main program class. It is not defined in the other classes. For the other classes to be able to call it, they must call it on an instance of the main class. That instance is named controller in the context of these other classes.
1 Note: even though you likely found the original code in an online tutorial, it originally came from this stackoverflow answer: Switch between two frames in tkinter
I have just started using and discovering PyQt5 and Qt. I installed it with
pip install pyqt5. Then I opened the designer.exe that comes with pyqt-tools and made my design.
After that I converted it from .ui to .py using pyuic design.ui > dialog.py.
When I run app.py it runs fine and the ui is working fine. But now my problem is: How do I add functions that get called when a button is pressed?
Here is my app.py code:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog)
def __init__(self):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.show()
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
Your approach is actually correct (at least, one of the correct ones): the pyuic generated files should never be modified unless you really (REALLY) know what you're doing and why. Also, their behavior should never be mimicked, as there's no reason to do that.
As a (I'd say, mandatory) reference, you can read more about this topic on the official PyQt guidelines about using Designer.
Let me do an important premise about this topic.
What you're doing is known as the single inheritance approach, meaning that you're creating a subclass that only inherits from the QWidget subclass you're using (QDialog, in this case) and you're building the ui on top of it using what's referred to as the form class. The form class is a standard Python object type that is responsible of creating all child widgets on top of the main subclass instance when its setupUi function is called.
The result is that the subclassed instance will be always referred to self within its scope, while all its children are available through the self.ui object.
A similar (and more common) approach is the multiple inheritance approach. In this way you inherit from both the QWidget subclass (QDialog) and the ui object. The UI result will be the same, but the widgets will be more directly available using self.objectName instead of self.ui.objectName.
Whether you use one or the other is a matter of choice, just remember that whenever you use the multiple inheritance approach the setupUi will potentially overwrite any previously set instance attribute, and if you create a new attribute for an already existing object name, that object will not be (directly) accessible anymore.
Finally, let me give you another suggestion: while not completely wrong, the link posted in the other answer not only gives a very misleading suggestion, but doesn't even dig more in why the previous post it refers to was wrong about. The bottom line is that pyuic created files should never be modified (nor you should try to mimic their behavior) for lots of reasons: not only if you need to change your ui you'll need to merge the already modified code with the new one (hoping that you've not overwritten it using pyuic), but it also leads to misconceptions about the whole object structure.
So, in your case (with the single inheritance approach), all objects created within Designer are actually accessible using self.ui.objectName, and that objectName is what is shown in the object inspector of Designer.
Let's assume that you created a UI with a single push button and you want to print something when that button is pressed. If you didn't do too many tests and modifications to your ui, that button's object name is probably "pushButton".
Then, your code will probably look like this:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog)
def __init__(self):
super().__init__()
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.pushButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print('button clicked!')
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
Note that Qt object names are not unique. You could theoretically set the same object name for more than one QObject (or QWidget), but there's no use in that. Consider that from the python perspective: each variable should have its own name, if you overwrite an already existing variable name, the previous one will become unaccessible (if not even garbage collected).
Designer is smart enough (not that it requires so much...) to prevent creating objects sharing the same object name, and that's why if you add another button, it will be named pushButton_2, pushButton_3, etc. PyQt takes benefit from that by creating unique instance attributes for all object names whether you're using the pyuic file or uic.loadUi().
For the sake of completeness, here's how a multiple inheritance approach would look, while behaving as the example above:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from dialog import Ui_Form
class AppWindow(QDialog, Ui_Form)
def __init__(self):
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print('button clicked!')
app = QApplication(sys.argv)
w = AppWindow()
w.show()
sys.exit(app.exec_())
In light of what explained above, consider the attribute name issue: if you create an attribute named self.pushButton before calling self.setupUi, after that call self.pushButton will be a reference to the push button and the previously set value will be lost; if you create a new attribute named self.pushButton after calling self.setupUi you won't be able to access the QPushButton instance anymore.
Now, while for normal usage they usually behave in the same way, there are small but still important differences between using the pyuic generated files, or uic.loadUiType(), and the more direct uic.loadUi, and unfortunately those differences are not officially documented (and, frankly, I don't remember all of them).
An example has been previously reported by eyllanesc in the question Size of verticalLayout is different in Qt Designer and PyQt program.
Consider that those are very specific exceptions (which might even be addressed in future releases of PyQt, especially now that Qt6 is on the way) and they normally don't create major issues for standard programming.
Finally (at last!), while Designer is usually smart, is not quite perfect. For example, "technically unimportant" objects as QAction separators are considered as "anonymous" and designer doesn't even apply their object names: if you add a separator to a menu, there's no way to directly access it, even if you actively name it.
I would recommend that you don't programm in that converted pythonscript. Just because if you want to change your layout and convert it again you overwrite everything programmed there.
Import this converted ui into a new pythonscript. There are tons of introductions like this (google them)
https://nitratine.net/blog/post/how-to-import-a-pyqt5-ui-file-in-a-python-gui/
Then you should read about slots and signals (you can also add them in the qt designer) or add the functions in pythonscript:
https://www.tutorialspoint.com/pyqt/pyqt_signals_and_slots.htm
its the easiest way to learn about them when following the tutorials ...
I am making a GUI that will have two buttons. I want to perform a sort of Animation on both of those when someone hovers over them.
Now, I know that using Window.bind(mouse_pos=my_mouse_pos_func) is a workaround since no on_mouse_hover is available for button.bind() like on_press and on_release. But that isn't working for me since I cannot specify which widgets in my screen to bind my function when using Window.bind(). Doing this works in a way but only if I get the cursor in my main window.
I have searched for this a lot and in almost 90% of the results I found people recommeding Window.bind(). And the other 10% are workarounds but were really unclear to me, for e.g workarounds like "listening for mouse events" and stuff like that.
Sorry, as I am being able to provide any code as I am quite clueless as to what to show. If anyone knows any simple workarounds to this, their help will be absolutely appreciated
I use the mentioned Window.bind() in the __init__() method of my app as:
Window.bind(mouse_pos=self.on_mouse_pos)
Then in the on_mouse_pos() method, I use the collide_point() method to determine if the mouse is over any of the Buttons in question:
def on_mouse_pos(self, window, pos):
for butt in self.root.ids.butt_box.children:
if butt.collide_point(*pos):
# do something here
You just need a list of the Buttons you want to check (I use the children of a container).
I know it's an older post, but just a follow-up in case someone (like me) has the same problem and finds their way here over Google. #John Anderson's solution works, but it seems easier to me to define a class for those buttons you want to display the desired behaviour and then use Window.bind() in its __init__(), e.g.
from kivy.core.window import Window
from kivy.uix.button import Button
class MyButton(Button):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Window.bind(mouse_pos=self.on_mouseover)
def on_mouseover(self, window, pos):
if self.collide_point(*pos):
# desired behaviour
That way there's no need to keep the list John mentions.
Exactly how do I utilize the various event methods that widgets have? Say I have a comboBox(drop down list) and I want to initiate a function every time someone changes the choice. There is the changeEvent() method in the documentation but It would be great if someone explains to me with a piece of code.
This is a pretty broad question. I recommend checking out the many tutorials on Youtube.com.
However, in your init method, put something like this:
self.ui.charge_codes_combo.currentIndexChanged.connect(self.setup_payments)
In my example, the combo box was placed on a form in Qt Designer. Self.setup_payment is a method triggered by the change in the combo box.
I hope this helps!
I was hoping some one could explain to me the difference and reason behind why I keep coming across two different methods for which people place buttons into the widget? I sometimes see
button = Button('Button', self)
or
self.spinner = QtGui.QSpinBox()
I just want to know whats the difference, is one more beneficial than the other? When to use which in what scenarios and why? Does the position of 'self' affect this in the widget some how?
Assuming you only care about PyQt and PySide…
There are two different places that self appears in your examples, which mean very different things.
First, as an argument to the constructor:
Every widget's constructor has a parent parameter, with a default value of None.
There are three reasons to leave out the parent:
You're going to assign the widget to a layout or other parent after creation.
You're creating a top-level window.
You're creating a special-purpose widget like QDesktopWidget.
If none of these are true, you need to pass a parent. If self is a widget, and the thing you're creating is a child of that widget, you will pass self as the parent.
Here's an example of the first alternative:
self.spinner = QtGui.QSpinBox()
hbox = Qt.QHBoxLayout()
hbox.addWidget(self.spinner)
self.addLayout(hbox)
We can't pass a parent to QSpinBox at construction time because its parent doesn't exist yet. So, we leave it off, and addWidget it to a layout object later.
This is all explained early in most tutorials for PySide or PyQt, like First programs in PySide and Layout management in PySide.
Meanwhile, one of your examples stores the widget in self.spinner, making it an instance attribute on self, which means we can refer to it later. For example, if some other method (like the signal handler for a button) wants to adjust the spinner's value, it can access it as self.spinner.
If you will never need to refer to the child widget in your code after the current function, you don't need to store it.
This part is explained in the Classes chapter in the Python tutorial.