aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
blob: c1234f23e1bd1f497767102cc8ed6b840d3672b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
Using `.ui` files from Designer or QtCreator with `QUiLoader` and `pyside6-uic`
*******************************************************************************

This page describes the use of
`Qt Designer <https://doc.qt.io/qt-6/qtdesigner-manual.html>`_ to create
graphical interfaces based on Qt Widgets for your Qt for Python project.
**Qt Designer** is a graphical UI design tool which is available as a
standalone binary (``pyside6-designer``) or embedded into the
`Qt Creator IDE <https://doc.qt.io/qtcreator>`_. Its use within **Qt Creator**
is described at
`Using Qt Designer <http://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_.

The designs are stored in `.ui` files, which is an XML-based format. It will
be converted to Python or C++ code populating a widget instance at project build
time by the `pyside6-uic <https://doc.qt.io/qt-6/uic.html>`_ tool.

To create a new Qt Design Form in **Qt Creator**, choose
`File/New File Or Project` and "Main Window" for template. Save it as
`mainwindow.ui`. Add a `QPushButton` to the center of the centralwidget.

Your file ``mainwindow.ui`` should look something like this:

.. code-block:: xml

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>400</width>
        <height>300</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralWidget">
       <widget class="QPushButton" name="pushButton">
        <property name="geometry">
         <rect>
          <x>110</x>
          <y>80</y>
          <width>201</width>
          <height>81</height>
         </rect>
        </property>
        <property name="text">
         <string>PushButton</string>
        </property>
       </widget>
      </widget>
      <widget class="QMenuBar" name="menuBar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>400</width>
         <height>20</height>
        </rect>
       </property>
      </widget>
      <widget class="QToolBar" name="mainToolBar">
       <attribute name="toolBarArea">
        <enum>TopToolBarArea</enum>
       </attribute>
       <attribute name="toolBarBreak">
        <bool>false</bool>
       </attribute>
      </widget>
      <widget class="QStatusBar" name="statusBar"/>
     </widget>
     <layoutdefault spacing="6" margin="11"/>
     <resources/>
     <connections/>
    </ui>

Now we are ready to decide how to use the **UI file** from Python.

Option A: Generating a Python class
===================================

The standard way to interact with a **UI file** is to generate a Python
class from it. This is possible thanks to the `pyside6-uic` tool.
To use this tool, you need to run the following command on a console::

    pyside6-uic mainwindow.ui > ui_mainwindow.py

We redirect all the output of the command to a file called `ui_mainwindow.py`,
which will be imported directly::

    from ui_mainwindow import Ui_MainWindow

Now to use it, we should create a personalized class for our widget
to **setup** this generated design.

To understand the idea, let's take a look at the whole code:

.. code-block:: python

    import sys
    from PySide6.QtWidgets import QApplication, QMainWindow
    from PySide6.QtCore import QFile
    from ui_mainwindow import Ui_MainWindow

    class MainWindow(QMainWindow):
        def __init__(self):
            super(MainWindow, self).__init__()
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)

    if __name__ == "__main__":
        app = QApplication(sys.argv)

        window = MainWindow()
        window.show()

        sys.exit(app.exec())

What is inside the *if* statement is already known from the previous
examples, and our new basic class contains only two new lines
that are in charge of loading the generated python class from the UI
file:

.. code-block:: python

    self.ui = Ui_MainWindow()
    self.ui.setupUi(self)

.. note::

  You must run `pyside6-uic` again every time you make changes
  to the **UI file**.

Option B: Loading it directly
=============================

To load the UI file directly, we will need a class from the **QtUiTools**
module:

.. code-block:: python

    from PySide6.QtUiTools import QUiLoader

The `QUiLoader` lets us load the **ui file** dynamically
and use it right away:

.. code-block:: python

    ui_file = QFile("mainwindow.ui")
    ui_file.open(QFile.ReadOnly)

    loader = QUiLoader()
    window = loader.load(ui_file)
    window.show()

The complete code of this example looks like this:

.. code-block:: python

    # File: main.py
    import sys
    from PySide6.QtUiTools import QUiLoader
    from PySide6.QtWidgets import QApplication
    from PySide6.QtCore import QFile, QIODevice

    if __name__ == "__main__":
        app = QApplication(sys.argv)

        ui_file_name = "mainwindow.ui"
        ui_file = QFile(ui_file_name)
        if not ui_file.open(QIODevice.ReadOnly):
            print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
            sys.exit(-1)
        loader = QUiLoader()
        window = loader.load(ui_file)
        ui_file.close()
        if not window:
            print(loader.errorString())
            sys.exit(-1)
        window.show()

        sys.exit(app.exec())

Then to execute it we just need to run the following on a
command prompt:

.. code-block:: python

    python main.py


.. _designer_custom_widgets:

Custom Widgets in Qt Designer
=============================

**Qt Designer** is able to use user-provided (custom) widgets. They are shown
in the widget box and can be dragged onto the form just like Qt's widgets (see
`Using Custom Widgets with Qt Designer <https://doc.qt.io/qt-6/designer-using-custom-widgets.html>`_
). Normally, this requires implementing the widget as a plugin to Qt Designer
written in  C++ implementing its
`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .

Qt for Python provides a simple interface for this which is similar to
:meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`.

The widget needs to be provided as a Python module, as shown by
the widgetbinding example (file ``wigglywidget.py``) or
the taskmenuextension example (file ``tictactoe.py``).

Registering this with Qt Designer is done by providing
a registration script named ``register*.py`` and pointing
the  path-type environment variable ``PYSIDE_DESIGNER_PLUGINS``
to the directory.

The code of the registration script looks as follows:

.. code-block:: python

    # File: registerwigglywidget.py
    from wigglywidget import WigglyWidget

    import QtDesigner


    TOOLTIP = "A cool wiggly widget (Python)"
    DOM_XML = """
    <ui language='c++'>
        <widget class='WigglyWidget' name='wigglyWidget'>
            <property name='geometry'>
                <rect>
                    <x>0</x>
                    <y>0</y>
                    <width>400</width>
                    <height>200</height>
                </rect>
            </property>
            <property name='text'>
                <string>Hello, world</string>
            </property>
        </widget>
    </ui>
    """

    QPyDesignerCustomWidgetCollection.registerCustomWidget(WigglyWidget, module="wigglywidget",
                                                           tool_tip=TOOLTIP, xml=DOM_XML)


QPyDesignerCustomWidgetCollection provides an implementation of
`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
exposing custom widgets to **Qt Designer** with static convenience functions
for registering types or adding instances of
`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .

The function
:meth:`registerCustomWidget()<PySide6.QtDesigner.QPyDesignerCustomWidgetCollection.registerCustomWidget>`
is used to register a widget type with **Qt Designer**. In the simple case, it
can be used like `QUiLoader.registerCustomWidget()`. It takes the custom widget
type and some optional keyword arguments passing values that correspond to the
getters of
`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ :

When launching **Qt Designer** via its launcher ``pyside6-designer``,
the custom widget should be visible in the widget box.

For advanced usage, it is also possible to pass the function an implementation
of the class QDesignerCustomWidgetInterface instead of the type to
:meth:`addCustomWidget()<PySide6.QtDesigner.QPyDesignerCustomWidgetCollection.addCustomWidget>`.
This is shown in taskmenuextension example, where a custom context menu
is registered for the custom widget. The example is a port of the
corresponding C++
`Task Menu Extension Example <https://doc.qt.io/qt-6/qtdesigner-taskmenuextension-example.html>`_ .

Troubleshooting the Qt Designer Plugin
++++++++++++++++++++++++++++++++++++++

- The launcher ``pyside6-designer`` must be used. The standalone
  **Qt Designer** will not load the plugin.
- The menu item **Help/About Plugin** brings up a dialog showing the plugins
  found and potential load error messages.
- Check the console or Windows Debug view for further error messages.
- Due to the buffering of output by Python, error messages may appear
  only after **Qt Designer** has terminated.
- When building Qt for Python, be sure to set the ``--standalone`` option
  for the plugin to be properly installed.