PySide/PyQt 覆盖小部件

问题描述 投票:0回答:4

我正在尝试在 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 注释该行,窗口就会出现,否则什么也不会发生......

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

python pyqt pyside
4个回答
15
投票

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

以下示例有一个父小部件,其中有几个按钮。其中之一(最明显的一个)需要弹出窗口。我已经准备了示例来处理父级调整大小,但尚未编写任何有关拖动弹出窗口的鼠标事件的代码(请参阅

mouseMoveEvent
mouseReleaseEvent
)。

所以这是代码:

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_())

结果如下:

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


4
投票

感谢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)花时间解决我的问题。


0
投票

您是否尝试将

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_())

0
投票

我已经更新并完善了@armatita 提供的代码,所以现在它是一个独立的小部件。如果有人仍然感兴趣,这里是示例https://github.com/machur/qt-extra-widgets/blob/main/examples/text_overlay_widget_example.py

Sample text overlay

© www.soinside.com 2019 - 2024. All rights reserved.