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

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

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

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

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

Предпосылки

  1. Вы уже должны знать некоторые основы Python
  2. Вы должны знать, как устанавливать пакеты или библиотеки с помощью pip
  3. Конечно, у вас уже должен быть установлен python.

Установка

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

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

>>> pip install PyQt5

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

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

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

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

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

main.py

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

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

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

main.qml

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

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

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

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

>>> cd helloApp

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

>>> python main.py

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

Обновить интерфейс

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

import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
    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"
            }
        }
    }
}

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

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

См. также:  Как динамически установить значение по умолчанию в WTForms RadioField?

В своем qml-коде обновите тип Текст, включив якоря, как показано ниже:

            ...
            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

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

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

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

main.qml

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

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

main.qml

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

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

main.py

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

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

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

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

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

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

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

main.py

...
from from PyQt5.QtCore import QObject, pyqtSignal

class Backend(QObject):
    def __init__(self):
        QObject.__init__(self)
...

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

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

main.qml

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

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

См. также:  Культ мастерства и как слова «я не знаю» могут вас спасти

В питоне передайте это

main.py

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

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

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

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

main.qml

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

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

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

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

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

...
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)
    ...

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

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

main.qml

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

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

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

main.py

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

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

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

Готово !!!

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

>>> python main.py

Бонус:

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

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

main.qml

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

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

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

>>> python main.py

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

main.py

import sys
from time import strftime, gmtime
import threading
from time import sleep
from 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())

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
    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, поскольку сборка является частью бонусного раздела, мы не устанавливали ее раньше.

>>> pip install pyinstaller

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

>>> pyinstaller main.py

Вместо этого сначала сделайте:

>>> pyi-makespec main.py

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

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

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

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

main.spec

...
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')

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

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

>>> pyinstaller main.spec

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

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

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

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

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

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

Дополнительные ресурсы можно найти на:

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

Понравилась статья? Поделиться с друзьями:
IT Шеф
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: