Вкратце: это было больно.
Предыстория
В комментариях к статье по основам питона спросили про графический интерфейс. До этого я как-то не писал GUI на Python, и тут такая возможность попробовать что-то новенькое. Существует несколько библиотек для этого, я остановился на PyQt5 (точнее PySide2, аналог PyQt5 под LGPL лицензией), потому что немного работал с простой Qt.
Run, button, run!
Делать простое окно с "Hello, world!" кажется слишком скучным, поэтому я обычно использую другой простой шаблон: на экране одна кнопка, при наведении курсора на эту кнопку она перемещается в случайное место.
Начнем с очень простой версии: есть окно, на нем одна кнопка, которая закрывает это окно.
Теперь допишем в класс RunnableButton механизм убегания:
Получилась такая простая программа с убегающей кнопкой.
С картинок сложно копировать, но там есть подстветка синтаксиса... Поэтому вот весь код в текстовом виде:
Вроде бы всё готово.
Но есть нюанс
Как пользователь сможет запустить это приложение без IDE?
Конечно, он может запустить с помощью питона, что-то типа
python run_button_run.py
Правда, для этого потребуется, чтобы был питон, да еще и с установленным PySide2 (а эта библиотека, например, не ставится в Anaconda, пока не добавишь канал conda-forge).
И вообще хотелось бы собрать исполняемый файл, а не давать конечному пользователю просто скрипт с кодом.
pyinstaller
Это утилита, которая умеет собирать исполняемый файл из python-скрипта. Просто устанавливаем её, запускаем
pyinstaller --onedir run_button_run.py
и оно работает. Ну или не работает, как повезет.
С приложением, которое написано выше было следующим образом. Первая компиляция создавала файл, но при его запуске ничего не происходило, точнее появлялось некое окно и через мгновение исчезало. Ну мы не первый день живем, поэтому пробую запуск через консоль и вижу ошибку.
Внимательный читатель мог заметить в коде выше два "лишних" импорта (QtCore и QtGui). Без этих импортов pyinstaller не видел эту зависимость и не копировал эти файлы в результирующий файл. Итогом была эта ошибка, поэтому появились эти "лишние" импорты.
Думаете всё? Теперь всё хорошо работает? Не совсем. Теперь ошибка, связанная с платформой:
qt.qpa.plugin: Could not find the Qt platform plugin "windows" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Однако, решение существует: нужно добавить в директорию, которую создает pyinstaller, добавить папку Platforms и туда скопировать qwindows.dll откуда-то из недр библиотек питона. Ну и делать это надо каждый раз при использование pyinstaller. На самом деле достаточно один раз написать маленький скрипт, который будет последовательно запускать pyinstaller и потом копировать нужный файл, но всё равно как-то грустно это.
В итоге, мне кажется, что это немного сложный способ написания графических приложений. Не думаю, что это проблема только pyinstaller, и надеюсь, что собранные мной грабли кому-то помогут на них не наступить.