Ищите по всему Интернету использование языка программирования Python, и они перечисляют их с настольными приложениями, помеченными как не очень подходящие для python. Но много лет назад, в 2016 году, когда я надеялся перейти от веб-разработки к разработке программного обеспечения, Google.com сказал мне, что я должен выбрать python, потому что он используется для создания некоторых современных и передовых научных приложений, а затем они упомянули blender3d. Я знал blender3d, это потрясающая программа для создания 3D.

Но это не их вина, уродливые вещи, которые люди используют в качестве витрин для графического интерфейса Python, отвратительны, слишком стары и выглядят слишком устаревшими, ни одному молодому человеку это не понравится. Я надеюсь изменить это представление с помощью этого простого руководства по настольному приложению. Давайте идти.

Мы будем использовать PyQt (подробнее об этом позже) вместо Tkinter, который был почти исключен из списка стандартных библиотек Python за то, что он устарел.

Что такое PyQt (произносится: pie-cute). Это порт фреймворка Qt (произносится: мило) с C++, . Этот фреймворк известен как необходимый фреймворк для разработчиков C++. Это фреймворк для blender3d, Tableau, Telegram, Anaconda Navigator, Ipython, Jupyter Notebook, VirtualBox, VLC и т. д. Мы будем использовать его вместо смущающего Tkinter.

Единственное, что нам нужно будет установить, это PyQt. Итак, откройте свой терминал, в Windows это будет командная строка или Powershell.

Установка

  1. Вы должны знать, как устанавливать пакеты или библиотеки с помощью pip.
  2. Конечно, у вас уже должен быть установлен Python.
  3. «https://johnsoncountytaxoffice.org/tdn/video-jos-v-puv-bg-061.html»
    «https://johnsoncountytaxoffice.org/tdn/video-jos-v-puv-bg- 062.html»
    «https://johnsoncountytaxoffice.org/tdn/video-jos-v-puv-bg-063.html»
    «https://johnsoncountytaxoffice.org/tdn/video- jos-v-puv-bg-064.html»
    «https://johnsoncountytaxoffice.org/tdn/video-jos-v-puv-bg-065.html»
    «https:// johnsoncountytaxoffice.org/tdn/Jos-v-Pul-dazn-01.html»
    «https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-dazn-02.html»
    « https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-dazn-03.html»
    «https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-dazn-04.html»< br /> «https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-dazn-05.html»
    «https://johnsoncountytaxoffice.org/tdn/v-ideo-Joshua-orario-it -01.html»
    «https://johnsoncountytaxoffice.org/tdn/v-ideo-Joshua-orario-it-02.html»
    «https://johnsoncountytaxoffice.org/tdn/v -ideo-Joshua-orario-it-03.html»
    «https://johnsoncountytaxoffice.org/td n/v-ideo-Joshua-orario-it-04.html»
    «https://johnsoncountytaxoffice.org/tdn/v-ideo-Joshua-orario-it-05.html»
    « https://johnsoncountytaxoffice.org/tdn/Js-v-Pv-voir-fr-01.html»
    «https://johnsoncountytaxoffice.org/tdn/Js-v-Pv-voir-fr-02 .html»
    «https://johnsoncountytaxoffice.org/tdn/Js-v-Pv-voir-fr-03.html»
    «https://johnsoncountytaxoffice.org/tdn/Js-v -Pv-voir-fr-04.html»
    «https://johnsoncountytaxoffice.org/tdn/Js-v-Pv-voir-fr-05.html»
    «https://johnsoncountytaxoffice .org/tdn/Jos-v-Pul-es01.html»
    «https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-es02.html»
    «https://johnsoncountytaxoffice .org/tdn/Jos-v-Pul-es03.html»
    «https://johnsoncountytaxoffice.org/tdn/Jos-v-Pul-es04.html»
    «https://johnsoncountytaxoffice .org/tdn/Jos-v-Pul-es05.html»
    «https://johnsoncountytaxoffice.org/tdn/joshua-v-pulev-mma-01.html»
    «https:/ /johnsoncountytaxoffice.org/tdn/joshua-v-pulev-mma-02.html»
    «https://johnsoncountytaxoffice.org/tdn/jo shua-v-pulev-mma-03.html»
    «https://johnsoncountytaxoffice.org/tdn/joshua-v-pulev-mma-04.html»
    «https://johnsoncountytaxoffice. org/tdn/joshua-v-pulev-mma-05.html»
    «http://www.tenandahalf.co.uk/rex1/video-jos-v-puv-bg-061.html»< br /> «http://www.tenandahalf.co.uk/rex1/video-jos-v-puv-bg-062.html»
    «http://www.tenandahalf.co.uk/rex1 /video-jos-v-puv-bg-063.html»
    «http://www.tenandahalf.co.uk/rex1/video-jos-v-puv-bg-064.html»
    «http://www.tenandahalf.co.uk/rex1/video-jos-v-puv-bg-065.html»
    «http://www.tenandahalf.co.uk/rex1/ Jos-v-Pul-dazn-01.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-dazn-02.html»
    «http: //www.tenandahalf.co.uk/rex1/Jos-v-Pul-dazn-03.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-dazn -04.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-dazn-05.html»
    «http://www.tenandahalf.co .uk/rex1/v-ideo-Joshua-orario-it-01.html»
    «http://www.tenandahalf.co.uk/rex1/v-ideo-Joshua-orario-it-02. html»
    «http://www.tenandahalf.co.uk/rex1/v-ideo-Joshua-orario-it-03.html»
    «http://www.tenandahalf.co.uk/rex1/ v-ideo-Joshua-orario-it-04.html»
    «http://www.tenandahalf.co.uk/rex1/v-ideo-Joshua-orario-it-05.html»
    > «http://www.tenandahalf.co.uk/rex1/Js-v-Pv-voir-fr-01.html»
    «http://www.tenandahalf.co.uk/rex1/Js -v-Pv-voir-fr-02.html»
    «http://www.tenandahalf.co.uk/rex1/Js-v-Pv-voir-fr-03.html»
    «http://www.tenandahalf.co.uk/rex1/Js-v-Pv-voir-fr-04.html»
    «http://www.tenandahalf.co.uk/rex1/Js- v-Pv-voir-fr-05.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-es01.html»
    «http:// www.tenandahalf.co.uk/rex1/Jos-v-Pul-es02.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-es03.html»< br /> «http://www.tenandahalf.co.uk/rex1/Jos-v-Pul-es04.html»
    «http://www.tenandahalf.co.uk/rex1/Jos-v -Pul-es05.html»
    «http://www.tenandahalf.co.uk/rex1/joshua-v-pulev-mma-01.html»
    «http://www.tenandahalf .co.uk/rex1/joshua-v-pulev-mma-02.html»
    «http://www.tenandahalf.co.uk/rex1/joshua-v-pulev-mma-03.html»
    «http://www.tenandahalf.co.uk/rex1/joshua- v-pulev-mma-04.html»
    «http://www.tenandahalf.co.uk/rex1/joshua-v-pulev-mma-05.html»
    «http:// jurassicjaunts.co.uk/wod/Videos-Jo-v-po-esp-001.html»
    «http://jurassicjaunts.co.uk/wod/Videos-Jo-v-po-esp-002 .html»
    «http://jurassicjaunts.co.uk/wod/Videos-Jo-v-po-esp-003.html»
    «http://jurassicjaunts.co.uk/wod /Videos-Jo-v-po-esp-004.html»
    «http://jurassicjaunts.co.uk/wod/Videos-Jo-v-po-esp-005.html»
    «https://www.fondebucanero.com/sites/default/files/webform/contacto/videos-jo-v-box-sky-002.html»
    «https://www.fondebucanero.com/ site/default/files/webform/contacto/videos-jo-v-box-sky-003.html»
    «https://www.fondebucanero.com/sites/default/files/webform/contacto/videos -jo-v-box-sky-004.html»
    «https://www.fondebucanero.com/sites/default/files/webform/contacto/videos-jo-v-box-sky-009. html»
    «https://www.f

Файлы и папки проекта

Введите следующую команду в терминале

PyQt5, потому что мы загружаем версию 5 PyQt, точнее версию 5.15. Дождитесь завершения установки, это займет всего минуту или две.

>>> pip install PyQt5

Теперь, когда мы закончили установку. Мы должны начать с нашего проекта. Создайте папку проекта для приложения, назовем ее: helloApp. Создавайте его в любом месте на своем компьютере, но хорошо, когда он организован.

Давайте сделаем «Hello World»

Откройте main.py, желательно в vscode, и введите следующий код

Обновить пользовательский интерфейс

main.py

Вышеприведенный код вызывает QGuiApplication и QQmlApplicationEngine, которые будут использовать Qml вместо QtWidgets в качестве уровня пользовательского интерфейса для приложения Qt. Затем он соединяет функцию выхода слоев пользовательского интерфейса с основной функцией выхода приложения. Таким образом, оба могут закрыться, когда пользователь закроет пользовательский интерфейс. Затем он загружает файл qml в качестве кода qml для пользовательского интерфейса Qml. app.exec() — это то, что запускает приложение, оно находится внутри sys.exit, поскольку возвращает код выхода приложения, который передается в sys.exit, который выходит из системы python.

import sysfrom PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngineapp = QGuiApplication(sys.argv)engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('./UI/main.qml')sys.exit(app.exec())

Добавьте этот код в main.qml.

main.qml

Приведенный выше код создает окно, видимый код очень важен, без него пользовательский интерфейс будет работать, но будет невидимым, с шириной и высота как указано, с названием «HelloApp». А для текста, расположенного по центру родителя (которым оказывается окно), отображается текст «Hello World», размер 24 пикселя в пикселях.

import QtQuick 2.15
import QtQuick.Controls 2.15ApplicationWindow {
    visible: true
    width: 600
    height: 500
    title: "HelloApp"    Text {
        anchors.centerIn: parent
        text: "Hello World"
        font.pixelSize: 24
    }}

Если у вас есть вышеперечисленное, вы можете запустить его и увидеть результат.

Перейдите в папку helloApp

Теперь запустите его, выполнив:

>>> cd helloApp

Если ваш код работает, вы должны увидеть:

>>> python main.py

`Окно приложения с текстом «Hello World»

Теперь давайте немного обновим пользовательский интерфейс, добавим изображение в качестве фона и текст, который будет иметь время.

Используйте режим реального времени

В приведенном выше примере есть тип ApplicationWindow, внутри которого находится тип Rectangle, который на самом деле заполняет все пространство окна. Внутри него находится Изображение и другой Прямоугольник, который выглядит так же, как и рядом с ним, но из-за отсутствия типа макета он на самом деле находится поверх тип Изображение. Прямоугольник имеет прозрачный цвет, так как по умолчанию прямоугольники белые, внутри него текст, который читается как 16:38:33, смоделировать время.

import QtQuick 2.15
import QtQuick.Controls 2.15ApplicationWindow {
    visible: true
    width: 400
    height: 600
    title: "HelloApp"    Rectangle {
        anchors.fill: parent        Image {
            sourceSize.width: parent.width
            sourceSize.height: parent.height
            source: "./images/playas.jpg"
            fillMode: Image.PreserveAspectCrop        }        Rectangle {
            anchors.fill: parent
            color: "transparent"            Text {
                text: "16:38:33"
                font.pixelSize: 24
                color: "white"
            }        }    }
}

Если вы запустите приложение, текст появится в верхнем левом углу окна. Нам это не нравится, поэтому мы собираемся сделать так, чтобы он отображался в левом нижнем углу с некоторыми полями.

В коде qml обновите тип Text, включив в него якоря, как показано ниже:

Теперь запустите его, выполнив

...            Text {
                anchors {
                    bottom: parent.bottom
                    bottomMargin: 12
                    left: parent.left
                    leftMargin: 12                }
                text: "16:38:33"
                font.pixelSize: 24
                ...
            }            ...

Вы должны увидеть что-то похожее на это

>>> python main.py

Теперь я хотел бы время, чтобы обновить

Позволяет использовать реальное время. Python предоставляет нам нативные функции, которые дают нам все виды функций, связанных со временем и датой. Нам нужна строка с текущим временем. gmtime предоставляет глобальную структуру времени со всеми видами информации, а strftime создает определенные части времени в виде строки с помощью функции gmtime.

Обновите время

импортировать функции strftime и gmtime

main.py

Затем создайте строку времени в любом месте файла.

import sys
from time import strftime, gmtime...

main.py

%H, %M, %S указывают strftime, что мы хотим видеть часы (в 24-часовом формате), минуты и секунды. (Подробнее о кодах форматов для strftime «здесь»). Эта переменная будет передана на уровень qml.

curr_time = strftime("%H:%M:%S", gmtime())

Давайте создадим свойство в qml, которое мы можем использовать для получения строки времени. Эта переменная упрощает изменение времени. Назовем это свойство currTime

main.qml

Используйте это свойство в qml, поэтому, когда это значение изменится, все остальные места, где оно использовалось, также изменятся.

...ApplicationWindow {
    ...
    title: "HelloApp"    property string currTime: "00:00:00"
    ...

main.qml

Теперь отправьте нашу переменную curr_time, которую мы создали в python, в qml, установив ее в свойство qml currTime.

...Text {
    ...
    text: currTime  // used to be; text: "16:38:33"
    font.pixelSize: 48
    color: "white"
}...

main.py

Приведенный выше код устанавливает для свойства qml currTime значение свойства python curr_time. Это один из способов передачи информации из Python на уровень пользовательского интерфейса.

...engine.load('./UI/main.qml')
engine.rootObjects()[0].setProperty('currTime', curr_time)...

Запустите приложение, и вы не увидите никаких ошибок, а также увидите текущее время. Ура!!! Вперед!!!

Чтобы держать наше время в курсе. Нам нужно будет использовать нити. Многопоточность в python проста и понятна, мы будем использовать ее вместо многопоточности в Qt. Поток использует функции или поток вызывает функцию. Я предпочитаю, чтобы мы использовали технику Qt, известную как сигналы, это профессиональный метод, и его изучение сделает вашу работу лучше и проще. Давайте поместим наш текущий временной код в функцию, используя подчеркивание (_) для имени файла. Я объясню почему позже. Это не требование или что-то в этом роде, это просто хорошая практика

Бонус:

Чтобы использовать сигналы, нам пришлось бы создать подкласс QObject.

Создайте подкласс QObject, назовите его как хотите. Я назову это Backend.

main.py

Приведенный выше код импортирует QObject и pyqtSignal, в pyside это называется Signal. Это одно из немногих различий между pyqt и pyside.

...
from from PyQt5.QtCore import QObject, pyqtSignal
class Backend(QObject):    def __init__(self):
        QObject.__init__(self)
...

Формально у нас была строка свойства, которая получала нашу строку curr_time от python, теперь мы создаем свойство QtObject для получения объекта Backend от python. Типов не так много. Qml преобразует базовые типы Python в bool, int, double, string, list. , QtObject и var. var может обрабатывать все типы Python, но его меньше всего любят.

main.qml

Приведенный выше код создает QtObject backend для хранения нашего объекта python back_end. Используемые имена принадлежат мне, вы можете изменить их на любые, какие захотите

...
property string currTime: "00:00:00"
property QtObject backend
...

В питоне передать его

main.py

В приведенном выше коде объект back_end был создан из класса Backend. Затем мы устанавливаем его в свойство qml с именем backend.

...
engine.load('./UI/main.qml')
back_end = Backend()
engine.rootObjects()[0].setProperty('backend', back_end)
...

В Qml один QtObject может получать множество функций (называемых сигналами) от python, который делает множество вещей, но они должны быть организованы в этом QtObject.

Создайте тип Connections и настройте его на backend. Теперь внутри типа Connections может быть столько функций, сколько мы хотим получить для бэкенда.

main.qml

Вот как мы подключаемся к сигналам Python.

...
Rectangle {
    anchors.fill: parent    Image {
    ...
    }
    ...}Connections {
    target: backend
}
...

Если мы не используем многопоточность, наш пользовательский интерфейс зависнет. Достаточно решительно заявить, что здесь нам нужна многопоточность, а не многопроцессорность.

Создайте две функции, одну для потоковой передачи, другую для фактической функции. Вот где подчеркивание пригодится.

main.py

В приведенном выше коде есть функция подчеркивания, которая создает обновленное время.

...
import threading
from time import sleep
...
class Backend(QObject):
    def __init__(self):
        QObject.__init__(self)    def bootUp(self):
        t_thread = threading.Thread(target=self._bootUp)
        t_thread.daemon = True
        t_thread.start()    def _bootUp(self):
        while True:
            curr_time = strftime("%H:%M:%S", gmtime())
            print(curr_time)
            sleep(1)...

Создайте pyqtsignal с именем updated и вызовите его из функции с именем updater.

main.py

В приведенном выше коде pyqtSignal, обновленный, имеет в качестве параметра arguments список, содержащий имя функции «обновитель». От этой функции updater qml должен получать данные. В функции updater мы вызываем (излучаем) сигнал updated и передаем ему данные (curr_time). эм>

...
from PyQt5.QtCore import QObject, pyqtSignal...    def __init__(self):
        QObject.__init__(self)    updated = pyqtSignal(str, arguments=['updater'])    def updater(self, curr_time):
        self.updated.emit(curr_time)    ...

Обновите qml, получите сигнал, создав обработчик сигнала, имя обработчика сигнала — это заглавная форма имени сигнала, которому предшествует «on». Итак, «mySignal» становится «onMySignal», а «mysignal» становится «onMysignal».

main.qml

В приведенном выше коде вы можете видеть, что обработчик сигнала updated называется onUpdated. Ему также передается curr_time как msg.

...
    target: backend    function onUpdated(msg) {
        currTime = msg;
    }
...

Все хорошо, но мы еще не вызвали функцию updater. Для небольшого приложения нет необходимости в отдельной функции для вызова сигнала. Но в большом приложении это рекомендуемый способ сделать это. Измените секунды задержки на 1/10 секунды. Я нашел эту цифру лучшей для обновления времени.

main.py

Функцию bootUp следует вызывать сразу после загрузки пользовательского интерфейса.

...
            curr_time = strftime("%H:%M:%S", gmtime())
            self.updater(curr_time)
            sleep(0.1)
...

Все готово!!!

...
engine.rootObjects()[0].setProperty('backend', back_end)back_end.bootUp()sys.exit(app.exec())

Запустите код:

Ваши секунды должны обновиться сейчас

>>> python main.py

Вы можете сделать окно безрамочным и приклеить его в правый нижний угол экрана.

Сделать окно безрамным

Сборка и последующие шаги

main.qml

Приведенный выше код устанавливает x, y для окна и добавляет флаги, чтобы сделать окно безрамочным. Флаг Qt.Window гарантирует, что даже несмотря на то, что окно не содержит рамок, мы по-прежнему получаем кнопку "Задача"

...
height: 600
x: screen.desktopAvailableWidth - width - 12
y: screen.desktopAvailableHeight - height - 48
title: "HelloApp"flags: Qt.FramelessWindowHint | Qt.Window...

Запустите его, и вы должны быть довольны тем, что видите.

Наконец-то кодирование завершено, и вот окончательные коды.

>>> python main.py

main.py

main.qml

import sys
from time import strftime, gmtime
import threading
from time import sleepfrom PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import QObject, pyqtSignal
class Backend(QObject):
    def __init__(self):
        QObject.__init__(self)    updated = pyqtSignal(str, arguments=['updater'])    def updater(self, curr_time):
        self.updated.emit(curr_time)    def bootUp(self):
        t_thread = threading.Thread(target=self._bootUp)
        t_thread.daemon = True
        t_thread.start()    def _bootUp(self):
        while True:
            curr_time = strftime("%H:%M:%S", gmtime())
            self.updater(curr_time)
            sleep(0.1)
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('./UI/main.qml')back_end = Backend()engine.rootObjects()[0].setProperty('backend', back_end)back_end.bootUp()sys.exit(app.exec())

Помимо имен, которые вы, возможно, изменили, все должно быть похоже.

import QtQuick 2.15
import QtQuick.Controls 2.15ApplicationWindow {
    visible: true
    width: 360
    height: 600
    x: screen.desktopAvailableWidth - width - 12
    y: screen.desktopAvailableHeight - height - 48
    title: "HelloApp"    flags: Qt.FramelessWindowHint | Qt.Window    property string currTime: "00:00:00"
    property QtObject backend    Rectangle {
        anchors.fill: parent        Image {
            sourceSize.width: parent.width
            sourceSize.height: parent.height
            source: "./images/playas.jpg"
            fillMode: Image.PreserveAspectFit
        }        Text {
            anchors {
                bottom: parent.bottom
                bottomMargin: 12
                left: parent.left
                leftMargin: 12
            }
            text: currTime
            font.pixelSize: 48
            color: "white"
        }    }
    Connections {
        target: backend        function onUpdated(msg) {
            currTime = msg;
        }
    }}

Создание приложения pyqt может быть самым простым, поскольку оно широко известно.

Следующие шаги

Для сборки установите pyinstaller, так как сборка является частью раздела бонусов, мы не устанавливали его раньше.

Мы могли бы легко запустить следующий код в папке приложений (helloApp), но нам нужно позаботиться об используемых нами ресурсах.

>>> pip install pyinstaller

Вместо этого сначала выполните:

>>> pyinstaller main.py

Сначала он создает файл спецификаций для обновления, а затем снова запускает pyinstaller.

>>> pyi-makespec main.py

Параметр data можно использовать для включения файлов данных в ваше приложение или папку приложения. Это список кортежей, и кортеж всегда имеет два элемента: целевой путь, который мы будем включать, и целевой путь, где он должен храниться в папке приложения. Путь назначения должен быть относительным. Если вы хотите, чтобы он размещался прямо там с исполняемыми файлами приложения, вы делаете его пустой строкой (''), если вы хотите, чтобы он находился во вложенной папке внутри папки приложения, вы указываете вложенную папку ('nest/nested/really_nested ')

Обновите параметр datas, как показано ниже, чтобы он соответствовал пути к папке вашего пользовательского интерфейса helloApp на вашем компьютере.

Установите для параметра console значение False, поскольку это графический интерфейс, и мы его не тестируем.

main.spec

Параметр name в вызове EXE — это имя самого исполняемого файла. например. main.exe или main.dmg, но параметр name в вызове COLLECT указывает имя папки, в которой будет храниться исполняемый файл и все сопровождающие его файлы. быть изменен. Но имена были основаны на файле, который мы использовали для создания спецификации, помните: main.py.

...
a = Analysis(['main.py'],
             ...
             datas=[('I:/path/to/helloApp/UI', 'UI')],
             hiddenimports=[],
...
exe = EXE(pyz,
          a.scripts,
          [],
          ...
          name='main',
          debug=False,
          ...
          console=False )
coll = COLLECT(exe,
               ...
               upx_exclude=[],
               name='main')

Наконец, создайте приложение, используя

Теперь вы должны увидеть папку с именем «dist» с другой папкой внутри нее с именем «main» с файлами приложения. Найдите файл main.exe или основной исполняемый файл и запустите его. ТАДААА! И все хорошо.

>>> pyinstaller main.spec

Помимо того, что папка пользовательского интерфейса была включена и использовалась в приложении, все вещи, о которых мы говорили, используются в производстве. Ресурсы объединяются перед развертыванием в рабочей среде.

Вы уже должны знать некоторые основы Python.

Но Сигналы, как использовалось фоновое изображение, безрамное окно — все это приемы, используемые в производстве и, так сказать, в реальном мире. Просто это нечто большее. Да, в безрамочной Windows есть еще кое-что, вам нужно обрабатывать заголовок, изменение размера и перетаскивание окна, среди прочего, если вы не собираетесь использовать его в качестве заставки, не так сложно, но это выходит за рамки этого руководство.

Qml — это намного больше, чем просто изображения, прямоугольники и текст, а система макетов состоит из четырех типов. Их легко изучать, но лучше всего использовать практический подход, поэтому я не стал их объяснять.

Продолжайте работать с PyQt и Qml, это приведет к карьере в области разработки программного обеспечения, встроенных систем и в будущем визуализации данных. Предпочитайте его TKinter, его популярность растет с каждым днем.

Дополнительные ресурсы можно найти на: «riverbankcomputing.com», «evileg.com», Youtube, Udemy и даже на Qt «doc.qt.io».

Увидимся в следующий раз.

Как создать свое первое настольное приложение на Python