I'm learning Pyside and I can't seem to get text from a QLineEdit into my own method so that I can input it into a query etc. I know it has to do with lineEdit.text(), but it isn't seeming to work. Do I need to associate it with a signal before the text will go into my variable??
This is the type of thing I've been trying. Do I need a textChanged signal to get it to update or something?? I've tried adding self.line , but that didn't work either, a little rusty on object oriented programming.
line=QtGui.QLineEdit(self)
myVar = line.text()
A short code example would be great. Thanks!
You seem to be creating the object and using it right afterwards. Of course, you get an empty string from text(); it doesn't work like that.
You should add the QLineEdit to a GUI, let the user do something with it and then obtain the text with QLineEdit.text(). To know when exactly the user changed the text, yes, you should connect to the QLineEdit.textEdited slot.
Here is a full example that uses such a mechanism to copy all the text from a QLineEdit to a QLabel as soon as it's modified.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QVBoxLayout()
self.setLayout(layout)
self.line_edit = QLineEdit()
layout.addWidget(self.line_edit)
self.label = QLabel()
layout.addWidget(self.label)
self.line_edit.textChanged.connect(self.line_edit_text_changed)
self.show()
def line_edit_text_changed(self, text):
self.label.setText(text)
app = QApplication(sys.argv)
mw = MainWindow()
app.exec_()
This is example shows how you can connect your own function to a slot. But since a QLabel has a setText slot, we could just do self.line_edit.textChanged.connect(self.line_edit.setText) and not define a function.
P.S. You really should read some tutorial; I found this one very useful.
Related
I am building a graphical interface for an application using PySide2. My main window is a QMainWindow and I am trying to open a pop-up window, which is a QDialog, whenever a specific action is performed on the main window.
The pop-up opens perfectly fine. However, after it is open, the main window is no longer responsive. I believe the problem is that my application is overwriting the main window with the popup window.
The error message whenever I try to change the main window's stackedWidget index is:
AttributeError: 'Ui_popupHideSuccess' object has no attribute 'stackedWidget'
The code I am using to open the main window is the following:
if __name__ == '__main__':
app = QApplication(sys.argv)
myWindow = MainWindow()
myWindow.show()
sys.exit(app.exec_())
And the code I am using to open the pop-up window is the following:
def showPopupSuccessHide(self):
self.window = QDialog()
self.ui = Ui_popupHideSuccess()
self.ui.setupUi(self.window)
self.window.show()
The code for the windows themselves are on other files (as I am using QtDesigner for developing them). I believe it to be unnecessary for resolving this issue, but I can provide it if needed. What am I doing wrong here? I need to open pop-ups and still interact with the main window after.
I have no idea how to actually resolve this. I believe my error to be in the code I am using to open the pop-up window, but I'm not sure how to tweak it for it to work properly.
TL;DR
Do not overwrite self.ui.
Explanation
How uic composition works
One of the common ways of properly using pyuic generated files is to use composition (as opposed to multiple inheritance):
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
from ui_mainWindow import Ui_MainWindow
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.myLineEdit.setText('some text')
This is perfectly fine, and makes sense: the concept is that an instance of the pyuic class (sometimes called "form class") is created and then the actual window is "set up" using that instance, with the self.ui object containing references to all widgets.
Note that making the ui persistent (using an instance attribute) is actually not a strict requirement, but it is usually necessary in order to be able to directly access the widgets, which is normally important to create signal connections or read properties.
But, if that's not required, it will work anyway: the widgets are automatically "reparented" to the main window (or their direct parents), and the garbage collection is not an issue as Qt will keep its own references internally (in Qt terms, "the window takes ownership").
Technically speaking, this is completely valid:
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
Ui_MainWindow().setupUi(self)
Then, we can still access the widgets using findChild and their object names (those set in Designer):
self.findChild(QLineEdit, 'myLineEdit').setText('some text')
Obviously, it is not very practical.
Creating "child" windows
When there is the need to create a child window (usually, a dialog), it's normally suggested to use an instance attribute to avoid garbage collection:
def createWindow(self):
self.window = QDialog()
self.window.show()
If that dialog also has a Designer file, we need to do something similar to what done at the beginning. Unfortunately, a very common mistake is to create the ui instance by using the same name:
def createWindow(self):
self.window = QDialog()
self.ui = Ui_Dialog()
self.ui.setupUi(self.window)
self.ui.anotherLineEdit.setText('another text')
self.window.show()
This is theoretically fine: all works as expected. But there's a huge problem: the above overwrites self.ui, meaning that we lose all references to the widgets of the main window.
Suppose that you want to set the text of the line edit in the dialog based on the text written in that of the main window; the following will probably crash:
def createWindow(self):
self.window = QDialog()
self.ui = Ui_Dialog()
self.ui.setupUi(self.window)
self.ui.anotherLineEdit.setText(self.ui.myLineEdit.text())
self.window.show()
This clearly shows an important aspect: it's mandatory to always think before assigning attributes that may already exist.
In the code here above, this was actually done twice: not only we overwrote the self.ui we created before, but we also did it for window(), which is an existing function of all Qt widgets (it returns the top level ancestor window of the widget on which it was called).
As a rule of thumb, always take your time to decide how to name objects, use smart names, and consider that most common names are probably already taken: remember to check the "List of all members, including inherited members" link in the documentation of the widget type you're using, until you're experienced enough to remember them.
Solutions
The obvious solution is to use a different name for the ui of the dialog:
def createWindow(self):
self.dialog = QDialog()
self.dialog_ui = Ui_Dialog()
self.dialog_ui.setupUi(self.dialog)
self.dialog_ui.anotherLineEdit.setText(self.ui.myLineEdit.text())
self.dialog.show()
A better solution is to create a subclass for your dialog instead:
class MyDialog(QDialog):
def __init__(self, parent=None)
super().__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
class MyWindow(QMainWindow):
# ...
def createWindow(self):
self.dialog = MyDialog()
self.dialog.ui.anotherLineEdit.setText(self.ui.myLineEdit.text())
self.dialog.show()
Also remember that another common (and, to my experience, simpler and more intuitive) method is to use multiple inheritance instead of composition:
class MyDialog(QDialog, Ui_Dialog):
def __init__(self, parent=None)
super().__init__(parent)
self.setupUi(self)
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.myLineEdit.setText('some text')
def createWindow(self):
self.dialog = MyDialog()
self.dialog.anotherLineEdit.setText(self.myLineEdit.text())
self.dialog.show()
The only issue of this approach is that it may inadvertently overwrite names of functions of the "main" widget: for instance, if you created a child widget in Designer and renamed it "window". As said above, if you always think thoroughly about the names you assign to objects, this will probably never happen (it doesn't make a lot of sense to name a widget "window").
A long time ago, I wanted to make a logo appear on top of the text in a QPushButton stacked on top of each other, but I couldn't find anyway
I read some stylesheets (couldn't find a single doc to read it all about all styles I can apply to a button)
tried the setLayoutDirection (RightToLeft and LeftToRight were there, but no UpToDown direction)
In my (I wish) last attempt I tried to inherit a QAbstractButton (I didn't find QAbstractPushButton, so I guess QAbstractButton is the answer) and change its paintEvent/paintEngine to draw an image or maybe add a vbox inside it as a layout to draw to components, but I can't find anything in python (specially PySide) which has an example in any possible way close to that. The best thing I found was the analogue clock example which was not very helpful because it was trying to work a QWidget and not a QAbstractButton and I want to keep the feel of a Native looking button.
I like my final product to be something like this.
source of the implemention of that
Python Enaml toolkit supported this feature out of the box (in one of its widgets), and I know it is QT based, so I really wish to know how it is possible?
p.s.: Also, is there a market for qt widgets? e.g.: a plugin system. Because rewriting an android like switch doesn't seem like the correct thing that I should do! even a good tutorial or doc would be appreicated (excluding official doc)
It is easier than you think, you can use QToolButton() like this:
import sys
from PySide6.QtCore import Qt, QSize
from PySide6.QtWidgets import QApplication, QVBoxLayout,QStyle, QWidget,
QToolButton
class Window(QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
button = QToolButton()
# here you choose the position of the icon and its text
button.setToolButtonStyle(
Qt.ToolButtonStyle.ToolButtonTextUnderIcon)
# here I just use built-in icon by PySide6 for this example
name = 'SP_DialogSaveButton'
pixmapi = getattr(QStyle, name)
icon = self.style().standardIcon(pixmapi)
# here we set text and icon of size 32x32 to the button
button.setIcon(icon)
button.setText("Sample text")
button.setIconSize(QSize(32, 32))
# finally we add our button to the layout
lay = QVBoxLayout(self)
lay.addWidget(button, alignment=Qt.AlignCenter)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())
I have been trying to get a QComboBox in PyQt5 to become populated from a database table. The problem is trying to find a method that recognizes a click event on it.
In my GUI, my combo-box is initially empty, but upon clicking on it I wish for the click event to activate my method for communicating to the database and populating the drop-down list. It seems so far that there is no built-in event handler for a click-event for the combo-box. I am hoping that I am wrong on this. I hope someone will be able to tell me that there is a way to do this.
The best article I could find on my use-case here is from this link referring to PyQt4 QComboBox:
dropdown event/callback in combo-box in pyqt4
I also found another link that contains a nice image of a QComboBox.
The first element seems to be a label followed by a list:
Catch mouse button pressed signal from QComboBox popup menu
You can override the showPopup method to achieve this, which will work no matter how the drop-down list is opened (i.e. via the mouse, keyboard, or shortcuts):
from PyQt5 import QtCore, QtWidgets
class ComboBox(QtWidgets.QComboBox):
popupAboutToBeShown = QtCore.pyqtSignal()
def showPopup(self):
self.popupAboutToBeShown.emit()
super(ComboBox, self).showPopup()
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.combo = ComboBox(self)
self.combo.popupAboutToBeShown.connect(self.populateConbo)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.combo)
def populateConbo(self):
if not self.combo.count():
self.combo.addItems('One Two Three Four'.split())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
However, for your particular use-case, I think a better solution might be to set a QSqlQueryModel on the combo-box, so that the items are updated from the database automatically.
Alternative Solution I :
We can use frame click, the code is to be used in the container of the combo box (windows/dialog/etc.)
def mousePressEvent(self, event):
print("Hello world !")
or
def mousePressEvent():
print("Hello world !")
Alternative Solution II :
We could connect a handler to the pressed signal of the combo's view
self.uiComboBox.view().pressed.connect(self.handleItemPressed)
...
def handleItemPressed(self, index):
item = self.uiComboBox.model().itemFromIndex(index)
print("Do something with the selected item")
Why would you want to populate it when it's activated rather than when the window is loaded?
I am currently developing an application with PySide (another Python binding for the Qt framework), and I populate my comboboxes in the mainwindow class __init__ function, which seems to be the way to go, judging by many examples.
Look at the example code under "QCombobox" over at Zetcode.
Let's assume I want to create a dialog box, a child of my main program:
from PyQt4 import QtGui, QtCore
class WizardJournal(QtGui.QDialog):
def __init__(self, parent):
super(WizardJournal, self).__init__(parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.wizard = QtGui.QWidget()
self.ok_button = QtGui.QPushButton("OK", self)
self.vbox_global = QtGui.QVBoxLayout(self)
self.vbox_global.addWidget(self.ok_button)
self.paret.wizard.setLayout(self.vbox_global)
self.parent.wizard.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
parent = QtGui.QWidget()
obj = WizardJournal(parent)
sys.exit(app.exec_())
This dialog box will be opened and closed by my main program. What is better regarding memory consumption:
self.ok_button = QtGui.QPushButton("OK", self)
self.ok_button = QtGui.QPushButton("OK")
Basically, I would like to know if I should mention the parent widget when I create a widget. When I will close this dialog box, will the OK button be released from memory if I didn't mention the parent widget when I created it ?
Given the way your example is currently structured, neither the dialog nor any of its child widgets will be deleted when it is closed.
You can see this by changing the end of the example to look like this:
app.exec_()
print('\n'.join(repr(w) for w in app.allWidgets()))
which will give output like this (once the dialog is closed):
<__main__.WizardJournal object at 0x7fcd850f65e8>
<PyQt4.QtGui.QPushButton object at 0x7fcd850f6708>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6558>
<PyQt4.QtGui.QDesktopWidget object at 0x7fcd850f6828>
<PyQt4.QtGui.QWidget object at 0x7fcd850f6678>
In PyQt, you have to be aware that there may be two kinds of reference held for a object: one on the Python side (the PyQt wrapper object) and one on the C++ side (the underlying Qt object). So to fully delete an object, you need to remove all of these references.
In general, Qt does not delete objects unless you explictly tell it to do so. This is something you need to be aware of when creating dialogs with a parent, because it is very easy to produce a memory leak otherwise. It is common to see code written like this:
def openDialog(self):
dialog = MyDialog(self)
dialog.show()
Which looks harmless at first glance - but the method will create a new dialog every time it is called, and Qt will end up holding on to every single one of them (because of the parent reference on the C++ side). One way to avoid this is to re-write the method so that it only keeps a reference on the Python side:
def openDialog(self):
self.dialog = MyDialog()
self.dialog.show()
But what to do about a modal dialog, which must have a parent? In that case, you could initialise the dialog class like this:
class MyDialog(QtGui.QDialog):
def __init__(self, parent):
super(MyDialog, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
And now Qt will automatically delete the dialog when it is closed, and also recursively delete all of its child objects as well. This will just leave behind an empty PyQt wrapper object, which will (eventually) be removed by the Python garbage-collector.
So for your particular example, I think I would re-write it to look something like this:
import sys
from PyQt4 import QtGui, QtCore
class WizardJournal(QtGui.QDialog):
def __init__(self, parent):
super(WizardJournal, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.initUI()
def initUI(self):
self.ok_button = QtGui.QPushButton("OK", self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.ok_button)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
parent = QtGui.QWidget()
obj = WizardJournal(parent)
app.exec_()
print('\n'.join(repr(w) for w in app.allWidgets()))
The dialog class is now completely self-contained, and there is only one external python reference to the instance of it. (If you need to access the parent widget from within the dialog class, you can use self.parent()).
PS: when widgets are added to a layout, they will be automatically re-parented to whatever top-level widget eventually contains the layout. So, strictly speaking, it is not necessary to explicitly set a parent for such widgets in your code.
Firstly, I'm new to Python, Qt and PySide so forgive me if this question seems too simple.
What I'm trying to do is to display a bunch of photos in a grid in a GUI constructed using PySide API. Further, when a user clicks on a photo, I want to be able to display the information corresponding to that photo. Additionally, I would like the container/widget used for displaying the photo to allow for the photo to be changed e.g. I should be able to replace any photo in the grid without causing the entire grid of photos to be created from scratch again.
Initially I tried to use QLabel to display a QPixmap but I realized (whether mistakenly or not) that I have no way to detect mouse clicks on the label. After some searching, I got the impression that I should subclass QLabel (or some other relevant class) and somehow override QWidget's(QLabel's parent class) mousePressEvent() to enable mouse click detection. Problem is I'm not sure how to do that or whether there is any alternative widget I can use to contain my photos other than the QLabel without having to go through subclass customization.
Can anyone suggest a more suitable container other than QLabel to display photos while allowing me to detect mouse clicks on the photo or provide some code snippet for subclassing QLabel to enable it to detect mouse clicks?
Thanks in advance for any replies.
I've added an example of how to emit a signal and connect to another slot. Also the docs are very helpful
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
picture = PictureLabel("pic.png", self)
picture.pictureClicked.connect(self.anotherSlot)
layout.addWidget(picture)
layout.addWidget(QLabel("click on the picture"))
def anotherSlot(self, passed):
print passed
print "now I'm in Main.anotherSlot"
class PictureLabel(QLabel):
pictureClicked = Signal(str) # can be other types (list, dict, object...)
def __init__(self, image, parent=None):
super(PictureLabel, self).__init__(parent)
self.setPixmap(image)
def mousePressEvent(self, event):
print "from PictureLabel.mousePressEvent"
self.pictureClicked.emit("emit the signal")
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
Even if the question has been answered, i want to provide an other way that can be used in different situations (see below) :
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
picture = QLabel()
picture.setPixmap("pic.png")
layout.addWidget(picture)
layout.addWidget(QLabel("click on the picture"))
makeClickable(picture)
QObject.connect(picture, SIGNAL("clicked()"), self.anotherSlot)
def anotherSlot(self):
print("AnotherSlot has been called")
def makeClickable(widget):
def SendClickSignal(widget, evnt):
widget.emit(SIGNAL('clicked()'))
widget.mousePressEvent = lambda evnt: SendClickSignal(widget, evnt)
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
This way doesn't imply subclassing QLabel so it can be used to add logic to a widget made with QtDeigner.
Pros :
Can be used over QTdesigner compiled files
Can be applied to any kind of widget (you might need to include a super call to the overrided function to ensure widget's normal behavior)
The same logic can be used to send other signals
Cons :
You have to use the QObject syntax to connect signals and slots