Python Заметки
2.31K subscribers
59 photos
2 videos
2 files
211 links
Интересные заметки и обучающие материалы по Python

Контакт: @paulwinex

⚠️ Рекламу на канале не делаю!⚠️

Хештеги для поиска:
#tricks
#libs
#pep
#basic
#regex
#qt
#django
#2to3
#source
#offtop
Download Telegram
В фреймворке PyQt (и PySide тоже) часто встречается настройка чего-либо с помощью так называемых флагов.

widget.setWindowFlags(Qt.Window)

Взаимодействие нескольких флагов делается с помощью бинарных (или побитовых) операторов.
Несколько флагов можно указать с помощью оператора "|"

list_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)

исключить флаг из уже имеющегося набора можно так

list_item.setFlags(list_item.flags() ^ Qt.ItemIsEnabled)

Добавить новый флаг к имеющимся можно так

list_item.setFlags(list_item.flags() | Qt.ItemIsEnabled)

А проверка наличия делается так

is_enabled = item.flags() & Qt.ItemIsEnabled > 0

Почему именно так? Всё дело в том как именно работают побитовые операторы. Но об этом в следующем посте.

#qt
Когда разрабатываете свой GUI с помощью PyQt для какого-либо софта бывает необходимо позаимствовать цвета из текущего стиля интерфейса. Например, чтобы правильно раскрасить свои виджеты, подогнав их по цвету. Ведь бывает, что ваш GUI используется в разных софтах. Причём некоторые со светлой темой а другие с тёмной.
По умолчанию стили наследуются, но если вы задаёте какую-либо раскраску для части виджета через свой styleSheet, то требуется ссылаться на цвета текущего стиля.

Как это сделать? Как получить нужный цвет из палитры имеющегося стиля? Это достаточно просто, нужно использовать класс QPalette и его роли.

Например, мне нужно достать цвет текста из одного виджета и применить его в другом как цвет фона (не важно зачем именно так, просто захотелось😊).

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

from PySide2.QtGui import QPalette
color = main_window.palette().color(QPalette.Text)

теперь можем использовать этот цвет в стилях

my_widget.setStyleSheet(f'background-color: {color.name()};')

Готово, мы динамически переопределили дефолтный стиль используя текущий стиль окна!

На самом деле есть запись покороче, в одну строку и без лишних переменных. Не очень-то по правилам CSS, но Qt это понимает.

my_widget.setStyleSheet('background-color: palette(Text);')

Этот способ не подходит если вам нужно как-то модифицировать цвет перед применением в своих стилях. В этом случае потребуется первый способ.
Зато он прекрасно сработает в файле .qss, то есть не придётся в коде прописывать раскраску отдельных элементов через ссылки на палитру, всё красиво сохранится в отдельном файле .qss!

QListView#my_widget::item:selected {
background: palette(Midlight);
}

Про имеющиеся роли можно почитать здесь 🌍

#qt #tricks
Ещё немного про base64.

Собрал пример со встроенной в код картинкой. Это иконка для окна на PySide2. Файл кодирован в base64 и просто сохранён в переменной.

Для использования этих данных даже не пришлось сохранять их в новый файл. Иконка создаётся на лету с помощью метода QPixmap.loadFromData()

...
raw_data = base64.decodebytes(ico_encoded)
ico = QPixmap()
ico.loadFromData(raw_data, "PNG")
...

🌎 Полный пример смотрите в gists.

#libs #tricks #qt
This media is not supported in your browser
VIEW IN TELEGRAM
Есть у QLabel есть одна особенность. Её минимальный размер определяется текстом, который в неё записан. Это приводит к тому что длинный текст принудительно увеличивает ширину интерфейса.
В большинстве случаев это выглядит плохо.
Как с этим бороться?

🔸 Обрезать текст заранее, задав лимит по длине строки. В этом случае мы теряем часть визуальной информации. Не всегда угадаешь нужный размер. В разных OS шрифт используется разный.

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

🔸 Переопределить paintEvent() и сделать кастомный рендеринг текста. Можно, но слишком сложно для такой задачи.

Проще всего обрезать текст под текущий размер виджета используя класс QFontMetrics.
Он имеет готовый метод elidedText(), который просто вызываем по событию resizeEvent.
Я также добавил установку ToolTip чтобы всегда можно было увидеть полный текст при наведении курсора.

🌎 Код здесь

#qt #source
This media is not supported in your browser
VIEW IN TELEGRAM
Опубликовал для вас один из своих учебных проектов моего курса про PySide2 — LaunchPanel.
Это панель, которая выезжает сверху экрана когда к ней подводишь курсор. Содержит кнопки для запуска любых команд.

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

Но самое главное в этом проекте то, что почти каждая строка задокументирована! 😱

Что используется в проекте?

🔸 стилизация виджетов через StyleSheet
🔸 настройка отображения окна как панели без рамок, заголовка и всего остального
🔸 использование файлов конфига
🔸 HTML текст в виджетах
🔸 анимация свойств виджета (позиция и прозрачность)
🔸 реакция виджета на курсор
🔸 запуск подпроцессов

🌎 Исходники забираем здесь

Смотрите, изучайте, пользуйтесь 😉

#qt #source
А вы ждёте Qt6 как жду его я? )))
Наверняка, те кто ждёт, уже в курсе, но я уточню даты релизов.

- Qt 6.0 Feature freeze - 31.8.2020
- Qt 6.0 Alpha - 2.10.2020
- Qt 6.0 Beta 1 - 15.10.2020
- Qt 6.0.0 RC - 17.11.2020
- Qt 6.0.0 Final - 1.12.2020

Полный список https://wiki.qt.io/Qt_6.0_Release

Между тем, библиотеки PySide3 ждать не стоит. Дело в том, что разработчики решили синхронизировать версии библиотек C++ и Qt for Python.
Так что ждём сразу PySide6!

#qt
🙄 Разминка для ума!

Треугольник Серпинского, интересная фигура которую построить достаточно просто.

Алгоритм такой:

1. создаём любые 3 точки на плоскости
2. из этих точек случайно выбираем любую, как начальную
3. случайно выбираем любую точку из этих же трёх точек как цель
4. перемещаемся в сторону цели на половину расстояния
5. повторяем бесконечно с пункта 3

Если сделать достаточно много итераций то вырисовывается интересная фигура. Треугольник, в который вписаны более мелкие треугольники. Это самый настоящий фрактал!

Я собрал пример построения такой фигуры на базе Qt.
🌎 Код можно посмотреть здесь.

С помощью paintEvent я рисую точки по озвученному алгоритму. Каждые 10 секунд либо по клику на виджете строится следующий треугольник.

Особенности примера:

🔸 Атрибут Qt.WA_OpaquePaintEvent позволяет сохранить то, что было нарисовано в прошлой итерации. Таким образом мы видим постепенное наполнение точек а не мелькающую одну точку.
🔸 QTimer позволяет создавать отложенные вызовы один раз или с повторением через интервал.
🔸 QColor.fromHsv() позволяет создать рандомный но предсказуемый цвет с помощью HSV схемы. Не слишком светлый и не слишком тёмный но всегда с разный. Рандомизации подвергается только смещение по цветовому кругу (Hue), яркость (Value) и насыщенность (Saturation) можно контролировать отдельно в своих пределах или оставить статичными. Обычный рандом цвета по RGB не даёт такой предсказуемый результат.
🔸 Каждый новый цикл с новым треугольником предварительно затемняет предыдущие через этот вызов
painter.fillRect(rec, QColor(0, 0, 0, 100))
То есть полупрозрачный цвет. Таким образом, чем старше треугольник, тем он темней.
Если сделать виджет фулскрин, то у нас получится некий ScreenSaver)))
🔸 Да, я знаю, что рисование в Qt не самый лучший способ сделать этот пример) Скорее всего самый НЕподходящий. Попробуйте сделать тоже самое но другими средствами.

#qt #source #tricks
Для тех кто пишет расширения на PyQt/PySide для CG-софтов.

Когда я только начинал писать тулзы под Maya (тогда еще версия 2010-2011) мне приходилось ручками ставить PyQt4 под Maya. Даже написал мануалы по установке на своём сайте. Но потом стал доступен из коробки PySide и позже он обновится до PySide2. Для некоторых систем была поддержка PyQt5.
И как простому разработчику поддерживать этот зоопарк? Ведь хочется чтобы тул работал на любой версии (вы тоже делали модуль что-то типа import_qt.py? 😁)

На помощь приходит проект Qt.py который поставил себе цель унифицировать использование Qt-биндингов вне зависимости от среды где запускается код. Те, кто давно пишут на Qt, скорее всего знают этот проект.
Он стал стандартом для CG-индустрии и используется в топовых студиях и проектах.
Qt․py помогает запускать один и тот же код на разных платформах с разными вариантами Qt-библиотек. Это может быть как интеграция в CG-софт, так и переносимость стендалонов между разными платформами с разными версиями Python.

Я решил рассказать о некоторых особенностях работы с этой библиотекой.
Сегодня о том, как установить и использовать Qt․py и что это вам даёт.

Установка

pip install Qt.py

Чтобы начать использовать Qt․py в коде достаточно заменить импорт вашего варианта Qt-биндинга на Qt․py

from [PySide|PyQt4|PySide2|PyQt5] import QtWidgets
=>
from Qt import QtWidgets

Теперь ваш код будет поддерживать любой вариант биндинга Qt в Python.
При этом не потребуется использовать if-else конструкции под разные версии. Все вызовы теперь одинаковы.
Всё что нужно сделать, это написать его по правилам PySide2. Именно эта версия была взята за основу.

Приоритет импорта такой:

1. PySide2
2. PyQt5
3. PySide
4. PyQt4

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

>>> import Qt
>>> Qt.__binding__
'PySide2'

Приоритет имопрта можно изменить через переменные QT_PREFERRED_BINDING и QT_PREFERRED_BINDING_JSON. Причем под каждый проект оверрайды можно настраивать индивидеально.

#qt #libs
В прошлом посте говоря "Все вызовы теперь одинаковы" я несколько слукавил. Всё-таки есть в этом зоопарке версий некоторая несовместимость вызов которой просто так не унифицировать. Эти моменты вынесены в отдельный модуль QtCompat (compatibility). Там не так много функций но они довольно полезны.

Этот модуль содержит унификаци модуля shiboken2, функций loadUi, translate и несколько переименованных функций классов или изменённую сигнатуру аргументов и возвращаемых значений. Это единственное исключение из правила когда вам потребуется где-то изменить свой код кроме импортов и этот код не похож на обычный код PySide2.

Например, в PyQt4 и PySide есть метод

QHeaderView.setResizeMode

Для PyQt5 и PySide2 они были благополучно переименованы в

QHeaderView.setSectionResizeMode

Чтобы применить этот метод следует использовать такой код

from Qt import QtCompath

header = self.horizontalHeader()
QtCompat.QHeaderView.setSectionResizeMode(header, QtWidgets.QHeaderView.Fixed)

Унификация загрузки UI файлов:

# PySide2
from PySide2.QtUiTools import QUiLoader
loader = QUiLoader()
widget = loader.load(ui_file)

# PyQt5
from PyQt5 import uic
widget = uic.loadUi(ui_file)

# Qt.py
from Qt import QtCompat
widget = QtCompat.loadUi(ui_file)

Хорошо что таких моментов не много и их легко запомнить.
Полный список можно посмотреть в таблице.

#qt #tricks
Модуль Qt․py это не просто текстовый модуль, его компоненты генерируются на лету в зависимости от ситуации.
Поэтому ваша любимая IDE не сможет качественно сообразить автокомплиты под этот модуль.
Решение здесь более чем очевидно, надо сделать stubs-файлы. Это файлы с расширением .pyi, описывающие содержимое модуля но не имеющие рабочего кода.
Ну что, готовы потратить пару месяцев своей жизни чтобы описать все классы Qt и их методы? 😭
Расслабьтесь, за вас это уже сделали добрые люди.
Спасибо Fredrik Averpil !

Качаем здесь ⬇️
https://github.com/fredrikaverpil/Qt.py/tree/stubs/stubs/Qt

Не думаю что стоит устанавливать Qt․py из этого репозитория. Он там не обновляется. Так что забираем только файлы .pyi.
За актуальность этих файлов тоже не ручаюсь, но большинство методов там имеются.

Установка:

🔸 Вариант 1:
- находим куда установлен сам модуль Qt․py, это будет одинокий файл который так и называется Qt․py
- кидаем директорию рядом с ним (если есть доступ на запись). Должно получиться так:

📁 site-packages\
📄 Qt.py
📁 Qt\
...

🔸 Вариант 2
- копируем директорию Qt куда угодно
- пробиваем путь к ней в настройках энвайронмента в переменную PATH так, чтобы путь был ДО директории Qt.

Закинуть можно и в свою домашнюю директорию. Если скопируете сюда:

~/stubs/Qt

То переменную пишем так

export PATH=~/stubs:${PATH}

После этого IDE должна распарсить stubs-файлы и автокомплиты появятся 😎

#qt #libs #tricks