diff options
Diffstat (limited to 'sources/pyside6/doc/tutorials/basictutorial')
21 files changed, 990 insertions, 0 deletions
diff --git a/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst b/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst new file mode 100644 index 000000000..bd45f1f64 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst @@ -0,0 +1,90 @@ +A Simple Button Tutorial +************************ + +In this tutorial, we'll show you how to handle **signals and slots** +using Qt for Python. **Signals and slots** is a Qt feature that lets +your graphical widgets communicate with other graphical widgets or +your python code. Our application creates a button that logs the +`Button clicked, Hello!` message to the python console each time you +click it. + +Let's start by importing the necessary PySide6 classes and python +`sys` module: +:: + + import sys + from PySide6.QtWidgets import QApplication, QPushButton + from PySide6.QtCore import Slot + +Let's also create a python function that logs the message to the +console: +:: + + # Greetings + @Slot() + def say_hello(): + print("Button clicked, Hello!") + +.. note:: The `@Slot()` is a decorator that identifies a function as + a slot. It is not important to understand why for now, + but use it always to avoid unexpected behavior. + +Now, as mentioned in previous examples you must create the +`QApplication` to run your PySide6 code: +:: + + # Create the Qt Application + app = QApplication(sys.argv) + +Let's create the clickable button, which is a `QPushButton` instance. +To label the button, we pass a python string to the constructor: +:: + + # Create a button + button = QPushButton("Click me") + +Before we show the button, we must connect it to the `say_hello()` +function that we defined earlier. There are two ways of doing this; +using the old style or the new style, which is more pythonic. Let's +use the new style in this case. You can find more information about +both these styles in the +`Signals and Slots in PySide6 <https://wiki.qt.io/Qt_for_Python_Signals_and_Slots>`_ +wiki page. + +The `QPushButton` has a predefined signal called **clicked**, which +is triggered every time the button is clicked. We'll connect this +signal to the `say_hello()` function: +:: + + # Connect the button to the function + button.clicked.connect(say_hello) + +Finally, we show the button and start the Qt main loop: +:: + + # Show the button + button.show() + # Run the main Qt loop + app.exec_() + +Here is the complete code for this example: +:: + + #!/usr/bin/python + + import sys + from PySide6.QtWidgets import QApplication, QPushButton + from PySide6.QtCore import Slot + + @Slot() + def say_hello(): + print("Button clicked, Hello!") + + # Create the Qt Application + app = QApplication(sys.argv) + # Create a button, connect it and show it + button = QPushButton("Click me") + button.clicked.connect(say_hello) + button.show() + # Run the main Qt loop + app.exec_() diff --git a/sources/pyside6/doc/tutorials/basictutorial/dialog.rst b/sources/pyside6/doc/tutorials/basictutorial/dialog.rst new file mode 100644 index 000000000..0ced571db --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/dialog.rst @@ -0,0 +1,145 @@ +Creating a Simple PySide6 Dialog Application +********************************************* + +This tutorial shows how to build a simple dialog with some +basic widgets. The idea is to let users provide their name +in a `QLineEdit`, and the dialog greets them on click of a +`QPushButton`. + +Let us just start with a simple stub that creates and shows +a dialog. This stub is updated during the course of this +tutorial, but you can use this stub as is if you need to: +:: + + import sys + from PySide6.QtWidgets import QApplication, QDialog, QLineEdit, QPushButton + + class Form(QDialog): + + def __init__(self, parent=None): + super(Form, self).__init__(parent) + self.setWindowTitle("My Form") + + + if __name__ == '__main__': + # Create the Qt Application + app = QApplication(sys.argv) + # Create and show the form + form = Form() + form.show() + # Run the main Qt loop + sys.exit(app.exec_()) + +The imports aren't new to you, the same for the creation of the +`QApplication` and the execution of the Qt main loop. +The only novelty here is the **class definition**. + +You can create any class that subclasses PySide6 widgets. +In this case, we are subclassing `QDialog` to define a custom +dialog, which we name as **Form**. We have also implemented the +`init()` method that calls the `QDialog`'s init method with the +parent widget, if any. Also, the new `setWindowTitle()` method +just sets the title of the dialog window. In `main()`, you can see +that we are creating a *Form object* and showing it to the world. + +Create the Widgets +=================== + +We are going to create two widgets: a `QLineEdit` where users can +enter their name, and a `QPushButton` that prints the contents of +the `QLineEdit`. +So, let's add the following code to the `init()` method of our Form: +:: + + # Create widgets + self.edit = QLineEdit("Write my name here..") + self.button = QPushButton("Show Greetings") + +It's obvious from the code that both widgets will show the corresponding +texts. + +Create a layout to organize the Widgets +======================================== + +Qt comes with layout-support that helps you organize the widgets +in your application. In this case, let's use `QVBoxLayout` to lay out +the widgets vertically. Add the following code to the `init()` method, +after creating the widgets: +:: + + # Create layout and add widgets + layout = QVBoxLayout() + layout.addWidget(self.edit) + layout.addWidget(self.button) + # Set dialog layout + self.setLayout(layout) + +So, we create the layout, add the widgets with `addWidget()`, +and finally we say that our **Form** will have our `QVBoxLayout` +as its layout. + +Create the function to greet and connect the Button +==================================================== + +Finally, we just have to add a function to our custom **Form** +and *connect* our button to it. Our function will be a part of +the Form, so you have to add it after the `init()` function: +:: + + # Greets the user + def greetings(self): + print ("Hello {}".format(self.edit.text())) + +Our function just prints the contents of the `QLineEdit` to the +python console. We have access to the text by means of the +`QLineEdit.text()` method. + +Now that we have everything, we just need to *connect* the +`QPushButton` to the `Form.greetings()` method. To do so, add the +following line to the `init()` method: +:: + + # Add button signal to greetings slot + self.button.clicked.connect(self.greetings) + +Once executed, you can enter your name in the `QLineEdit` and watch +the console for greetings. + +Complete code +============= + +Here is the complete code for this tutorial: +:: + + import sys + from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication, + QVBoxLayout, QDialog) + + class Form(QDialog): + + def __init__(self, parent=None): + super(Form, self).__init__(parent) + # Create widgets + self.edit = QLineEdit("Write my name here") + self.button = QPushButton("Show Greetings") + # Create layout and add widgets + layout = QVBoxLayout() + layout.addWidget(self.edit) + layout.addWidget(self.button) + # Set dialog layout + self.setLayout(layout) + # Add button signal to greetings slot + self.button.clicked.connect(self.greetings) + + # Greets the user + def greetings(self): + print ("Hello %s" % self.edit.text()) + + if __name__ == '__main__': + # Create the Qt Application + app = QApplication(sys.argv) + # Create and show the form + form = Form() + form.show() + # Run the main Qt loop + sys.exit(app.exec_()) diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons.png b/sources/pyside6/doc/tutorials/basictutorial/icons.png Binary files differnew file mode 100644 index 000000000..0bcfd7d77 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png b/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png Binary files differnew file mode 100644 index 000000000..c7a532dfe --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons/forward.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png b/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png Binary files differnew file mode 100644 index 000000000..d0beadb43 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons/pause.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/play.png b/sources/pyside6/doc/tutorials/basictutorial/icons/play.png Binary files differnew file mode 100644 index 000000000..345685337 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons/play.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png b/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png Binary files differnew file mode 100644 index 000000000..979f18565 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons/previous.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png b/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png Binary files differnew file mode 100644 index 000000000..1e88ded3a --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/icons/stop.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/player-new.png b/sources/pyside6/doc/tutorials/basictutorial/player-new.png Binary files differnew file mode 100644 index 000000000..e1f660e5f --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/player-new.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/player.png b/sources/pyside6/doc/tutorials/basictutorial/player.png Binary files differnew file mode 100644 index 000000000..3060a990d --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/player.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/qml.rst b/sources/pyside6/doc/tutorials/basictutorial/qml.rst new file mode 100644 index 000000000..c972daa00 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/qml.rst @@ -0,0 +1,67 @@ +Your First Application Using PySide6 and QtQuick/QML +***************************************************** + +QML is a declarative language that lets you develop applications +faster than with traditional languages. It is ideal for designing the +UI of your application because of its declarative nature. In QML, a +user interface is specified as a tree of objects with properties. In +this tutorial, we will show how to make a simple "Hello World" +application with PySide6 and QML. + +A PySide6/QML application consists, at least, of two different files - +a file with the QML description of the user interface, and a python file +that loads the QML file. To make things easier, let's save both files in +the same directory. + +Here is a simple QML file called `view.qml`: + +.. code-block:: javascript + + import QtQuick 2.0 + + Rectangle { + width: 200 + height: 200 + color: "green" + + Text { + text: "Hello World" + anchors.centerIn: parent + } + } + +We start by importing `QtQuick 2.0`, which is a QML module. + +The rest of the QML code is pretty straightforward for those who +have previously used HTML or XML files. Basically, we are creating +a green rectangle with the size `200*200`, and adding a Text element +that reads "Hello World". The code `anchors.centerIn: parent` makes +the text appear centered in relation to its immediate parent, which +is the Rectangle in this case. + +Now, let's see how the code looks on the PySide6. +Let's call it `main.py`: + +.. code-block:: python + + from PySide6.QtWidgets import QApplication + from PySide6.QtQuick import QQuickView + from PySide6.QtCore import QUrl + + app = QApplication([]) + view = QQuickView() + url = QUrl("view.qml") + + view.setSource(url) + view.show() + app.exec_() + +If you are already familiar with PySide6 and have followed our +tutorials, you have already seen much of this code. +The only novelties are that you must `import QtQuick` and set the +source of the `QQuickView` object to the URL of your QML file. +Then, as any Qt widget, you call `QQuickView.show()`. + +.. note:: If you are programming for desktop, you should consider + adding `view.setResizeMode(QQuickView.SizeRootObjectToView)` + before showing the view. diff --git a/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst b/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst new file mode 100644 index 000000000..5ac560a8f --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/qrcfiles.rst @@ -0,0 +1,169 @@ +Using `.qrc` Files (`pyside6-rcc`) +********************************** + +The `Qt Resource System`_ is a mechanism for storing binary files +in an application. + +The most common uses are for custom images, icons, fonts, among others. + +In this tutorial you will learn how to load custom images as button icons. + +For inspiration, we will try to adapt the multimedia player example +from Qt. + +As you can see on the following image, the `QPushButton` that are used +for the media actions (play, pause, stop, and so on) are using the +default icons meant for such actions. + +.. image:: player.png + :alt: Multimedia Player Qt Example + +You could make the application more attractive by designing the icons, +but in case you don't want to design them, `download the following set`_ +and use them. + +.. image:: icons.png + :alt: New Multimedia icons + +You can find more information about the `rcc` command, and `.qrc` file +format, and the resource system in general in the `Qt Resource System`_ +site. + +.. _`download the following set`: icons/ + + +The `.qrc` file +================ + +Before running any command, add information about the resources to a `.qrc` +file. +In the following example, notice how the resources are listed in `icons.qrc` + +:: + + </ui> + <!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>icons/play.png</file> + <file>icons/pause.png</file> + <file>icons/stop.png</file> + <file>icons/previous.png</file> + <file>icons/forward.png</file> + </qresource> + </RCC> + + +Generating a Python file +========================= + +Now that the `icons.qrc` file is ready, use the `pyside6-rcc` tool to generate +a Python class containing the binary information about the resources + +To do this, we need to run:: + + pyside6-rcc icons.rc -o rc_icons.py + +The `-o` option lets you specify the output filename, +which is `rc_icons.py` in this case. + +To use the generated file, add the following import at the top of your main Python file:: + + import rc_icons + + +Changes in the code +=================== + +As you are modifying an existing example, you need to modify the following +lines: + +.. code-block:: python + + from PySide6.QtGui import QIcon, QKeySequence + playIcon = self.style().standardIcon(QStyle.SP_MediaPlay) + previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward) + pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause) + nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward) + stopIcon = self.style().standardIcon(QStyle.SP_MediaStop) + +and replace them with the following: + +.. code-block:: python + + from PySide6.QtGui import QIcon, QKeySequence, QPixmap + playIcon = QIcon(QPixmap(":/icons/play.png")) + previousIcon = QIcon(QPixmap(":/icons/previous.png")) + pauseIcon = QIcon(QPixmap(":/icons/pause.png")) + nextIcon = QIcon(QPixmap(":/icons/forward.png")) + stopIcon = QIcon(QPixmap(":/icons/stop.png")) + +This ensures that the new icons are used instead of the default ones provided +by the application theme. +Notice that the lines are not consecutive, but are in different parts +of the file. + +After all your imports, add the following + +.. code-block:: python + + import rc_icons + +Now, the constructor of your class should look like this: + +.. code-block:: python + + def __init__(self): + super(MainWindow, self).__init__() + + self.playlist = QMediaPlaylist() + self.player = QMediaPlayer() + + toolBar = QToolBar() + self.addToolBar(toolBar) + + fileMenu = self.menuBar().addMenu("&File") + openAction = QAction(QIcon.fromTheme("document-open"), + "&Open...", self, shortcut=QKeySequence.Open, + triggered=self.open) + fileMenu.addAction(openAction) + exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", + self, shortcut="Ctrl+Q", triggered=self.close) + fileMenu.addAction(exitAction) + + playMenu = self.menuBar().addMenu("&Play") + playIcon = QIcon(QPixmap(":/icons/play.png")) + self.playAction = toolBar.addAction(playIcon, "Play") + self.playAction.triggered.connect(self.player.play) + playMenu.addAction(self.playAction) + + previousIcon = QIcon(QPixmap(":/icons/previous.png")) + self.previousAction = toolBar.addAction(previousIcon, "Previous") + self.previousAction.triggered.connect(self.previousClicked) + playMenu.addAction(self.previousAction) + + pauseIcon = QIcon(QPixmap(":/icons/pause.png")) + self.pauseAction = toolBar.addAction(pauseIcon, "Pause") + self.pauseAction.triggered.connect(self.player.pause) + playMenu.addAction(self.pauseAction) + + nextIcon = QIcon(QPixmap(":/icons/forward.png")) + self.nextAction = toolBar.addAction(nextIcon, "Next") + self.nextAction.triggered.connect(self.playlist.next) + playMenu.addAction(self.nextAction) + + stopIcon = QIcon(QPixmap(":/icons/stop.png")) + self.stopAction = toolBar.addAction(stopIcon, "Stop") + self.stopAction.triggered.connect(self.player.stop) + playMenu.addAction(self.stopAction) + + # many lines were omitted + +Executing the example +===================== + +Run the application by calling `python main.py` to checkout the new icon-set: + +.. image:: player-new.png + :alt: New Multimedia Player Qt Example + +.. _`Qt Resource System`: https://doc.qt.io/qt-5/resources.html diff --git a/sources/pyside6/doc/tutorials/basictutorial/style.qss b/sources/pyside6/doc/tutorials/basictutorial/style.qss new file mode 100644 index 000000000..b84b98f05 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/style.qss @@ -0,0 +1,23 @@ +QListWidget { + color: #FFFFFF; + background-color: #33373B; +} + +QListWidget::item { + height: 50px; +} + +QListWidget::item:selected { + background-color: #2ABf9E; +} + +QLabel { + background-color: #FFFFFF; + qproperty-alignment: AlignCenter; +} + +QPushButton { + background-color: #2ABf9E; + padding: 20px; + font-size: 18px; +} diff --git a/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst b/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst new file mode 100644 index 000000000..50bb2514c --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/uifiles.rst @@ -0,0 +1,187 @@ +Using `.ui` files from Designer or QtCreator with `QUiLoader` and `pyside6-uic` +******************************************************************************* + +This page describes the use of Qt Creator to create graphical +interfaces for your Qt for Python project. +You will need **Qt Creator** to design and modify your interface (UI file). + +If you don't know how to use Qt Creator, refer to the +`Using Qt Designer <http://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_ +documentation page. + +At Qt Creator, create a new Qt Design Form, choose "Main Window" for template. +And save 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 +=================================== + +Another option 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("Cannot open {}: {}".format(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 diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgets.rst b/sources/pyside6/doc/tutorials/basictutorial/widgets.rst new file mode 100644 index 000000000..d86ba2623 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgets.rst @@ -0,0 +1,45 @@ +Your First QtWidgets Application +********************************* + +As with any other programming framework, +you start with the traditional "Hello World" program. + +Here is a simple example of a Hello World application in PySide6: + +.. code-block:: python + + import sys + from PySide6.QtWidgets import QApplication, QLabel + + app = QApplication(sys.argv) + label = QLabel("Hello World!") + label.show() + app.exec_() + + +For a widget application using PySide6, you must always start by +importing the appropriate class from the `PySide6.QtWidgets` module. + +After the imports, you create a `QApplication` instance. As Qt can +receive arguments from command line, you may pass any argument to +the QApplication object. Usually, you don't need to pass any +arguments so you can leave it as is, or use the following approach: + +.. code-block:: python + + app = QApplication([]) + +After the creation of the application object, we have created a +`QLabel` object. A `QLabel` is a widget that can present text +(simple or rich, like html), and images: + +.. code-block:: python + + # This HTML approach will be valid too! + label = QLabel("<font color=red size=40>Hello World!</font>") + +.. note:: After creating the label, we call `show()` on it. + +Finally, we call `app.exec_()` to enter the Qt main loop and start +to execute the Qt code. In reality, it is only here where the label +is shown, but this can be ignored for now. diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png Binary files differnew file mode 100644 index 000000000..c30dd621b --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-no.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png Binary files differnew file mode 100644 index 000000000..eb90e216d --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-no.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png Binary files differnew file mode 100644 index 000000000..5a714977e --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-simple-yes.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png Binary files differnew file mode 100644 index 000000000..8ba49bd26 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling-yes.png diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py new file mode 100644 index 000000000..94e44c5c5 --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.py @@ -0,0 +1,95 @@ +############################################################################# +## +## Copyright (C) 2020 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +import sys + +from PySide6.QtCore import Qt +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QListWidget, + QListWidgetItem, QPushButton, QVBoxLayout, + QWidget) + +_placeholder = """ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim +veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea +commodo consequat. Duis aute irure dolor in reprehenderit in voluptate +velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint +occaecat cupidatat non proident, sunt in culpa qui officia deserunt +mollit anim id est laborum +""" + + +class Widget(QWidget): + def __init__(self, parent=None): + super(Widget, self).__init__(parent) + + menu_widget = QListWidget() + for i in range(10): + item = QListWidgetItem("Item {}".format(i)) + item.setTextAlignment(Qt.AlignCenter) + menu_widget.addItem(item) + + text_widget = QLabel(_placeholder) + button = QPushButton("Something") + + content_layout = QVBoxLayout() + content_layout.addWidget(text_widget) + content_layout.addWidget(button) + main_widget = QWidget() + main_widget.setLayout(content_layout) + + layout = QHBoxLayout() + layout.addWidget(menu_widget, 1) + layout.addWidget(main_widget, 4) + self.setLayout(layout) + + +if __name__ == "__main__": + app = QApplication() + + w = Widget() + w.show() + + _style = None + with open("style.qss", "r") as f: + _style = f.read() + app.setStyleSheet(_style) + + sys.exit(app.exec_()) diff --git a/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst new file mode 100644 index 000000000..8deef1d7f --- /dev/null +++ b/sources/pyside6/doc/tutorials/basictutorial/widgetstyling.rst @@ -0,0 +1,169 @@ +Widget Styling +************** + +Qt Widgets application use a default theme depending on the platform. +In some cases, there are system-wide configurations that modify the Qt theme, +and applications are displayed differently. + +However, you can take care of your own widgets and provide a custom style +to each component. As an example, look at the following simple snippet: + +.. code-block:: python + + import sys + from PySide6.QtCore import Qt + from PySide6.QtWidgets import QApplication, QLabel + + if __name__ == "__main__": + app = QApplication() + w = QLabel("This is a placeholder text") + w.setAlignment(Qt.AlignCenter) + w.show() + sys.exit(app.exec_()) + +When you execute this code, you will see a simple `QLabel` aligned at the +center, and with a placeholder text. + +.. image:: widgetstyling-simple-no.png + :alt: Simple Widget with no style + +You can style your application using the CSS-like syntax. +For more information, see `Qt Style Sheets Reference`_. + +A `QLabel` can be styled differently by setting some of its CSS +properties, such as `background-color` and `font-family`, +so let's see how does the code look like with these changes: + +.. code-block:: python + + import sys + from PySide6.QtCore import Qt + from PySide6.QtWidgets import QApplication, QLabel + + if __name__ == "__main__": + app = QApplication() + w = QLabel("This is a placeholder text") + w.setAlignment(Qt.AlignCenter) + w.setStyleSheet(""" + background-color: #262626; + color: #FFFFFF; + font-family: Titillium; + font-size: 18px; + """) + w.show() + sys.exit(app.exec_()) + +Now when you run the code, notice that the `QLabel` looks different with your +custom style: + +.. image:: widgetstyling-simple-yes.png + :alt: Simple Widget with Style + + +.. note:: + + If you don't have the font `Titillium` installed, you can try with any + other you prefer. + Remember you can list your installed fonts using `QFontDatabase`, + specifically the `families()` method. + + +Styling each UI element separately like you did in the previous snippet is a +lot of work. The easier alternative for this is to use Qt Style Sheets, +which is one or more `.qss` files defining the style for the UI elements in +your application. + +More examples can be found in the `Qt Style Sheet Examples`_ documentation +page. + + +.. _`Qt Style Sheets Reference`: https://doc.qt.io/qt-5/stylesheet-reference.html +.. _`Qt Style Sheet Examples`: https://doc.qt.io/qt-5/stylesheet-examples.html + +Qt Style Sheets +=============== + +.. warning:: + + Before starting modifying your application, keep in mind that you will be + responsible for all the graphical details of the application. + Altering margins, and sizes might end up looking strange or incorrect, so you + need to be careful when altering the style. + It's recommended to create a full new Qt style to cover all the possible + corner cases. + +A `qss` file is quite similar to a CSS file, but you need to specify the Widget +component and optionally the name of the object:: + + QLabel { + background-color: red; + } + + QLabel#title { + font-size: 20px; + } + +The first style defines a `background-color` for all `QLabel` objects in your +application, whereas the later one styles the `title` object only. + +.. note:: + + You can set object names with the `setObjectName(str)` function to any Qt + object, for example: for a `label = QLabel("Test")`, you can write + `label.setObjectName("title")` + + +Once you have a `qss` file for your application, you can apply it by reading +the file and using the `QApplication.setStyleSheet(str)` function: + +.. code-block:: python + + if __name__ == "__main__": + app = QApplication() + + w = Widget() + w.show() + + with open("style.qss", "r") as f: + _style = f.read() + app.setStyleSheet(_style) + + sys.exit(app.exec_()) + +Having a general `qss` file allows you to decouple the styling aspects of +the code, without mixing it in the middle of the general functionality, and you +can simply enable it or disable it. + +Look at this new example, with more widgets components: + +.. literalinclude:: widgetstyling.py + :linenos: + :lines: 59-81 + +This displays a two column widget, with a `QListWidget` on the left and a +`QLabel` and a `QPushButton` on the right. It looks like this when you run the +code: + +.. image:: widgetstyling-no.png + :alt: Widget with no style + +If you add content to the previously described `style.qss` file, you can modify +the look-n-feel of the previous example: + +.. literalinclude:: style.qss + :linenos: + +The style changes mainly the color of the different widgets, alter the +alignment, and includes some spacing. +You can also use state-based styling on the QListWidget *items* for example, to +style them differently depending on whether they are *selected* or not. + +After applying all the styling alternatives you explored in this topic, notice +that the `QLabel` example looks a lot different now. +Try running the code to check its new look: + +.. image:: widgetstyling-yes.png + :alt: Widget with style + +You have the freedom to tune your style sheets and provide a really nice +look-n-feel to all your applications. |