【问题标题】:PySide/PyQt Overlay widgetPySide/PyQt 覆盖小部件
【发布时间】:2017-10-31 01:59:13
【问题描述】:

我正在尝试在 PySide 中实现这样的目标:https://codepen.io/imprakash/pen/GgNMXO 我想要做的是创建一个无框的子窗口,下面有一个黑色的覆盖层。

我没有成功创建子窗口无框和覆盖...

这是复制 HTML 的基本代码:

from PySide import QtCore, QtGui
import sys

class MainWindow(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.resize(800, 500)

        self.button = QtGui.QPushButton("Click Me")

        self.setLayout(QtGui.QVBoxLayout())
        self.layout().addWidget(self.button)

        # Connections
        self.button.clicked.connect(self.displayOverlay)


    def displayOverlay(self):
        popup = QtGui.QDialog(self)
        popup.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        popup.setLayout(QtGui.QHBoxLayout())
        popup.layout().addWidget(QtGui.QLabel("HI"))
        popup.show()
        print "clicked"

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

如果您使用 FramelessWindowHint 注释该行,则窗口会出现,否则不会发生任何事情...

我真的希望有人可以帮助我。感谢您花时间阅读我的问题。

【问题讨论】:

  • 它可以完全复制 Codepen 示例,但它会比简单示例中的几个指令要复杂一些。构建一个 QWidget 并使用 FramelessWindowHint 设置它。重载paintEvent以绘制半透明背景(嗯...,可能需要WA_TranslucentBackground)。这个小部件将完全覆盖父级,因此也绘制弹出框(QPainter 中的 drawRect)。在没有布局的正确位置添加按钮。重载调整大小以确保按钮始终处于正确位置。重载父级调整大小以在打开时自动调整弹出窗口的大小。
  • 今天不好,但如果我看到你不会得到你的问题的答案,我会举一个小例子让我的解释更清楚一点。祝你好运。
  • 哇,谢谢你的解释,我从来没有看过Painter,所以对我来说很模糊......我会试试你说的,但是当你发布它时我也会得到你的例子:) 谢谢!
  • paintEvent 只是您手动控制小部件视觉效果的一种方式。在这种特殊情况下,您可以使用它来诱使用户看到禁用的深色父小部件和可爱的弹出窗口。我在我的推荐中添加了一个 sn-p。希望这足以让您入门。

标签: python pyqt pyside


【解决方案1】:

我将使用PyQt5 进行解释。它可能与 PySide(我不确定它是否仍在维护)和 PyQt4 有一些不同,但转换起来应该不会太难。

以下示例有一个包含几个按钮的父小部件。其中之一(显而易见的)要求弹出窗口。我已经准备好处理父级调整大小的示例,但没有编写任何关于拖动弹出窗口的鼠标事件的代码(参见mouseMoveEventmouseReleaseEvent)。

代码如下:

import sys
from PyQt5 import QtWidgets, QtCore, QtGui

class TranslucentWidgetSignals(QtCore.QObject):
    # SIGNALS
    CLOSE = QtCore.pyqtSignal()

class TranslucentWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(TranslucentWidget, self).__init__(parent)

        # make the window frameless
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)

        self.fillColor = QtGui.QColor(30, 30, 30, 120)
        self.penColor = QtGui.QColor("#333333")

        self.popup_fillColor = QtGui.QColor(240, 240, 240, 255)
        self.popup_penColor = QtGui.QColor(200, 200, 200, 255)

        self.close_btn = QtWidgets.QPushButton(self)
        self.close_btn.setText("x")
        font = QtGui.QFont()
        font.setPixelSize(18)
        font.setBold(True)
        self.close_btn.setFont(font)
        self.close_btn.setStyleSheet("background-color: rgb(0, 0, 0, 0)")
        self.close_btn.setFixedSize(30, 30)
        self.close_btn.clicked.connect(self._onclose)

        self.SIGNALS = TranslucentWidgetSignals()

    def resizeEvent(self, event):
        s = self.size()
        popup_width = 300
        popup_height = 120
        ow = int(s.width() / 2 - popup_width / 2)
        oh = int(s.height() / 2 - popup_height / 2)
        self.close_btn.move(ow + 265, oh + 5)

    def paintEvent(self, event):
        # This method is, in practice, drawing the contents of
        # your window.

        # get current window size
        s = self.size()
        qp = QtGui.QPainter()
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.Antialiasing, True)
        qp.setPen(self.penColor)
        qp.setBrush(self.fillColor)
        qp.drawRect(0, 0, s.width(), s.height())

        # drawpopup
        qp.setPen(self.popup_penColor)
        qp.setBrush(self.popup_fillColor)
        popup_width = 300
        popup_height = 120
        ow = int(s.width()/2-popup_width/2)
        oh = int(s.height()/2-popup_height/2)
        qp.drawRoundedRect(ow, oh, popup_width, popup_height, 5, 5)

        font = QtGui.QFont()
        font.setPixelSize(18)
        font.setBold(True)
        qp.setFont(font)
        qp.setPen(QtGui.QColor(70, 70, 70))
        tolw, tolh = 80, -5
        qp.drawText(ow + int(popup_width/2) - tolw, oh + int(popup_height/2) - tolh, "Yep, I'm a pop up.")

        qp.end()

    def _onclose(self):
        print("Close")
        self.SIGNALS.CLOSE.emit()


class ParentWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ParentWidget, self).__init__(parent)

        self._popup = QtWidgets.QPushButton("Gimme Popup!!!")
        self._popup.setFixedSize(150, 40)
        self._popup.clicked.connect(self._onpopup)

        self._other1 = QtWidgets.QPushButton("A button")
        self._other2 = QtWidgets.QPushButton("A button")
        self._other3 = QtWidgets.QPushButton("A button")
        self._other4 = QtWidgets.QPushButton("A button")

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(self._popup)
        hbox.addWidget(self._other1)
        hbox.addWidget(self._other2)
        hbox.addWidget(self._other3)
        hbox.addWidget(self._other4)
        self.setLayout(hbox)

        self._popframe = None
        self._popflag = False

    def resizeEvent(self, event):
        if self._popflag:
            self._popframe.move(0, 0)
            self._popframe.resize(self.width(), self.height())

    def _onpopup(self):
        self._popframe = TranslucentWidget(self)
        self._popframe.move(0, 0)
        self._popframe.resize(self.width(), self.height())
        self._popframe.SIGNALS.CLOSE.connect(self._closepopup)
        self._popflag = True
        self._popframe.show()

    def _closepopup(self):
        self._popframe.close()
        self._popflag = False


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    main = ParentWidget()
    main.resize(500, 500)
    main.show()
    sys.exit(app.exec_())

结果如下:

逻辑如下。您创建一个空的 Widget 并手动绘制背景和弹出窗口(paintEvent)。您添加一个用于关闭弹出窗口的按钮。为此,您构建一个 Signal 并让父小部件完成关闭。这很重要,因为您需要让父窗口小部件控制弹出窗口的一些重要元素(例如关闭、调整大小等)。您可以添加更多复杂性,但希望该示例足以满足初学者的需求。

【讨论】:

    【解决方案2】:

    多亏了 armatita,我成功地得到了我想要的东西。目前,有一些问题,但它有效,我得到了我想要的结果。

    我将代码提供给下一个将寻找相同内容的人。

    from PySide import QtCore, QtGui
    import sys
    
    class CtmWidget(QtGui.QWidget):
        def __init__(self, parent = None):
            QtGui.QWidget.__init__(self, parent)
    
            self.button = QtGui.QPushButton("Close Overlay")
            self.setLayout(QtGui.QHBoxLayout())
            self.layout().addWidget(self.button)
    
            self.button.clicked.connect(self.hideOverlay)
    
        def paintEvent(self, event):
    
            painter = QtGui.QPainter()
            painter.begin(self)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
            path = QtGui.QPainterPath()
            path.addRoundedRect(QtCore.QRectF(self.rect()), 10, 10)
            mask = QtGui.QRegion(path.toFillPolygon().toPolygon())
            pen = QtGui.QPen(QtCore.Qt.white, 1)
            painter.setPen(pen)
            painter.fillPath(path, QtCore.Qt.white)
            painter.drawPath(path)
            painter.end()
    
        def hideOverlay(self):
            self.parent().hide()
    
    
    
    class Overlay(QtGui.QWidget):
        def __init__(self, parent, widget):
            QtGui.QWidget.__init__(self, parent)
            palette = QtGui.QPalette(self.palette())
            palette.setColor(palette.Background, QtCore.Qt.transparent)
            self.setPalette(palette)
    
            self.widget = widget
            self.widget.setParent(self)
    
    
        def paintEvent(self, event):
            painter = QtGui.QPainter()
            painter.begin(self)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
            painter.fillRect(event.rect(), QtGui.QBrush(QtGui.QColor(0, 0, 0, 127)))
            painter.end()
    
        def resizeEvent(self, event):
            position_x = (self.frameGeometry().width()-self.widget.frameGeometry().width())/2
            position_y = (self.frameGeometry().height()-self.widget.frameGeometry().height())/2
    
            self.widget.move(position_x, position_y)
            event.accept()
    
    class MainWindow(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.resize(800, 500)
    
            self.button = QtGui.QPushButton("Click Me")
    
            self.setLayout(QtGui.QVBoxLayout())
            self.layout().addWidget(self.button)
            self.popup = Overlay(self, CtmWidget())
            self.popup.hide()
    
            # Connections
            self.button.clicked.connect(self.displayOverlay)
    
        def displayOverlay(self):
            self.popup.show()
            print "clicked"
    
        def resizeEvent(self, event):
            self.popup.resize(event.size())
            event.accept()
    
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec_())
    

    再次感谢你们(ymmx 和 armatita)花时间解决我的问题。

    【讨论】:

      【解决方案3】:

      您是否尝试将popup.show() 替换为popup.exec_()?并删除 self 作为 Qdialog 的参数?我将 QDialog 更改为 QmessageBox 以便能够退出子窗口,但它仍然可以与 QDialog 一起使用。

      popup =  QMessageBox()
      popup.setWindowFlags( Qt.FramelessWindowHint)
      popup.setLayout( QHBoxLayout())
      popup.layout().addWidget( QLabel("HI"))
      popup.exec_()
      

      更新

      class Popup(QDialog  ):
          def __init__(self):
              super().__init__()
              self.setWindowFlags(  Qt.CustomizeWindowHint)
              self.setLayout( QHBoxLayout())
              Button_close = QPushButton('close')
              self.layout().addWidget( QLabel("HI"))
              self.layout().addWidget( Button_close)
              Button_close.clicked.connect( self.close )
              self.exec_()
              print("clicked") 
      
          def mousePressEvent(self, event):
              self.oldPos = event.globalPos()
      
          def mouseMoveEvent(self, event):
              delta = QPoint (event.globalPos() - self.oldPos)
              #print(delta)
              self.move(self.x() + delta.x(), self.y() + delta.y())
              self.oldPos = event.globalPos()
      
      
      
      class MainWindow( QWidget):
          def __init__(self):
              QWidget.__init__(self)
              self.resize(800, 500)
      
              self.button =  QPushButton("Click Me")
      
              self.setLayout( QVBoxLayout())
              self.layout().addWidget(self.button)
      
              # Connections
              self.button.clicked.connect(self.displayOverlay)
      
      
          def displayOverlay(self):
              Popup( )
      
      
      if __name__ == "__main__":
          app =  QApplication(sys.argv)
          window = MainWindow()
          window.show()
          sys.exit(app.exec_())
      

      【讨论】:

      • 好的开始,问题是我不能删除它,移动它(使用代码,跟随主窗口)或其他东西一旦创建......
      • 你不能拖动或关闭窗口,因为你使用了这个属性:popup.setWindowFlags( Qt.FramelessWindowHint)
      • 如果您想要拖动或其他属性,您必须全部指定。
      • 要拖动弹出窗口,我认为您必须创建一个新类并按照此处的建议添加 mousepressevent 和 mousemoveevent stackoverflow.com/questions/37718329/…
      • 我知道我不能拖动它,因为没有要拖动的标题栏,但我在注释中指定了我通过代码尝试过的代码,因此它应该与代码一起执行简单的 popup.move(我想要的地方)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多