#python #pyqt5
#python #pyqt5
Вопрос:
Я пытаюсь реализовать сетку изображений с заголовками под ними, с тенью при наведении курсора мыши. Что я сделал до сих пор, так это добавил тень на два виджета (ярлык с изображением и ярлык с заголовком), но я хотел бы иметь тень на прямоугольной области, которая их содержит. Он попытался поместить их в другой виджет и применить эффект к этому виджету, но он по-прежнему применяется к обеим меткам. Код ниже.
import sys, os
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'PyQt5 layout - pythonspot.com'
self.left = 100
self.top = 100
self.width = 800
self.height = 600
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
content_widget = QWidget()
self.setCentralWidget(content_widget)
self._lay = QGridLayout(content_widget)
self.shadow = QGraphicsDropShadowEffect(self)
self.shadow.setBlurRadius(5)
nb = 6
i = 0
for i in range(0, 12):
panel=QWidget()
vbox = QVBoxLayout()
pixmap = QPixmap(str(i 1) "jpg")
pixmap = pixmap.scaled(100, 150, transformMode=Qt.SmoothTransformation)
img_label = QLabel(pixmap=pixmap)
vbox.addWidget(img_label)
txt_label = QLabel(str(i 1))
vbox.addWidget(txt_label)
vbox.addStretch(1)
panel.setLayout(vbox)
self._lay.addWidget(panel , int(i/nb), i%nb)
panel.installEventFilter(self)
i = i 1
self.show()
def eventFilter(self, object, event):
if event.type() == QEvent.Enter:
object.setGraphicsEffect(self.shadow)
self.shadow.setEnabled(True)
elif event.type() == QEvent.Leave:
print("Mouse is not over the label")
self.shadow.setEnabled(False)
return False
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Комментарии:
1. Вы имеете в виду, что вам нужна тень для всего контейнера (
panel
), например? i.stack.imgur.com/gnWL8.png2. @musicamante Да, это то, что я имею в виду
3. Есть ли конкретная причина для того, чтобы не устанавливать отдельные graphicseffect для всех виджетов?
Ответ №1:
Проблема заключается в том, что обычный QWidget обычно сам по себе ничего не рисует: это просто прозрачный виджет. Если вы примените графический эффект, он будет результатом того, что находится внутри этого виджета.
Решение состоит в том, чтобы убедиться, что виджет непрозрачен, путем вызова setAutoFillBackground(True)
.
К сожалению, особенно в вашем случае, результат будет не очень хорошим, потому что у вас много других виджетов и определенное расстояние между ними. В итоге у вас будет тень за всем:
Решением было бы вызывать raise_()
всякий раз, когда установлен графический эффект, чтобы убедиться, что виджет на самом деле выше всего остального (среди братьев и сестер и дочерних элементов его родителя, очевидно).
К сожалению, опять же, у этого есть небольшая, но важная проблема, связанная с вашей реализацией: в первый раз, когда эффект удаляется из виджета, потому что он установлен на другом, окружающие виджеты не обновляются правильно.
В основном это связано с оптимизацией движка рисования и реализацией графического эффекта.
Чтобы избежать этой проблемы, есть две возможности:
- установите уникальный графический эффект для каждого виджета, отключенный при создании, а затем включите его только на
enterEvent
:
class App(QMainWindow):
def __init__(self):
# ...
for i in range(0, 12):
panel = QWidget()
effect = QGraphicsDropShadowEffect(panel, enabled=False, blurRadius=5)
panel.setGraphicsEffect(panel)
# ...
def eventFilter(self, obj, event):
if event.type() == QEvent.Enter and obj.graphicsEffect():
obj.graphicsEffect().setEnabled(True)
elif event.type() == QEvent.Leave and obj.graphicsEffect():
obj.graphicsEffect().setEnabled(False)
return super().eventFilter(obj, event)
- в качестве альтернативы, получите ограничивающий прямоугольник графического эффекта, переведите его в координаты виджета, проверьте, пересекает ли какая-либо другая «родственная» геометрия ограничивающий прямоугольник и, в конечном итоге, вызовите
update()
эти виджеты:
class App(QMainWindow):
def __init__(self):
# ...
self.panels = []
for i in range(0, 12):
panel = QWidget()
self.panels.append(panel)
# ...
def eventFilter(self, obj, event):
if event.type() == QEvent.Enter:
obj.setGraphicsEffect(self.shadow)
self.shadow.setEnabled(True)
obj.raise_()
elif event.type() == QEvent.Leave:
obj.graphicsEffect().setEnabled(False)
rect = self.shadow.boundingRect().toRect()
rect.translate(obj.geometry().topLeft())
for other in self.panels:
if other != obj and other.geometry().intersects(rect):
other.update()
return super().eventFilter(obj, event)
PS: object
это встроенный тип Python, вы не должны использовать его в качестве переменной.
Комментарии:
1. Спасибо за вашу помощь! Первое решение не сработало для меня, у меня продолжает появляться эта странная перекрестная тень справа внизу, даже если между виджетами есть некоторое пространство. Однако второй вариант работал намного лучше. Я не уверен в этом, но кажется, что смещение применяется только к нижней правой части тени, но не эффективно в начале, я имею в виду, что тень начинается слишком близко к виджету. Также есть пробел под заголовком. Я понимаю, что это происходит из-за растянутого пространства, которое я добавил, чтобы заголовок прикреплялся к изображению. Я думаю, мне придется использовать другой макет, чем вертикальный прямоугольник.
2. Может ли другое решение прийти из QML? Я наткнулся на это: doc.qt.io/qt-5/qml-qtgraphicaleffects-rectangularglow.html Но не мог понять, как им пользоваться, и тоже не нашел ни одного полезного документа.
3. @Benco смещение является свойством эффекта (см. Документацию ), и по умолчанию оно составляет 8 пикселей в правом нижнем углу.