Qt remove a key value from an animation - qt

I'm using Qt's Python binding to develop an application.
I made a nonlinear animation by setting custom key values,
as said in Qt docs:
It is also possible to set values situated between the start and end value. The interpolation will then go by these points.
QPushButton button("Animated Button");
button.show();
QPropertyAnimation animation(&button, "geometry");
animation.setDuration(10000);
animation.setKeyValueAt(0, QRect(0, 0, 100, 30));
animation.setKeyValueAt(0.8, QRect(250, 250, 100, 30));
animation.setKeyValueAt(1, QRect(0, 0, 100, 30));
animation.start();
Goal
I update my animation values and use it in several sections, and I want to make it linear sometimes.
Problem
I can't find a way to remove an animation's set key values in order to make it linear. I tried setting "startValue" "endValue" to my animation but they just replace the default animation key values (0.0 and 1.0) and the custom key value I set before will remain there. Bellow is a sample code:
import sys
from PyQt5.QtCore import QRect, QPoint, QPropertyAnimation, QParallelAnimationGroup
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QFrame
class Form(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.resize(600, 400)
self.setWindowTitle("Form 1")
self.frame = QFrame(self)
self.frame.resize(200, 150)
self.frame.move(20, 20)
self.setStyleSheet("""QFrame {
background-color: orange;
}""")
self.button = QPushButton("Start Animation", self)
self.button.resize(self.button.sizeHint())
self.button.move(20, 300)
self.define_animation()
self.button.clicked.connect(self.frame_anim.start)
self.show()
def define_animation(self):
self.frame_anim = QPropertyAnimation(self.frame, b"geometry")
self.frame_anim.setDuration(1000)
self.frame_anim.setStartValue(self.frame.geometry())
self.frame_anim.setKeyValueAt(0.75, QRect(QPoint(20, 100), self.frame.size()))
self.frame_anim.setKeyValueAt(1, QRect(QPoint(380, 220), self.frame.size()))
self.frame_anim.finished.connect(lambda: print("ََAnimation key values", self.frame_anim.keyValues()))
self.frame_anim.finished.connect(self.define_new_animation)
def define_new_animation(self):
self.frame_anim.setStartValue(QRect(QPoint(380, 220), self.frame.size()))
self.frame_anim.setEndValue(QRect(QPoint(20, 220), self.frame.size()))
app = QApplication(sys.argv)
form = Form()
sys.exit(app.exec_())
The first animation is nonlinear and I set a custom key value, but the next one will keep that key value.
I'm looking for a solution to remove a set key value from an animation or any logical way to make it linear after setting custom key values.

Both setStartValue and setEndValue won't "clear" the current animation, but only set new states for the start and beginnig, while leaving all other nested objects.
In order to update the current animation and reset all its keys, you need to clear the existing mapping by using setKeyValues() with an empty mapping.
This is a possible solution:
class Form(QMainWindow):
# ...
def define_new_animation(self):
self.frame_anim.setKeyValues({})
self.frame_anim.setStartValue(
QRect(QPoint(380, 220), self.frame.size()))
self.frame_anim.setEndValue(
QRect(QPoint(20, 220), self.frame.size()))

Related

Should I use QGraphicsView to display an image and some decorated text side by side?

I want to create a "details" view for books I have downloaded.
With the attached image as an example, imagine the red block to the left is the book's cover page, and metadata related to it is displayed to the right.
With the way I have it done right now:
from PySide6 import QtWidgets as qtw
from PySide6 import QtGui as qtg
from PySide6 import QtCore as qtc
class Details:
def __init__(self):
self.location = "/home/user/Desktop/Untitled.png"
self.title = "Some title"
self.subtitle = "Sub title"
self.id = 123124
def to_html(self):
return """
<p>
<b>Author =</b> author<br/>
<b>Published Date =</b> 2000-1-1<br/>
<b>Pages =</b> 500<br/>
</p>
"""
class DetailsWidget(qtw.QWidget):
_title_font = qtg.QFont()
_title_font.setBold(True)
_title_font.setPixelSize(24)
_subtitle_font = qtg.QFont()
_subtitle_font.setBold(True)
_subtitle_font.setPixelSize(19)
_id_font = qtg.QFont()
_id_font.setBold(True)
_id_font.setPixelSize(15)
_redacted_details_font = qtg.QFont()
_redacted_details_font.setPixelSize(12)
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.setFixedSize(1000, 500)
self.setWindowFlag(qtc.Qt.WindowType.Dialog, True)
self.setLayout(qtw.QGridLayout())
self.layout().setContentsMargins(0, 0, 0, 0)
self._details: Details = Details()
self._thumbnail_image = qtg.QImage(self._details.location)
self._thumbnail_image = self._thumbnail_image.scaled(
500,
500,
qtc.Qt.AspectRatioMode.KeepAspectRatio,
qtc.Qt.TransformationMode.SmoothTransformation,
)
self._details_rect = qtc.QRect(
self._get_actual_geometry().left() + self._thumbnail_image.width() + 10,
self._get_actual_geometry().top(),
self._get_actual_geometry().width() - self._thumbnail_image.width() - 20,
self._get_actual_geometry().height(),
)
height = 0
self._title_rects = []
font_metrics_rect = qtg.QFontMetrics(self._title_font).boundingRect(
self._details_rect, qtc.Qt.TextFlag.TextWordWrap, self._details.title, 0
)
drawing_rect = qtc.QRect(self._details_rect)
self._title_rects.append(drawing_rect)
height += font_metrics_rect.height() + 10
drawing_rect = qtc.QRect(self._details_rect)
drawing_rect.moveTop(height)
self._title_rects.append(drawing_rect)
font_metrics_rect = qtg.QFontMetrics(self._title_font).boundingRect(
self._details_rect, qtc.Qt.TextFlag.TextWordWrap, self._details.subtitle, 0
)
drawing_rect = qtc.QRect(self._details_rect)
height += font_metrics_rect.height() - 3
drawing_rect.moveTop(height)
self._title_rects.append(drawing_rect)
font_metrics_rect = qtg.QFontMetrics(self._title_font).boundingRect(
self._details_rect,
qtc.Qt.TextFlag.TextWordWrap,
str(self._details.id),
0,
)
self._title_rects.append(drawing_rect)
height += font_metrics_rect.height() + 10
self._details_rect.moveTop(height)
self._redacted_details_text_document = qtg.QTextDocument()
self._redacted_details_text_document.setHtml(self._details.to_html())
# First set the width,
self._redacted_details_text_document.setTextWidth(self._details_rect.width())
# then get the height of the QTextDocument based on the given width and set
# that + the titles heights + bottom padding as the total height.
if (total_height:=height + self._redacted_details_text_document.size().height() + 10) > self.height():
self.setFixedHeight(total_height)
def _get_actual_geometry(self) -> qtc.QRect:
# Probably not needed for normal desktop environments with window
# managers but I'm an epik i3 user so self.geometry() does not work as
# intended when full screening the window with $mod + F. Or I'm just
# retarded and this is not even a problem.
geometry = self.geometry()
geometry.setTopLeft(qtc.QPoint(0, 0))
return geometry
def paintEvent(self, event: qtg.QPaintEvent) -> None:
total_height = 0
painter = qtg.QPainter(self)
painter.setRenderHint(qtg.QPainter.RenderHint.TextAntialiasing)
painter.drawImage(0, 0, self._thumbnail_image)
painter.save()
painter.setFont(self._title_font)
painter.drawText(
self._title_rects[0], qtc.Qt.TextFlag.TextWordWrap, self._details.title
)
painter.setFont(self._subtitle_font)
painter.drawText(
self._title_rects[1], qtc.Qt.TextFlag.TextWordWrap, self._details.subtitle
)
painter.setFont(self._id_font)
painter.drawText(
self._title_rects[2],
qtc.Qt.TextFlag.TextWordWrap,
str(self._details.id),
)
painter.translate(self._details_rect.topLeft())
painter.setFont(self._redacted_details_font)
self._redacted_details_text_document.drawContents(painter)
painter.restore()
app = qtw.QApplication()
widget = DetailsWidget()
widget.show()
app.exec()
I can display the text and the image next to each other just fine, but the text is not selectable. Looking around for a way to do so, I stumbled upon QGraphicsTextItem. Should I re-do the whole thing in a QGraphicsView instead of using the paintEvent on a QWidget? The reason I'm hesitant to do so is because I don't know of the cons of using a QGraphicsView, maybe it's a lot more resource heavy and not the best for this use case?
You're complicating things unnecessarily.
Just use a basic QHBoxLayout and two QLabels, with the one on the left for the image, and the one on the right for the details.
If you want to allow text selection, use QLabel.setTextInteractionFlags(Qt.TextSelectableByMouse).
An even better solution would be to use a QGraphicsView with a QGraphicsPixmapItem for the image (using fitInView() in the resizeEvent to always show it as large as possible) and a QTextEdit for the details, set in read only mode.
Note that your usage of _get_actual_geometry is wrong in principle (besides the fact that you're calling 4 times in a row, while you could just use a local variable instead), because when a widget has not been shown yet it always has a default size (100x30 for widgets created with a parent, otherwise 640x480), so not only you'll be getting a wrong geometry, but you're also changing it, since setTopLeft() will only move the corner, not translate the rectangle: if you want the basic rectangle of the widget, just use rect(). Obviously, if you properly use layouts as suggested above, this won't be necessary in the first place.

QDockWidget with QPixmap - how to prevent QPixmap limiting the downsizing of parent widget while maintaining aspect ration?

I haven't worked with images in labels for a long time so I'm stuck with an issue - once resized a QPixmap (loaded inside a QLabel or similar widget) cannot return to a smaller (downsized) version of itself. This is particularly annoying when working with docked widgets in a QMainWindow or similar setting:
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
from random import seed
from random import random
class CentralWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
vb_layout = QVBoxLayout()
self.label = QLabel('Central Widget')
self.label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
vb_layout.addWidget(self.label)
self.setLayout(vb_layout)
class DockedWidget(QDockWidget):
class Widget(QWidget):
def __init__(self):
QWidget.__init__(self)
vb_layout = QVBoxLayout()
self.label = QLabel()
# Enable scaled contents, otherwise enjoy artifacts and visual glitches
self.label.setScaledContents(True)
self.rimg = QImage(self.width(),self.height(), QImage.Format_Grayscale8)
self.rimg.fill(Qt.black)
print(self.rimg.width(), self.rimg.height())
for j in range(self.height()):
for i in range(self.width()):
r = round(random()* 255)
if r % 2 == 0:
self.rimg.setPixel(i, j, qRgb(255, 0, 0))
self.label.setPixmap(QPixmap.fromImage(self.rimg))
vb_layout.addWidget(self.label)
self.setLayout(vb_layout)
def resizeEvent(self, e: QResizeEvent) -> None:
super().resizeEvent(e)
preview = self.label.pixmap()
# FIXME Trying to figure out a way to scale image inside label up and down
self.label.setPixmap(preview.scaled(self.label.width(),self.label.height(),Qt.KeepAspectRatio))
def __init__(self):
QDockWidget.__init__(self)
self.setWindowTitle('Docked Widget')
self.widget = DockedWidget.Widget()
self.setWidget(self.widget)
class MyWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setGeometry(300, 100, 270, 100)
self.setWindowTitle('Test')
dockedwidget = DockedWidget()
self.addDockWidget(Qt.LeftDockWidgetArea, dockedwidget)
widget = CentralWidget()
self.setCentralWidget(widget)
seed(1)
app = QApplication([])
win = MyWindow()
win.show()
app.exec_()
I've tried to link the pixmap's scaling to the parent label, which in terms should be controlled by the behaviour of the docked widget. Initially I was facing the issue that the image would stretch and create weird artifacts:
I figured out I had to enable scaled contents (QLabel.setScaledContents()) but I'm still facing the issue that I cannot go below the initial size of the image:
Minimum size restricts resizing beyond the initially set image size
Increasing the size is not a problem
I need to make the image capable of downsizing properly, otherwise it compromises the rest of the components in the layout in my actual setup. I'm thinking that the solution lies somewhere between the resize event and the size policy.
QLabel does not provide support for aspect ratio, and it normally only allows to scale it to sizes bigger than the image size (but this can be worked around using setMinimumSize(1, 1)).
While you could create a subclass that actually paints the proper aspect ratio no matter of the widget size, it's often not necessary, as you could use a QGraphicsView and a basic pixmap item.
class ImageWidget(QGraphicsView):
def __init__(self):
super().__init__()
self.setFrameShape(0)
self.setStyleSheet('''
QGraphicsView {
background: transparent;
}
''')
rimg = QImage(640, 480, QImage.Format_Grayscale8)
rimg.fill(Qt.black)
for j in range(480):
for i in range(640):
r = round(random()* 255)
if r % 2 == 0:
rimg.setPixel(i, j, qRgb(255, 0, 0))
scene = QGraphicsScene()
self.setScene(scene)
self.pixmapItem = scene.addPixmap(QPixmap.fromImage(rimg))
def resizeEvent(self, event):
super().resizeEvent(event)
self.fitInView(self.pixmapItem, Qt.KeepAspectRatio)
class DockedWidget(QDockWidget):
def __init__(self):
QDockWidget.__init__(self)
self.setWindowTitle('Docked Widget')
self.widget = ImageWidget()
self.setWidget(self.widget)
Note: 1. use nested classes only when actually necessary, otherwise they only make the code cumbersome and difficult to read; 2. all widgets have a default size when created (640x480, or 100x30 for widgets created with a parent), so using self.width() and self.height() is pointless.

How do I prevent double valueChanged events when I press the arrows in a QSpinbox?

Could be a either a Qt bug, or a bug in my app, I am hoping someone can help me diagnose this.
I am building a PyQt5 application with an interface built using qtdesigner and pyuic5.
I have to QSpinBox widgets in my window, which I connect to two methods like this:
self.img1IndexBox.valueChanged.connect(self.changeImage1)
self.img2IndexBox.valueChanged.connect(self.changeImage2)
Everything seems to work fine in the application, except for one thing: If I scroll the mouse over the spinbox, I can make it increment the value. If I change the value with text, all works fine. If I use keyboard arrows on the spinbox, it works fine. But if I click on either the up or down arrows from the spinbox, I get get two changeValue events, a double increment. Like I clicked twice. In fact, it even looks from the animation that it is creating an event for the downpress, and another when the button goes back up.
Could this be just a library bug, or what could be causing this in my program? How could I debug this?
You might be able to prevent that double thing by setting spinbox enable to false.
then make it enable to true after processing large data.
Upon clicking the arrow up/down
on_valuechanged
ui->spinbox->setEnabled(false);
then set to true before the function on_valuechanged ends.
ui->spinbox->setEnabled(true);
Apparently the problem is the event is triggering a very long routine, this delays the "button release" event, and it is enough time to make the system think the user is actually holding the button, generating more events... But I would still be interested in learning what would be a good walk-around. Would there be a nice pyqt-onic way to start a thread for that method?
http://www.qtcentre.org/archive/index.php/t-43078.html
Instead of using valueChanged.connect use editingFinished.connect it will make sure the function is called only after value is provided.
PyQt
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())
PySide
import sys
from PySide6.QtWidgets import QDoubleSpinBox, QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QSpinBox
from PySide6.QtGui import Qt
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
# def valueChanged(self, value):
# print(value)
def valueChanged(self):
print(f"Value changed new value is : {self.spinBox.value()}")
def initUI(self):
self.setGeometry(0, 0, 200, 100)
self.layout = QVBoxLayout()
self.spinBox = QSpinBox()
self.spinBox.setAlignment(Qt.AlignCenter)
self.spinBox.setRange(0, 1000)
# self.spinBox.valueChanged.connect(self.valueChanged)
self.spinBox.editingFinished.connect(self.valueChanged)
self.layout.addWidget(self.spinBox)
self.setLayout(self.layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec())

Ending a QDrag Prematurely

I want my application to terminate all drag and drops in a dragLeaveEvent, without the user releasing the mouse button.
The problem is that the loop suspends all events that could cancel a QDrag while it is happening, even though the documentation states:
"On Linux and Mac OS X, the drag and drop operation can take some
time, but this function does not block the event loop. Other events
are still delivered to the application while the operation is
performed. On Windows, the Qt event loop is blocked during the
operation. However, QDrag.exec() on Windows causes processEvents() to
be called frequently to keep the GUI responsive. If any loops or
operations are called while a drag operation is active, it will block
the drag operation."
Because of this, I cannot call events which would end the drag.
So far, I've tried what is suggested here, as seen in the code. I'm using PyQt5, but if a solution works in Qt it should work in PyQt.
Edit: I'm a little scared to delete the drag, as the scene does not own it. I suppose I could set it up to own it though, but as was posted here it should not work.
Edit2: Added code with my non-working attempts to fix it. I'd really like to solve this issue without having to make my own drag-drop framework. Also trimmed post.
import sys
from PyQt5.QtWidgets import (QMainWindow, QApplication,
QGraphicsView, QGraphicsScene, QGraphicsWidget, QGraphicsRectItem)
from PyQt5.QtCore import (QMimeData, Qt, QByteArray, QCoreApplication,
QEvent, QPoint)
from PyQt5.QtGui import QBrush, QColor, QDrag, QPen, QMouseEvent
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.scene = CustomScene()
self.view = QGraphicsView(self.scene, self)
self.setGeometry(100, 100, 600, 600)
self.view.setGeometry(0, 0, 500, 500)
self.show()
class CustomScene(QGraphicsScene):
def __init__(self):
super().__init__()
self.customWidgets = []
for i in range(5):
newItem = CustomDragWidget()
self.addItem(newItem)
self.customWidgets.append(newItem)
newItem.setGeometry(i * 50, i * 50, 50, 50)
def dragLeaveEvent(self, event):
# Work your magic here. I've tried the following:
# 1)
self.customWidgets[0].dropEvent(event)
# 2)
self.dropEvent(event)
# 3)
eve = QMouseEvent(QEvent.MouseButtonRelease, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
QCoreApplication.sendEvent(self.views()[0], eve)
QCoreApplication.processEvents()
# 4)
eve = QMouseEvent(QEvent.MouseButtonRelease, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
QCoreApplication.sendEvent(self.customWidgets[0], eve)
QCoreApplication.processEvents()
def dropEvent(self, QGraphicsSceneDragDropEvent):
# a dummy dropevent that tries to stop the drop, but doesnt work
QGraphicsSceneDragDropEvent.accept()
class CustomDragWidget(QGraphicsWidget):
def __init__(self,):
super().__init__()
self.squareItem = QGraphicsRectItem()
self.squareItem.setBrush(QBrush(QColor(Qt.blue)))
self.squareItem.setPen(QPen(QColor(Qt.black), 2))
self.squareItem.setRect(0, 0, 50, 50)
self.squareItem.setParentItem(self)
self.setAcceptDrops(True)
def mousePressEvent(self, event):
mime = QMimeData()
itemData = QByteArray()
mime.setData('application/x-dnditemdata', itemData)
drag = QDrag(self)
drag.setMimeData(mime)
drag.exec(Qt.MoveAction)
def dropEvent(self, event):
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
This is a little bit hackish, but it seems to work (on Linux, anyway):
def dragLeaveEvent(self, event):
QCoreApplication.postEvent(self,
QKeyEvent(QEvent.KeyPress, Qt.Key_Escape, Qt.NoModifier))

Using signal from comboBox to disable QspinBox in Pyqt

I have comboBox as one of widget and QspinBox as another widget. I want to disable QspinBox widget if we change option in available in comboBox widget. As an example in my code given below if I change option from option_1 to option_2, QspinBox widget need to get disabled. So how can I do it..?? Any help with example will be appreciated. My code is as follows,
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
class Ui_tri_combobox(object):
def setupUi(self, tri_combobox):
tri_combobox.setObjectName(_fromUtf8("tri_combobox"))
tri_combobox.resize(686, 510)
self.centralWidget = QtGui.QWidget(tri_combobox)
self.centralWidget.setObjectName(_fromUtf8("centralWidget"))
self.comboBox = QtGui.QComboBox(self.centralWidget)
self.comboBox.setGeometry(QtCore.QRect(50, 130, 221, 27))
self.comboBox.setObjectName(_fromUtf8("comboBox"))
self.comboBox.addItem(_fromUtf8(""))
self.comboBox.addItem(_fromUtf8(""))
self.spinBox = QtGui.QSpinBox(self.centralWidget)
self.spinBox.setGeometry(QtCore.QRect(360, 130, 251, 27))
self.spinBox.setObjectName(_fromUtf8("spinBox"))
tri_combobox.setCentralWidget(self.centralWidget)
self.menuBar = QtGui.QMenuBar(tri_combobox)
self.menuBar.setGeometry(QtCore.QRect(0, 0, 686, 25))
self.menuBar.setObjectName(_fromUtf8("menuBar"))
tri_combobox.setMenuBar(self.menuBar)
self.mainToolBar = QtGui.QToolBar(tri_combobox)
self.mainToolBar.setObjectName(_fromUtf8("mainToolBar"))
tri_combobox.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
self.statusBar = QtGui.QStatusBar(tri_combobox)
self.statusBar.setObjectName(_fromUtf8("statusBar"))
tri_combobox.setStatusBar(self.statusBar)
self.retranslateUi(tri_combobox)
QtCore.QMetaObject.connectSlotsByName(tri_combobox)
def retranslateUi(self, tri_combobox):
tri_combobox.setWindowTitle(_translate("tri_combobox", "tri_combobox", None))
self.comboBox.setItemText(0, _translate("tri_combobox", "option_1", None))
self.comboBox.setItemText(1, _translate("tri_combobox", "option_2", None))
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
tri_combobox = QtGui.QMainWindow()
ui = Ui_tri_combobox()
ui.setupUi(tri_combobox)
tri_combobox.show()
sys.exit(app.exec_())
You're looking for the wonderful world of Qt signals and slots.
In simple code terms, this is the code you want executed when someone selects an element from the comboBox.
def dropdownSelect(self, index):
self.spinBox.setEnabled(not index)
Of course a non-toy example will use a more complicated series of if-statements, but the general idea is the same. In this case, index 0 is the first element, 1 is option_1, etc. Add this function to your Ui class.
Now link it by adding this line to setupUi:
self.comboBox.currentIndexChanged.connect(self.dropdownSelect)
Here's the documentation on this particular signal. What's going on here is that you're telling Qt that when the comboBox value is adjusted, you have a special handling function for that. The Qt core event loop handles managing all of that. The signal is a "pattern", that tells you which parameters you'll have access to in your slot.

Resources