Attaching callbacks to events
In this chapter we will add some functionality to the window and we will learn how to obtain the values from the different widgets.
About Qt Signals and Slots
Qt (and therefore PySide/PyQt) has built-in signals and slots to control the most common functionalities of widgets. Signals are messages that are emitted by a widget when a certain event or action ocurs (a button was pressed, for example). Slots receive inputs from signals, and are able to perform common actions such as update a value or enable a widget. Signals and slots can be connected together as an easy way of achieving complex behaviors from common wiget interactions.
More info on signals and slot here at ZetCode.
Adding callbacks
The first action we want to be performed is to disable the cornerSpinBox when the cornerCheckBox is unchecked. To achieve this, we will define a callback function insde the class PCBOutlineCreator:
def onCornersCheckBoxChangedState(self, checked):
pass
And we will connect the signal emitted when the state of the CheckBox changes to this callback:
# Connect slots/callbacks and signals
self.cornersCheckBox.stateChanged.connect(self.onCornersCheckBoxChangedState)
Now, we can write the code to be executed inside the callback:
def onCornersCheckBoxChangedState(self, checked):
self.cornersSpinBox.setEnabled(bool(checked))
If we run the code, we can see that initially the cornersSpinBox is still enabled. As the initial state was unchecked, the state has not changed on reset and the signal was not sent. In order for the code to run as expected we have to either modify our UI file to make the checkbox enabled by default, modify the UI file to make the spinbox disabled by default, or perform either of these actions programatically using this sentence:
self.cornersSpinBox.setEnabled(False)
Using Qt Signals and Slots
Previously we added a callback function to disable the cornerSpinBox if it is unchecked. We can achieve the same result easily by connecting the corresponding signal and slot:
# Connect slots/callbacks and signals
self.cornersCheckBox.stateChanged.connect(lambda i: self.cornersSpinBox.setEnabled(bool(i)))
In this case, as stateChanged signal returns an int and setEnabled expects a bool, a lambda function was used to convert the value.
Extracting values from the widgets
In order to create the outline for our PCB, we need the dimensions specified by the user in the different widgets. For that pursose, we will add a callback to the save button that prints on the stdout the different values extracted from the widgets. first, we connect the signal to the callback:
self.saveButton.clicked.connect(self.onSaveButtonClicked)
And then we add the callback funtion. Inside this function, we request the values of the different widgets and we print them on the stdout:
def onSaveButtonClicked(self):
filename = self.inputFileLineEdit.text()
length = self.lengthSpinBox.value()
width = self.widthSpinBox.value()
line_width = self.lineWidthSpinBox.value()
rounded = self.cornersCheckBox.isChecked()
corners_radius = self.cornersSpinBox.value()
x = self.xSpinBox.value()
y = self.ySpinBox.value()
print "Values are: "
print "Filename: %s" % filename
print "Length: %.2f Width: %.2f" % (length, width)
print "Line width: %.2f" % line_width
if corners_radius:
print "Corner radius: %.2f" % corners_radius
print "x: %.2f y: %.2f" % (x, y)
When we run the code, and press the save button we get the following message on the stdout:
Complete code
from PySide import QtCore,QtGui
from PySide import QtUiTools
import os, sys
def load_ui(file_name, where=None):
"""
Loads a .UI file into the corresponding Qt Python object
:param file_name: UI file path
:param where: Use this parameter to load the UI into an existing class (i.e. to override methods)
:return: loaded UI
"""
# Create a QtLoader
loader = QtUiTools.QUiLoader()
# Open the UI file
ui_file = QtCore.QFile(file_name)
ui_file.open(QtCore.QFile.ReadOnly)
# Load the contents of the file
ui = loader.load(ui_file, where)
# Close the file
ui_file.close()
return ui
class PCBOutlineCreator(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUI()
self.resetValues()
def setupUI(self):
# Load UI and set it as main layout
ui_file_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'PCBOutlineCreator.ui')
main_widget = load_ui(ui_file_path, self)
layout = QtGui.QVBoxLayout()
layout.addWidget(main_widget)
self.setLayout(layout)
# Get a reference to all required widgets
self.inputFileButton = self.findChild(QtGui.QToolButton, 'inputFileButton')
self.inputFileLineEdit = self.findChild(QtGui.QLineEdit, 'inputFileLineEdit')
self.exportButton = self.findChild(QtGui.QPushButton, 'exportButton')
self.saveButton = self.findChild(QtGui.QPushButton, 'saveButton')
self.lengthSpinBox = self.findChild(QtGui.QDoubleSpinBox, 'lengthSpinBox')
self.widthSpinBox = self.findChild(QtGui.QDoubleSpinBox, 'widthSpinBox')
self.lineWidthSpinBox = self.findChild(QtGui.QDoubleSpinBox, 'lineWidthSpinBox')
self.cornersSpinBox = self.findChild(QtGui.QDoubleSpinBox, 'cornersSpinBox')
self.cornersCheckBox = self.findChild(QtGui.QCheckBox, 'cornersCheckBox')
self.xSpinBox = self.findChild(QtGui.QDoubleSpinBox, 'xSpinBox')
self.ySpinBox = self.findChild(QtGui.QDoubleSpinBox, 'ySpinBox')
# Configure widget ranges:
max_float = sys.float_info.max
self.lengthSpinBox.setMinimum(0)
self.lengthSpinBox.setMaximum(max_float)
self.widthSpinBox.setMinimum(0)
self.widthSpinBox.setMaximum(max_float)
self.lineWidthSpinBox.setMinimum(0)
self.lineWidthSpinBox.setMaximum(max_float)
self.lineWidthSpinBox.setSingleStep(0.1)
self.cornersSpinBox.setMinimum(0)
self.cornersSpinBox.setMaximum(max_float)
self.xSpinBox.setMinimum(-max_float)
self.xSpinBox.setMaximum(max_float)
self.ySpinBox.setMinimum(-max_float)
self.ySpinBox.setMaximum(max_float)
# Connect slots/callbacks and signals
self.cornersSpinBox.setEnabled(False)
self.cornersCheckBox.stateChanged.connect(self.onCornersCheckBoxChangedState)
self.saveButton.clicked.connect(self.onSaveButtonClicked)
def resetValues(self):
self.lengthSpinBox.setValue(50)
self.widthSpinBox.setValue(50)
self.lineWidthSpinBox.setValue(0.2)
self.cornersCheckBox.setChecked(False)
self.cornersSpinBox.setValue(5)
self.xSpinBox.setValue(0)
self.ySpinBox.setValue(0)
def onCornersCheckBoxChangedState(self, checked):
self.cornersSpinBox.setEnabled(bool(checked))
def onSaveButtonClicked(self):
filename = self.inputFileLineEdit.text()
length = self.lengthSpinBox.value()
width = self.widthSpinBox.value()
line_width = self.lineWidthSpinBox.value()
rounded = self.cornersCheckBox.isChecked()
corners_radius = self.cornersSpinBox.value()
x = self.xSpinBox.value()
y = self.ySpinBox.value()
print "Values are: "
print "Filename: %s" % filename
print "Length: %.2f Width: %.2f" % (length, width)
print "Line width: %.2f" % line_width
if corners_radius:
print "Corner radius: %.2f" % corners_radius
print "x: %.2f y: %.2f" % (x, y)
if __name__ == '__main__':
# Create Qt app
app = QtGui.QApplication(sys.argv)
# Create the widget and show it
gui = PCBOutlineCreator()
gui.show()
# Run the app
sys.exit(app.exec_())