PySide / PyQt Overlay小部件

时间:2017-05-30 14:20:37

标签: python pyqt pyside

我试图在PySide中实现类似的功能:https://www.haskell.org/hoogle/?hoogle=delete 我想要做的是创建一个无框架的儿童窗户,下方有黑色覆盖。

我没有成功创建无框架子窗口和叠加层......

这是复制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注释该行,则窗口会出现,否则什么都不会发生......

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

3 个答案:

答案 0 :(得分:8)

我将使用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_())

结果如下:

Parent widget for a PyQt5 experiment Creating a popup in PyQt5

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

您是否尝试将popup.show()替换为popup.exec_()?并删除自我作为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_())