Installation¶
Before we start, ensure you have PyQt5 installed. You can install it via pip:
pip install PyQt6
Creating a Simple Window¶
Let's start by creating a simple window. Create a new Python file (main.py
for example) and add the following code:
import sys
from PyQt6.QtWidgets import QApplication,QWidget
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("Test Demo")
window.setGeometry(100,100,300,200) # (x,y,width,height)
window.show()
sys.exit(app.exec())
app = QApplication(sys.argv)
: We create an instance of QApplication.sys.argv
is a list of command-line arguments.window = QWidget()
: We create a QWidget instance, which represents our application's main window.sys.exit(app.exec())
: Start the application's event loop and exit gracefully when it's done.
PyQt6 Signals, Slots & Events¶
Signals & Slots¶
Signals
are notifications emitted by widgets when something happens.
- That something can be any number of things, from
pressing a button
, to thetext of an input box changing
, to thetext of the window changing
. - Many signals are initiated by user action, but this is not a rule.
Slots
is the name Qt uses for the receivers of signals
.
In Python, any
function
(ormethod
) in your application can be used as aslot
-- simply by connecting thesignal
to it.If the
signal
sends data, then the receiving function will receive that data too.
Let's take a look at the basics of Qt signals and how you can use them to hook widgets up to make things happen in your apps.
QPushButton
Signals¶
We create a simple custom slot
named the_button_was_clicked
which accepts the clicked signal
from the QPushButton
.
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_clicked)
# Set the central widget of the Window.
self.setCentralWidget(button)
def the_button_was_clicked(self):
print("Clicked!")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
We've heard already that signals can also send data to provide more information about what has just happened.
The .clicked
signal is no exception, also providing a checked (or toggled) state for the button.
For normal buttons this is always False
, so our first slot ignored this data. However, we can make our button checkable
and see the effect.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.button_is_checked = True
self.setWindowTitle("My App")
button = QPushButton("Press Me!")
button.setCheckable(True)
button.clicked.connect(self.the_button_was_toggled)
button.setChecked(self.button_is_checked)
self.setCentralWidget(button)
def the_button_was_toggled(self, checked):
self.button_is_checked = checked
print(self.button_is_checked)
Connecting widgets together directly [Not suggested]¶
So far we've seen examples of connecting widget signals
to Python methods
.
When a signal
is fired from the widget, our Python method
is called and receives the data from the signal
.
But you don't always need to use a Python function
to handle signals
-- you can also connect Qt widgets
directly to one another.
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QVBoxLayout, QWidget
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("My App")
self.label = QLabel()
self.input = QLineEdit()
self.input.textChanged.connect(self.label.setText)
layout = QVBoxLayout()
layout.addWidget(self.input)
layout.addWidget(self.label)
container = QWidget()
container.setLayout(layout)
# Set the central widget of the Window.
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Events¶
Every interaction the user has with a Qt application is an event
. Qt represents these events using event
objects which package up information about what happened.
One of the main events which widgets receive is the QMouseEvent
.
QMouseEvent
events are created for each and every mouse movement and button click on a widget. The following event handlers are available for handling mouse events
Method | Returns |
---|---|
Event handler |
Event type moved |
mouseMoveEvent |
Mouse moved |
mousePressEvent |
Mouse button pressed |
mouseReleaseEvent |
Mouse button released |
mouseDoubleClickEvent |
Double click detected |
All
mouse events
in Qt are tracked with theQMouseEvent
object, with information about the event being readable from the following event methods.Method Returns .button()
Specific button that triggered this event .buttons()
State of all mouse buttons (OR'ed flags) .position()
Widget-relative position as a QPoint
integer
Context Menus¶
Context menus
are small context-sensitive menus which typically appear when right clicking
on a window.
Qt has support for generating these menus using the .contextMenuEvent
function from a QMainWindow
.
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow, QMenu
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
def contextMenuEvent(self, e):
context = QMenu(self)
context.addAction(QAction("test 1", self))
context.addAction(QAction("test 2", self))
context.addAction(QAction("test 3", self))
context.exec(e.globalPos())
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Just for completeness, there is actually a signal
-based approach to creating context menus
. It's exactly same as follows:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.show()
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self.on_context_menu)
def on_context_menu(self, pos):
context = QMenu(self)
context.addAction(QAction("test 1", self))
context.addAction(QAction("test 2", self))
context.addAction(QAction("test 3", self))
context.exec(self.mapToGlobal(pos))
A comprehensive Example¶
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QWidget, QMainWindow, QPushButton, QLabel, QGridLayout, QApplication, QLineEdit, QComboBox, QMenu
from PyQt6.QtGui import QIcon, QAction
class Window(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.clicked_times = 0
self.setMinimumSize(300,300)
grid_layout = QGridLayout()
self.label = QLabel("This is a label", alignment=Qt.AlignmentFlag.AlignCenter)
self.btn = QPushButton("Click Me!")
self.lineEdit = QLineEdit()
self.lineEdit.setPlaceholderText("Please Enter your last name")
self.comboBox = QComboBox()
self.comboBox.addItem("Red")
self.comboBox.addItem("Green")
self.comboBox.addItem("Yellow")
self.comboBox.addItem(QIcon("./asset/utd.jpg"), "UTD")
self.btn.clicked.connect(self.clickHandler)
self.lineEdit.textChanged.connect(self.textChangeHandler)
self.lineEdit.setMaxLength(10)
self.comboBox.currentTextChanged.connect(self.comboChange)
grid_layout.addWidget(self.label, 0, 0)
grid_layout.addWidget(self.comboBox, 1, 0)
grid_layout.addWidget(self.lineEdit, 2, 0)
grid_layout.addWidget(self.btn, 3, 0)
centerWidget = QWidget()
centerWidget.setLayout(grid_layout)
self.setCentralWidget(centerWidget)
def clickHandler(self):
print("Button clicked!")
self.clicked_times += 1
self.label.setText(f"Button has Been Clicked {self.clicked_times} times!")
def textChangeHandler(self):
self.label.setText(f"Your last name is {self.lineEdit.text()}")
def comboChange(self):
self.label.setText(f"Current Combo Box text is {self.comboBox.currentText()}")
def mousePressEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
# handle the left-button press in here
self.label.setText("mousePressEvent LEFT")
elif e.button() == Qt.MouseButton.MiddleButton:
# handle the middle-button press in here.
self.label.setText("mousePressEvent MIDDLE")
elif e.button() == Qt.MouseButton.RightButton:
# handle the right-button press in here.
self.label.setText("mousePressEvent RIGHT")
def mouseReleaseEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
self.label.setText("mouseReleaseEvent LEFT")
elif e.button() == Qt.MouseButton.MiddleButton:
self.label.setText("mouseReleaseEvent MIDDLE")
elif e.button() == Qt.MouseButton.RightButton:
self.label.setText("mouseReleaseEvent RIGHT")
def mouseDoubleClickEvent(self, e):
if e.button() == Qt.MouseButton.LeftButton:
self.label.setText("mouseDoubleClickEvent LEFT")
elif e.button() == Qt.MouseButton.MiddleButton:
self.label.setText("mouseDoubleClickEvent MIDDLE")
elif e.button() == Qt.MouseButton.RightButton:
self.label.setText("mouseDoubleClickEvent RIGHT")
def contextMenuEvent(self, e):
context = QMenu(self)
action1 = QAction("test 1", self)
action2 = QAction("test 2", self)
action3 = QAction("test 3", self)
context.addAction(action1)
context.addAction(action2)
context.addAction(action3)
action1.triggered.connect(lambda: self.setLabelText("test 1 from context menu"))
action2.triggered.connect(lambda: self.setLabelText("test 2 from context menu"))
action3.triggered.connect(lambda: self.setLabelText("test 3 from context menu"))
context.exec(e.globalPos())
def setLabelText(self, text):
self.label.setText(text)
app = QApplication([])
window = Window()
window.show()
app.exec()