PyQt5in Python - Use a QLabel like a button [duplicate] - python

I have a Qlabel filled with QPixmap and I want to start a process/function once this label clicked. I had extended QLabel class as follows:
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
class QLabel_alterada(QLabel):
clicked=pyqtSignal()
def __init(self, parent):
QLabel.__init__(self, QMouseEvent)
def mousePressEvent(self, ev):
self.clicked.emit()
Then, in my pyuic5-based .py file (I used QtDesigner to do the layout) after importing the module where I save the extended QLabel class, inside the automatically generated setupui, function I changed my Label from
self.label1=QtWidgets.QLabel(self.centralwidget)
to
self.label1 = QLABEL2.QLabel_alterada(self.centralwidget)
Finally, in the core app Python file where I put all the procedures/classes whetever needed to the application functionality I added
self.ui.label1.clicked.connect(self.dosomestuff)
The application does not crashes but the labels still not clickable. Can someone give me some help on this?

I do not understand why you pass QMouseEvent to the parent constructor, you must pass the parent attribute as shown below:
class QLabel_alterada(QLabel):
clicked=pyqtSignal()
def mousePressEvent(self, ev):
self.clicked.emit()
To avoid having problems with imports we can directly promote the widget as shown below:
We place a QLabel and right click and choose Promote to ...:
We get the following dialog and place the QLABEL2.h in header file and QLabel_changed in Promoted class Name, then press Add and Promote
Then we generate the .ui file with the help of pyuic. Obtaining the following structure:
├── main.py
├── QLABEL2.py
└── Ui_main.ui
Obtaining the following structure:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.label.clicked.connect(self.dosomestuff)
def dosomestuff(self):
print("click")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Since Python can pass function as object, I think make a class inherit QLabel only to make it clickable is overkill. Instead I do like this:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QFrame, QLabel
def foo(*arg, **kwargs):
print("Bar")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
Frame = QFrame()
label = QLabel(Frame)
label.mousePressEvent = foo
label.setText("Label")
Frame.show()
sys.exit(app.exec_())

class ClickableLabel(QtWidgets.QLabel):
def __init__(self, whenClicked, parent=None):
QtWidgets.QLabel.__init__(self, parent)
self._whenClicked = whenClicked
def mouseReleaseEvent(self, event):
self._whenClicked(event)
and then:
my_label = ClickableLabel(self.my_label_clicked)
...
def my_label_clicked(self, event):
button = event.button()
modifiers = event.modifiers()
if modifiers == Qt.NoModifier and button == Qt.LeftButton:
logger.debug('my_label_clicked: hooray!')
return
LOGGER.debug('my_label_clicked: unhandled %r', event)

In PyQT5, you can try to add code in setupUi and create clicked method:
def setupUi()
...
self.label1.linkActivated.connect(self.clicked)
...
def clicked(self):
print(self.label1.text()) #For example, printing the label text...
Hope this helps!

Related

Extending a Widget Loaded from a UI File

I'd like to be able to load a widget (QMainWindow in this case) from a .ui file, but also be able to extend the class so that I can do things like catch keyboard events.
For example, I have the following that doesn't work but shows what I'm trying to accomplish:
class MyMainWindow(QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
self.window = QUiLoader().load("mainwindow.ui", self)
self.setCentralWidget(self.window)
self.window.keyPressEvent = self.key_pressed
self.window.setEnabled(True)
def show(self):
self.window.show()
def key_pressed(self, event):
print(event)
Because I cannot extend the object loaded from QUiLoader, I attempted to hijack the keyPressEvent method in that object, and assign it to my own key_pressed method. This doesn't work, but I'm unsure how else to go about capturing keyboard events.
I know I could create MyMainWindow and set it's base class to QMainWindow, and then override the keyPressEvent method, but then I have to do all the layout in code, and I'd much rather leverage the .ui file. How do you go about doing this?
To listen to events from other widgets you must use an eventFilter. Also the initial window is never displayed so it is better to replace it with a QObject.
from PySide2 import QtCore, QtWidgets, QtUiTools
class MyApp(QtCore.QObject):
def __init__(self):
super(MyApp, self).__init__()
self.window = QtUiTools.QUiLoader().load("mainwindow.ui")
self.window.installEventFilter(self)
def show(self):
self.window.show()
def eventFilter(self, obj, event):
if obj is self.window:
if event.type() == QtCore.QEvent.KeyPress:
print(event.key())
return super(MyApp, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MyApp()
w.show()
sys.exit(app.exec_())

How to show the stackwidget?

I'm trying to use the stackWidget to show different widgets .
However when I pressed the item on the listWdget , stackWidget below will not be displayed.
Here is my code,and I don't know where is wrong.
#!/usr/bin/python
# music_1.py
import sys
from PyQt4 import QtGui, QtCore
from music_ui import Ui_Form
class music(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
self.ui.listWidget.insertItem(0,("Warm"))
self.ui.listWidget.insertItem(1,("Funny"))
self.ui.listWidget.insertItem(2,("Terror"))
self.ui.stackedWidget=QtGui.QStackedWidget()
warm=Warm()
funny=Funny()
terror=Terror()
self.ui.stackedWidget.addWidget(warm)
self.ui.stackedWidget.addWidget(funny)
self.ui.stackedWidget.addWidget(terror)
self.connect(self.ui.listWidget,QtCore.SIGNAL("currentRowChanged(int)"),self.ui.stackedWidget,QtCore.SLOT("setCurrentIndex(int)"))
class Warm(QtGui.QWidget):
def __init__(self,parent=None):
super(Warm,self).__init__(parent)
w1=QtGui.QPushButton("w1")
w2=QtGui.QPushButton("w2")
buttonLayout=QtGui.QHBoxLayout()
buttonLayout.addStretch(1)
buttonLayout.addWidget(w1)
buttonLayout.addWidget(w2)
class Funny(QtGui.QWidget):
def __init__(self,parent=None):
super(Funny,self).__init__(parent)
f1=QtGui.QPushButton("f1")
f2=QtGui.QPushButton("f2")
f3=QtGui.QPushButton("f3")
buttonLayout=QtGui.QHBoxLayout()
buttonLayout.addStretch(1)
buttonLayout.addWidget(f1)
buttonLayout.addWidget(f2)
buttonLayout.addWidget(f3)
class Terror(QtGui.QWidget):
def __init__(self,parent=None):
super(Terror,self).__init__(parent)
t1=QtGui.QPushButton("t1")
t2=QtGui.QPushButton("t2")
buttonLayout=QtGui.QHBoxLayout()
buttonLayout.addStretch(1)
buttonLayout.addWidget(t1)
buttonLayout.addWidget(t2)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = music()
myapp.show()
sys.exit(app.exec_())
I never worked with UI Files but this is my guess:
self.ui.stackedWidget=QtGui.QStackedWidget()
here you create a QStackedWidget which gets assigned to the class. The problem is that it is not added to the UI that is getting displayed but just to the instance of the Ui_Form. What you have to do is either:
Add the QStackedWidget in the UI Designer and erase the line above
or
Add the QStackedWidget to one of your layouts
which can be done like this:
self.ui.mySuperCoolLayout.addWidget(self.ui.stackedWidget)

PyQt4 signals and slots - QToolButton

In PyQt4 I have a main window which when the settings button is clicked opens the settings dialog
from PyQt4 import QtCore, QtGui
import ui_Design, ui_Settings_Design
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.settingsBtn.clicked.connect(lambda: self.showSettings())
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
The above works and my SettingsDialog is displayed but I cant get the setPageIndex to work
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(self.setPageIndex)
#QtCore.pyqtSlot()
def setPageIndex(self):
print 'selected'
self.settingsStackedWidget.setCurrentIndex(0)
The bookSettingsBtn is a QToolButton
self.bookSettingsBtn = QtGui.QToolButton(self.navigationFrame)
And the settingsStackedWidget is a QStackedWidget
self.settingsStackedWidget = QtGui.QStackedWidget(SettingsDialog)
At this point I am pretty confused on signals and slots and nothing I have read clears this up - if anyone can point out what I am doing wrong above and also potentially direct me to a good (beginners) guide / tutorial on signals and slots it would be greatly appreciated
I would also like to know how to make setPageIndex work as follows:
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)
I'm not sure why you're doing the following, but that's the issue:
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
SettingsDialog itself is a proper QDialog. You don't need to instantiate another QDialog.
Right now, you're creating an empty QDialog and then populate it with the same ui as SettingsDialog (i.e. setupUi(dialog)), then you show this dialog. But... The signal connection is for SettingsDialog, and the dialog you're showing doesn't have that.
Basically, you don't need that extra QDialog at all. The following should be enough:
def showSettings(self):
dialog = SettingsDialog()
dialog.exec_()
Ok. So here is an example how you pass an argument to a slot
from functools import partial
# here you have a button bookSettingsBtn:
self.bookSettingsBtn = QtGui.QPushButton("settings")
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, self.bookSettingsBtn.text()))
#pyqtSlot(str) # this means the function expects 1 string parameter (str, str) 2 string parameters etc.
def setPageIndex(self, selection):
print "you pressed button " + selection
In your case the argument would be an int. Of course you have to get the value from somewhere
and then put it in the partial part as the argument (here I just used the text of the button),
but you can use int, bool etc. Just watch the slot signature.
Here is a tutorial that helped me:
http://zetcode.com/gui/pyqt4/
I hope this helps.
Hey here I have a fully running example (just copy paste it in a python file and run it):
Maybe this helps you. It's a small example but here you see how it works.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from functools import partial
class MyForm(QMainWindow):
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
button1 = QPushButton('Button 1')
button2 = QPushButton('Button 2')
button1.clicked.connect(partial(self.on_button, button1.text()))
button2.clicked.connect(partial(self.on_button, button1.text()))
layout = QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
#pyqtSlot(str)
def on_button(self, n):
print "Text of button is: " + str(n)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
form = MyForm()
form.show()
app.exec_()
So I dont really understand why but changing the way the settingsDialog is called from the MainWindow has fixed my problem. I guess the parent window needed to be specified??:
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
....
def showSettings(self):
self.settingsDialog = QtGui.QDialog(self)
self.settingsDialog.ui = SettingsDialog(self)
self.settingsDialog.ui.show()
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, 1))
#QtCore.pyqtSlot(int)
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)

How to detect any mouse click on PySide Gui?

I am trying implement a feature such that when a mouse is clicked on the gui, a function is triggered
Below is my mouse click detection, it doesn't work when I click on any part of the gui
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)
layout.addWidget(QLabel("this is the main frame"))
layout.gui_clicked.connect(self.anotherSlot)
def anotherSlot(self, passed):
print passed
print "now I'm in Main.anotherSlot"
class MyLayout(QHBoxLayout):
gui_clicked = Signal(str)
def __init__(self, parent=None):
super(MyLayout, self).__init__(parent)
def mousePressEvent(self, event):
print "Mouse Clicked"
self.gui_clicked.emit("emit the signal")
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
This is my goal
Mouseclick.gui_clicked.connect(do_something)
Any advice would be appreciated
Define mousePressEvent inside Main:
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)
layout.addWidget(QLabel("this is the main frame"))
def mousePressEvent(self, QMouseEvent):
#print mouse position
print QMouseEvent.pos()
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
This can get complicated depending on your needs. In short, the solution is an eventFilter installed on the application. This will listen the whole application for an event. The problem is "event propagation". If a widget doesn't handle an event, it'll be passed to the parent (and so on). You'll see those events multiple times. In your case, for example QLabel doesn't do anything with a mouse press event, therefore the parent (your main window) gets it.
If you actually filter the event (i.e. you don't want the original widget to respond to the event), you won't get that problem. But, I doubt that this is your intent.
A simple example for just monitoring:
import sys
from PySide import QtGui, QtCore
class MouseDetector(QtCore.QObject):
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
print 'mouse pressed', obj
return super(MouseDetector, self).eventFilter(obj, event)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
layout = QtGui.QHBoxLayout()
layout.addWidget(QtGui.QLabel('this is a label'))
layout.addWidget(QtGui.QPushButton('Button'))
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mouseFilter = MouseDetector()
app.installEventFilter(mouseFilter)
main = MainWindow()
main.show()
sys.exit(app.exec_())
You can see that, clicking on the QLabel will give you something like:
mouse pressed <PySide.QtGui.QLabel object at 0x02B92490>
mouse pressed <__main__.MainWindow object at 0x02B92440>
Because, QLabel receives the event and since it doesn't do anything with it, it's ignored and passed to the parent (MainWindow). And it's caught by the filter/monitor again.
Clicking on the QPushButton doesn't have any problem because it uses that event and does not pass to the parent.
PS: Also note that this can cause performance problems since you are inspecting every single event in the application.

Load other windows when button clicked. PyQt

I am trying to call another window from a button click in python 2.7 using PyQt4. The code below opens the AddBooking dialog but immediately closes it. Im new to Gui programming, can somebody please tell me what is wrong with my code?
from PyQt4 import QtGui
from HomeScreen import Ui_HomeScreen
from AddBooking import Ui_AddBooking
import sys
class HomeScreen(QtGui.QWidget, Ui_HomeScreen):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.show()
self.Add_Booking_Button.clicked.connect(self.handleButton)
def handleButton(self):
AddBooking2()
class AddBooking2(QtGui.QWidget, Ui_AddBooking):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = HomeScreen()
window.show()
sys.exit(app.exec_())
Don't use multi-inheritance and neither call show function inside class initializer. The problem is that the object you are creating with AddBooking2() is a temporal and it's destroyed automatically when the function ends. So you need use some variable to reference that object something like:
addbooking = AddBooking2()
addbooking.show()
Also, since you are working with QtDesigner and pyuic4 tools you can make connections a little bit easier. Said that, your code can be modified:
from PyQt4 import QtGui
from PyQt4.QtCore import pyqtSlot
from HomeScreen import Ui_HomeScreen
from AddBooking import Ui_AddBooking
import sys
class HomeScreen(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_HomeScreen()
self.ui.setupUi(self)
#pyqtSlot("")
def on_Add_Booking_Button_clicked(self): # The connection is carried by the Ui_* classes generated by pyuic4
addbooking = AddBooking2()
addbooking.show()
class AddBooking2(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_AddBooking()
self.ui.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = HomeScreen()
window.show()
sys.exit(app.exec_())
The dialog closes immediately because you are not keeping a reference to it, and so it will get garbage-collected as soon as it goes out of scope.
The simplest way to fix it would be to do something like this:
def handleButton(self):
self.dialog = AddBooking2()
self.dialog.show()
and you can also remove the self.show() lines from AddBooking2.__init__ and HomeScreen.__init__, which are redundant. Other than that, your code looks fine.

Categories