PyQT: Input Widgets

You may have experienced some headaches in the past when creating GUIs to gather user input. Python makes this process easy with input widgets. Keep reading to learn more.

The edge that GUI has over CLI is the varied ways it offers to gather user input.  In a CLI-based application, there are at most two ways of taking the input – as strings of alphanumeric or as a single character or number. With a GUI-based application, the input can be gathered using various widgets such as textboxes, checkboxes, radio buttons etc.

In most GUI frameworks, input widgets are the most complex or difficult to create. But the simplicity of Python comes to the rescue with PyQT. There are many input widgets supplied with PyQT itself, and if the platform happens to be KDE, then widgets that are capable of handling multiple types of input strategies are available.

Starting from this part, I will be focusing on the different input widgets provided by PyQT. In this discussion I will be introducing the line input widget along with the checkbox widget. The first two sections will focus on the slots and signals of the widgets. The following sections will test the widgets by using them in a form that will later become part of a larger application. That sets the agenda for this discussion.

QLineEdit and QCheckbox: Understanding the Basic Widgets

Every GUI toolkit provides some widgets that provide basic services for gathering user inputs. These widgets don’t fit the category of heavy duty GUI widgets of the likes of a rich text editor, tree view et al. However they are quite indispensable when a pretty good user interface must be created. The single line textbox and checkbox are two of such basic yet indispensable widgets. In the world of PyQT they are known as QLineEdit and QCheckbox. 

The difference that these widgets have between their counterparts in other languages is the way events are handled. That’s right – signals and slots. So let’s look at the most common signals and slots that are available for QLineEdit and QCheckbox.

QLineEdit

As the name suggests, QLineEdit is a one line text editor. In other words, it supports basic editing services but the text cannot span over multiple lines (read one line). In essence, it lets the user enter and edit a single line of plain text. The most common slots of this widget are:

  • setText()
  • selectAll()
  • setMaxLength()
  • insert()

and the signals are:

  • textChanged()
  • textEdited()

Let’s look at the slots first.

The setText() slot is used to set a new text into the QLineEdit. When a new text is set, it removes the text that is already present. It takes one parameter – QString. When text is set using setText() method, the textChanged() signal is emitted.

The selectAll() slot is used to select all the entered text. That means this slot can be used to highlight the entered text. In some cases it becomes necessary to place default values for QLineEdit. But whenever the user tries to enter data, the default value has to be deleted. For this to take place, the default data present has to be completely highlighted so that it can be deleted when the user tries to enter data. Hence, to make the current data in the QLineEdit preselected, this slot can be called.

The setMaxLength() slot takes care of the requirement to set the maximum length of the text that can be entered. It takes an integer value as argument.

While the setText() slot replaces or appends the text (according to the logic used), there are times, when text needs to be inserted. The insert() slot comes handy in such situations. One thing to keep in mind is that while invoking this slot, if any  portion of the text is highlighted, it will be deleted and the value of the QString passed as argument will be pasted in place of that portion.

That’s about it for the theory of QLineEdit’s service slots. Now let’s look at how they can be used. The following block of code creates an object of QLineEdit, sets its length to 10, sets the text to “123- BackwardSt” and then selects it all:

Address = QLineEdit( )
Address.setMaxLength(10)
Address.setText(‘123- BackwardSt’)
Address.selectAll()

The next part of this puzzle is the signals emitted by QLineEdit.

Whenever the text is changed programmatically using the setText() slot, the textChanged() signal is emitted. This signal takes a QString argument which is the new text that has been set in QLineEdit. This is emitted when the text changes in any other way (such as the user changes the text).

The textEdited() signal also takes a QString as an argument and is emitted whenever text is edited (or changed). However if the text is changed via the setText() slot, then this signal is not emitted.

The one question that still remains about QLineEdit is that of how to access the text contained in QLineEdit. For this, the property of QLineEdit known as text() can be used. It returns a QString object containing the current text of the QLineEdit. That brings us to the next widget – QCheckBox.

{mospagebreak title=QCheckBox}

This widget provides the checkbox with text. It can have three states – checked, unchecked and no change of state. In essence it is an option button that can be turned on or off. In an application checkbox it can come handy when a set of options have to be presented to the user and user has the choice of selecting one or more options.

Typically checkboxes are used when their state doesn’t effect the states of other widgets. However there are exceptions to this rule as we will see in the example. The main aspect of QCheckBox is that just like QButton, it also has been derived from QAbstractButton and doesn’t have any slots. However, the methods provide all the required services. Secondly, a group of logically similar checkboxes can be grouped visually into a category using the QButtonGroup. The main signal of QCheckbox is stateChange(), and its main methods are checkStateSet() and setText(). The second method is inherited. 

The stateChange() signal is used whenever the user checks or unchecks the widget. In other words, when the state of the widget changes, the widget emits a stateChange signal. The argument contains the check box’s new Toggle state. The Toggle state is an integer that contains current state.

The checkStateSet() method takes Qt.CheckState as argument. The valid values are Qt.Unchecked, Qt.PartiallyChecked and Qt.Checked. The second value comes into the picture when items in hierarchical models may be partially checked if some, but not all, of their children are checked.

The setText() method can be used to set the text of a checkbox. The parameter passed is QString, representing the text to be set. However, it doesn’t cause any signals to be emitted. Using the setIcon method, an icon can be placed beside the checkbox.

To create a checkbox with the text "secure," the code would be:

cb12=QCheckBox("&Secure”)
cb12.setTristate(TRUE)

That brings us to the end of this section. In the next section I would be designing a form that would provide minimal connection details to connect to a server.

{mospagebreak title=PyQT in the Real World}

From this part onwards I will be developing a small application spread over several articles. So the first dialog that we need is a host connection panel. In this at present there will be minimal input gathering. At this level only two textboxes, one checkbox and one button, exist. The logic is that both the textboxes will contain default values, and when the first textbox is filled, then the default value in the second textbox will be selected. So here is the code. Keep one thing in mind: this is just skeletal code, so there are no decorations.

First the imports and defining of the panel:

from qt import *

class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal =
0,fl = 0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

Then creating the labels and the textboxes:

from qt import *

class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal = 0,fl = 0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

        self.textLabel1 = QLabel(self,"textLabel1")

        self.textLabel1.setGeometry(QRect
(30,71,111,30))

        self.textLabel1_2 = QLabel(self,"textLabel1_2")

        self.textLabel1_2.setGeometry(QRect
(30,140,111,30))

       

        self.lineEdit1 = QLineEdit(self,"lineEdit1")

        self.lineEdit1.setGeometry(QRect
(160,70,181,31))

        self.lineEdit2 = QLineEdit(self,"lineEdit2")

        self.lineEdit2.setGeometry(QRect
(161,140,180,31))

Then let’s define the checkbox and the pushbuttons.

   class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal = 0,fl =
0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

        self.textLabel1 = QLabel(self,"textLabel1")

        self.textLabel1.setGeometry(QRect(30,71,111,30))

        self.textLabel1_2 = QLabel(self,"textLabel1_2")

        self.textLabel1_2.setGeometry(QRect(30,140,111,30))

        self.lineEdit1 = QLineEdit(self,"lineEdit1")

        self.lineEdit1.setGeometry(QRect(160,70,181,31))

        self.lineEdit2 = QLineEdit(self,"lineEdit2")

        self.lineEdit2.setGeometry(QRect(161,140,180,31))

        self.checkBox1 = QCheckBox(self,"checkBox1")

        self.checkBox1.setGeometry(QRect(30,190,91,21))

        self.pushButton1 = QPushButton
(self,"pushButton1")

        self.pushButton1.setEnabled(1)

        self.pushButton1.setGeometry(QRect
(30,230,141,21))

        self.pushButton1_2 = QPushButton
(self,"pushButton1_2")

        self.pushButton1_2.setEnabled(1)

        self.pushButton1_2.setGeometry(QRect
(210,230,141,21))

By now you will be wondering why I have not set the text of the widgets. There is a twist. To enable a multilingual approach, the _tr function can be used. This method, when provided with translation files, can show the captions or the text in the desired language. Here I have placed it in a method called languageChange() which is called after the widgets are placed.

class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal = 0,fl =
0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

        self.textLabel1 = QLabel(self,"textLabel1")

        self.textLabel1.setGeometry(QRect(30,71,111,30))

        self.textLabel1_2 = QLabel(self,"textLabel1_2")

        self.textLabel1_2.setGeometry(QRect(30,140,111,30))

        self.lineEdit1 = QLineEdit(self,"lineEdit1")

        self.lineEdit1.setGeometry(QRect(160,70,181,31))

        self.lineEdit2 = QLineEdit(self,"lineEdit2")

        self.lineEdit2.setGeometry(QRect(161,140,180,31))

         self.checkBox1 = QCheckBox(self,"checkBox1")

        self.checkBox1.setGeometry(QRect(30,190,91,21))

        self.pushButton1 = QPushButton(self,"pushButton1")

        self.pushButton1.setEnabled(1)

        self.pushButton1.setGeometry(QRect(30,230,141,21))

        self.pushButton1_2 = QPushButton(self,"pushButton1_2")

        self.pushButton1_2.setEnabled(1)

        self.pushButton1_2.setGeometry(QRect(210,230,141,21))

        self.languageChange()

        self.resize(QSize(600,480).expandedTo
(self.minimumSizeHint()))

        self.clearWState(Qt.WState_Polished)

    def languageChange(self):

        self.setCaption(self.__tr("Form1"))

        self.textLabel1.setText(self.__tr("Host"))

        self.textLabel1_2.setText(self.__tr("Port"))

        self.pushButton1.setText(self.__tr("Submit"))

        self.pushButton1_2.setText(self.__tr("Reset"))

        self.checkBox1.setText(self.__tr("Secured"))

        self.lineEdit1.setText(self.__tr
("192.168.1.1"))

        self.lineEdit2.setText(self.__tr("3306"))

    def __tr(self,s,c = None):

        return qApp.translate("Form1",s,c)

{mospagebreak title=The Connections}

Next comes the connections. The enterPressed signal of the lineEdit1 is connected with the selectAll slot of lineEdit2. Similarly, the clicked signal of pushbutton2(having reset its value) is connected to the clear slot of both the textboxes.

class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal = 0,fl =
0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

        self.textLabel1 = QLabel(self,"textLabel1")

        self.textLabel1.setGeometry(QRect(30,71,111,30))

        self.textLabel1_2 = QLabel(self,"textLabel1_2")

        self.textLabel1_2.setGeometry(QRect(30,140,111,30))

        self.lineEdit1 = QLineEdit(self,"lineEdit1")

        self.lineEdit1.setGeometry(QRect(160,70,181,31))

        self.lineEdit2 = QLineEdit(self,"lineEdit2")

        self.lineEdit2.setGeometry(QRect(161,140,180,31))

       self.checkBox1 = QCheckBox(self,"checkBox1")

        self.checkBox1.setGeometry(QRect(30,190,91,21))

        self.pushButton1 = QPushButton(self,"pushButton1")

        self.pushButton1.setEnabled(1)

        self.pushButton1.setGeometry(QRect(30,230,141,21))

        self.pushButton1_2 = QPushButton(self,"pushButton1_2")

        self.pushButton1_2.setEnabled(1)

        self.pushButton1_2.setGeometry(QRect(210,230,141,21))

        self.languageChange()

        self.resize(QSize(600,480).expandedTo
(self.minimumSizeHint()))

        self.clearWState(Qt.WState_Polished)

         self.connect(self.lineEdit1,SIGNAL
("returnPressed()"),

                      self.lineEdit2.selectAll)

         self.connect(self.pushButton1_2,SIGNAL
("clicked()"),

                                      self.lineEdit1.clear)

         self.connect(self.pushButton1_2,SIGNAL
("clicked()"),

                                    self.lineEdit2.clear)

    def languageChange(self):

        self.setCaption(self.__tr("Form1"))

        self.textLabel1.setText(self.__tr("Host"))

        self.textLabel1_2.setText(self.__tr("Port"))

        self.pushButton1.setText(self.__tr("Submit"))

        self.pushButton1_2.setText(self.__tr("Reset"))

        self.checkBox1.setText(self.__tr("Secured"))

        self.lineEdit1.setText(self.__tr("192.168.1.1"))

        self.lineEdit2.setText(self.__tr("3306"))

    def __tr(self,s,c = None):

        return qApp.translate("Form1",s,c)

{mospagebreak title=The Main Part}

Lastly, here is the "main" part of the application:

class Form1(QDialog):

    def __init__(self,parent = None,name = None,modal = 0,fl =
0):

        QDialog.__init__(self,parent,name,modal,fl)

        if not name:

            self.setName("Form1")

        self.textLabel1 = QLabel(self,"textLabel1")

        self.textLabel1.setGeometry(QRect(30,71,111,30))

        self.textLabel1_2 = QLabel(self,"textLabel1_2")

        self.textLabel1_2.setGeometry(QRect(30,140,111,30))

       

        self.lineEdit1 = QLineEdit(self,"lineEdit1")

        self.lineEdit1.setGeometry(QRect(160,70,181,31))

        self.lineEdit2 = QLineEdit(self,"lineEdit2")

        self.lineEdit2.setGeometry(QRect(161,140,180,31))

       self.checkBox1 = QCheckBox(self,"checkBox1")

        self.checkBox1.setGeometry(QRect(30,190,91,21))

        self.pushButton1 = QPushButton(self,"pushButton1")

        self.pushButton1.setEnabled(1)

        self.pushButton1.setGeometry(QRect(30,230,141,21))

        self.pushButton1_2 = QPushButton(self,"pushButton1_2")

        self.pushButton1_2.setEnabled(1)

        self.pushButton1_2.setGeometry(QRect(210,230,141,21))

        self.languageChange()

        self.resize(QSize(600,480).expandedTo
(self.minimumSizeHint()))

        self.clearWState(Qt.WState_Polished)

        self.connect(self.lineEdit1,SIGNAL("returnPressed
()"),self.lineEdit2.selectAll)

        self.connect(self.pushButton1_2,SIGNAL("clicked
()"),self.lineEdit1.clear)

        self.connect(self.pushButton1_2,SIGNAL("clicked
()"),self.lineEdit2.clear)

    def languageChange(self):

        self.setCaption(self.__tr("Form1"))

        self.textLabel1.setText(self.__tr("Host"))

        self.textLabel1_2.setText(self.__tr("Port"))

        self.pushButton1.setText(self.__tr("Submit"))

        self.pushButton1_2.setText(self.__tr("Reset"))

        self.checkBox1.setText(self.__tr("Secured"))

        self.lineEdit1.setText(self.__tr("192.168.1.1"))

        self.lineEdit2.setText(self.__tr("3306"))

    def __tr(self,s,c = None):

        return qApp.translate("Form1",s,c)

 if __name__ == "__main__":

    a = QApplication(sys.argv)

    QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT
("quit()"))

    w = Form1()

    a.setMainWidget(w)

    a.exec_loop()

That brings us to the end of this discussion. There are certain concepts such as translation and labels that have been used in the example but not explained. Those will be the topics of future discussions along with the remaining input widgets. Till then…

Google+ Comments

Google+ Comments