#qt
#qt
Вопрос:
Qt имеет набор графических классов, которые представлены в 2 вариантах: с целочисленной точностью и точностью с плавающей запятой.
Это те, которые я помню
| QLine | QLineF |
| QMargins | QMarginsF |
| QPoint | QPointF |
| QRect | QRectF |
| QSize | QSizeF |
Помимо очевидного различия в том, что один использует целые числа, а другой использует числа с плавающей запятой, как указано в их названиях и в официальной документации, у меня есть немало сомнений…
- Каковы варианты использования для одного семейства классов и другого?
- Позиции и размеры больше, чем их целочисленный аналог?
- Дробные значения?
- Имеет ли это значение при рисовании?
- Будут ли графики более плавными, если я использую QLineF вместо QLine?
Комментарии:
1. Да, вы можете размещать объекты на субпиксельных границах, используя значения fp.
2. Хорошо, это имеет немного больше смысла, но в документах Qt этого немного не хватает.
Ответ №1:
Хотя эти классы часто взаимозаменяемы и имеют почти одинаковые реализации, существуют особые различия в их использовании и результате.
Классы на основе целых чисел в основном используются для определения координат экрана (положения виджетов, размеров и т. Д.), Которые обычно рассматриваются на основе единиц измерения в пикселях (которые, очевидно, являются целыми числами).
Однако существуют важные различия при работе с позиционированием / столкновением и рисованием.
Рассмотрим следующее:
>>> p = QtCore.QPoint(1, 1)
>>> r = QtCore.QRect(0, 0, 1, 1)
>>> print(r.contains(p))
False
>>> r = QtCore.QRectF(0, 0, 1, 1)
>>> print(r.contains(p))
True
Это связано с тем, что QRect учитывает только целочисленный («пиксельный») размер и, очевидно (1, 1)
, находится на «другом пикселе».
QRect также характерен для функций right()
and bottom()
, потому что (как объясняется в документации) они всегда возвращают left width-1
and top height-1
по историческим причинам:
>>> r = QtCore.QRect(0, 0, 1, 1)
>>> print(r.right())
0
В свете этого всегда имейте в виду, что то же самое работает для установщиков этих координат (как set*
и move*
):
>>> r.setRight(1)
>>> print(r)
PyQt5.QtCore.QRect(0, 0, 2, 1)
>>> r.moveRight(0)
>>> print(r)
PyQt5.QtCore.QRect(-1, 0, 2, 1)
Классы с плавающей запятой также имеют функции, недоступные для классов, основанных на целых числах (в основном потому, что их реализация была бы невозможной / полезной / разумной).
Например, класс QLineF:
- может возвращать точку пересечения с другим QLineF (или пересечение их расширения);
- имеет функции для получения и установки угла самой линии (от ее
p1
начальной точки) или угла, созданного с помощью другой линии (или, лучше, их расширений, если они не пересекаются) [1]; - может быть создан из полярного, с заданной длиной и углом;
Классы с плавающей запятой позволяют более точно рисовать и позиционировать, что является важным аспектом, когда вам нужно сглаживание рисования или вы имеете дело с контентом, который можно масштабировать или основанным на пропорциональных значениях (рассмотрим увеличенный QGraphicsScene или отображение текста, поскольку шрифты основаны на векторах).
Например, следующее даст вам оченьразные результаты:
painter.setRenderHints(painter.Antialiasing)
painter.drawRect(QtCore.QRect(1, 1, 2, 2))
painter.drawRect(QtCore.QRectF(1, 1, 2.5, 2.5))
Затем важно помнить, что все простые функции рисования QPainter, которые принимают числовые значения в качестве основных параметров, всегда будут использовать целочисленные значения (я полагаю, это связано с динамической типизацией Python, поскольку функции C принимают только целые числа со знаком):
painter.drawRect(0.5, 0.5, 5.5, 5.5)
# same as:
painter.drawRect(0, 0, 5, 5)
# so you should use:
painter.drawRect(QtCore.QRectF(0.5, 0.5, 5.5, 5.5))
Наконец, хотя Qt (и Python) иногда допускают прозрачное использование обоих типов, в общем случае строго требуется один или другой:
- все функции, связанные с геометрией виджета, принимают только целочисленные классы (
setGeometry(QRect)
,resize(QSize)
, и т.д.); - то же самое касается переопределений функций, которые должны возвращать значения геометрии, такие как
sizeHint()
модельSizeHintRole
элемента, прямоугольники, возвращаемые подклассом QStyle или set для QStyleOption; - QRegion может принимать только QPolygon и QRect, поскольку это объект с отображением в пикселях;
- QGraphicsRectItem, QGraphicsEllipseItem и QGraphicsPolygonItem разрешают только классы с плавающей запятой в своих конструкторах или установщиках;
- сложные конструкторы используют классы своего типа точности (QRect не принимает QPointF или QSizeF и т. Д.);
- все функции, которые отображают координаты QGraphicsScene, должны использовать целочисленные классы при отображении на сцену и с плавающей запятой при выводе из сцены;
Всякий раз, когда вам нужно преобразование из одного типа в другой, просто используйте конструктор класса с плавающей запятой или to[*]
функцию, которую он предоставляет:
intRect = QtCore.QRect(0, 0, 10, 10)
floatRect = QtCore.QRectF(intRect)
newIntRect = floatRect.toRect()
intTopLeft = floatRect.topLeft().toPoint()
intSize = floatRect.size().toSize()
midRadiusLine = QtCore.QLineF.fromPolar(
intRect.width() * .5, 45).translated(floatRect.center())
intMidRadiusLine = midRadius.toLine()
[1] Имейте в виду, что установка углов за пределами диапазона 0-360 может дать вам неожиданные результаты: line.setAngle(361)
приведет к line.angle()
значению, равному 0,9999999999999748 из-за точности с плавающей запятой «[im]» и характера числа Pi.