Предпосылка: то, что вы привели в качестве примера, кажется не очень хорошей вещью. Это также кажется скорее глюком, чем «функцией», и добавление таких «невидимых» строк может привести к раздражающему GUI для пользователя. Единственный сценарий, в котором я бы использовал его, был бы чисто графическим / причудливым, для которого вы действительно хотите создать «сбой» по некоторым причинам. Также обратите внимание, что следующие решения не просты, и их использование требует от вас продвинутого уровня навыков и опыта работы с Qt, потому что, если вы действительно не понимаете, что происходит, вы наверняка столкнетесь с ошибками или неожиданные результаты, которые будет очень трудно исправить.
Сейчас. На самом деле вы не можете «нарисовать невидимую линию», но есть определенные обходные пути, которые могут дать вам аналогичный результат, в зависимости от ситуации.
Основная проблема в том, что происходит рисование (по крайней мере, на Qt) от «низа» каждого виджета, и каждый дочерний виджет закрашивается поверх предыдущего процесса рисования в обратном порядке расположения: если у вас есть виджеты, которые перекрываются, самый верхний закрашивает другой. Это более ясно, если у вас есть контейнерный виджет (например, QFrame или QGroupBox) с цветом фона, а его дочерние элементы используют другой: фон дочерних элементов будет закрашен поверх родительского.
(Теоретически) самое простое решение - добавить дочерний виджет, который не , добавлен в главный менеджер компоновки виджетов.
Два важных примечания:
- Следующее будет работать только в случае применения к верхнему виджету, к которому должна быть применена «невидимая линия».
- Если виджет, к которому вы применяете это , а не верхний уровень окно, линия, вероятно, будет не действительно невидимой.
class TestWithChildLine(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
for row in range(3):
for col in range(6):
layout.addWidget(QtWidgets.QDial(), row, col)
# create a widget child of this one, but *do not add* it to the layout
self.invisibleWidget = QtWidgets.QWidget(self)
# ensure that the widget background is painted
self.invisibleWidget.setAutoFillBackground(True)
# and that it doesn't receive mouse events
self.invisibleWidget.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
def resizeEvent(self, event):
super().resizeEvent(event)
# create a rectangle that will be used for the "invisible" line, wide
# as the main widget but with 10 pixel height, then center it
rect = QtCore.QRect(0, 0, self.width(), 10)
rect.moveCenter(self.rect().center())
# set the geometry of the "invisible" widget to that rectangle
self.invisibleWidget.setGeometry(rect)
К сожалению, у этого подхода есть большая проблема: если цвет фона имеет альфа-компонент или использует растровое изображение (как это делают многие стили, и у вас есть NO элемент управления или доступ к нему), в результате не будет невидимой строкой.
Вот скриншот, сделанный с помощью стиль «Кислород» (для макета я установил интервал в 20 пикселей); как вы можете видеть, в стиле Oxygen dr aws пользовательский градиент для оконных фонов, который приведет к появлению «невидимой линии»:
Единственный простой способ для этого - установить фон с помощью таблиц стилей (изменение палитры недостаточно, поскольку стиль все равно будет использовать свой собственный способ рисования с использованием градиента, полученного из роли QPalette.Window):
self.invisibleWidget = QtWidgets.QWidget(self)
self.invisibleWidget.setObjectName('InvisibleLine')
self.invisibleWidget.setAutoFillBackground(True)
self.invisibleWidget.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
self.setStyleSheet('''
TestWithChildFull, #InvisibleLine {
background: lightGray;
}
''')
Селекторы необходимы, чтобы избежать распространения таблицы стилей на дочерние виджеты; Я использовал селектор «#» для определения имени объекта «невидимого» виджета.
Как вы можете видеть, теперь мы потеряли градиент, но результат работает, как и ожидалось:
Сейчас. Есть и другое, более сложное решение, но оно должно работать в любой ситуации, при условии, что вы все еще используете его в окне верхнего уровня.
Этот подход все еще использует технику дочерних виджетов, но использует QWidget.render()
для рисования текущего фона окна верхнего уровня на QPixmap, а затем установите это растровое изображение на дочерний виджет (который теперь является QLabel).
Хитрость заключается в использовании DrawWindowBackground
render flag , что позволяет рисовать виджет без дочерних элементов. Обратите внимание, что в этом случае я использовал черный фон, который показывает «более светлый» градиент на границах, которые лучше демонстрируют эффект:
class TestWithChildLabel(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
layout.setSpacing(40)
for row in range(3):
for col in range(6):
layout.addWidget(QtWidgets.QDial(), row, col)
self.invisibleWidget = QtWidgets.QLabel(self)
self.invisibleWidget.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
palette = self.palette()
palette.setColor(palette.Window, QtGui.QColor('black'))
self.setPalette(palette)
def resizeEvent(self, event):
super().resizeEvent(event)
pm = QtGui.QPixmap(self.size())
pm.fill(QtCore.Qt.transparent)
qp = QtGui.QPainter(pm)
maskRect = QtCore.QRect(0, 0, self.width(), 50)
maskRect.moveTop(50)
region = QtGui.QRegion(maskRect)
self.render(qp, maskRect.topLeft(), flags=self.DrawWindowBackground,
sourceRegion=region)
qp.end()
self.invisibleWidget.setPixmap(pm)
self.invisibleWidget.setGeometry(self.rect())
И вот результат:
Наконец, еще одна альтернатива - вручную применить маску к каждому дочернему виджету в соответствии с их положением. Но это может стать очень трудным (и, возможно, трудным для управления / отладки), если у вас сложные макеты или большое количество дочерних элементов, так как вам нужно будет установить (или отменить) маску для всех прямых дочерних элементов каждый время, когда происходит событие изменения размера. Я не буду демонстрировать этот сценарий, так как считаю, что он слишком сложный и ненужный.