PyQt5 - 在 QMainMenu 中,如何使 QWidget 成为父级(暂时)?

Posted

技术标签:

【中文标题】PyQt5 - 在 QMainMenu 中,如何使 QWidget 成为父级(暂时)?【英文标题】:PyQt5 - in a QMainMenu, how to make a QWidget the parent (temporarily)? 【发布时间】:2018-08-01 14:07:58 【问题描述】:

我有一个QMainWindow,我用QWidget 进行了初始化。我希望每次按下按钮New 在我的QMainWiindow 中,它会暂时打开QWidget(在我的情况下,直到鼠标按钮释放)。

我无法与QMainWindowQWidget 交互。我尝试了很多选项,但似乎我尝试的所有方法都将QWidget 绑定到QMainWindow 屏幕,我不希望这样。

举个例子会更容易:

TempWidgetMenu.py 是我的QMainWindow 班级。当我按下New 时,会出现一个QWidget,它会将屏幕着色为灰色,并且会为从按下按钮到释放按钮的矩形着色(就像在 Windows 截图工具中一样)。

我希望每次按下New 时,我都能够从屏幕的每个点绘制一个矩形,并且第一次就这样做了。 当我第二次(或之后)按下New 时,它将为除主菜单屏幕之外的所有内容着色,并且不会响应按钮操作。

我希望每次按下按钮时小部件都成为屏幕中程序的“父级”。

TempWidgetMenu.py(主):

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction
from PyQt5.QtGui import QPixmap, QPainter
import TempWidget


class Menu(QMainWindow):

    def __init__(self):
        super().__init__()
        newAct = QAction('New', self)
        newAct.triggered.connect(self.new_image_window)
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(newAct)
        self.opac_rect = TempWidget.TempOpacWidget()
        self.image = QPixmap("background.png")
        self.setGeometry(100, 100, 500, 300)
        self.resize(self.image.width(), self.image.height())
        self.show()

    def new_image_window(self):
        self.opac_rect.start()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawPixmap(self.rect(), self.image)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainMenu = Menu()
    sys.exit(app.exec_())

TempWidget.py

import tkinter as tk
from PyQt5 import QtWidgets, QtCore, QtGui


class TempOpacWidget(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()
        root = tk.Tk()
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        self.setGeometry(0, 0, screen_width, screen_height)
        self.setWindowTitle(' ')
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.busy = False

    def start(self):

        self.busy = True
        self.setWindowOpacity(0.3)
        self.show()

    def paintEvent(self, event):
        if self.busy:
            brush_color = (128, 128, 255, 100)
            opacity = 0.3
        else:
            brush_color = (0, 0, 0, 0)
            opacity = 0

        self.setWindowOpacity(opacity)
        qp = QtGui.QPainter(self)
        qp.setBrush(QtGui.QColor(*brush_color))
        qp.drawRect(QtCore.QRectF(self.begin, self.end))

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.busy = False
        self.repaint()

我意识到我在一开始就初始化了TempOpacWidget。我只想初始化一次,因为它在做同样的事情。

我该如何解决这个问题,让TempOpacWidget 每次我打电话给他时都会成为他的父母?

编辑:如果有什么不清楚的地方,运行代码会很有意义。按New,选择一个矩形(用鼠标),然后再按New选择另一个矩形,你就会明白问题出在哪里了。

【问题讨论】:

【参考方案1】:

我不太明白会发生什么, 但我添加并更改了一些代码行。 单击New 按钮并绘制一个矩形。 你应该有新的想法。 祝你好运。

TempWidgetMenu.py

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction
from PyQt5.QtGui     import QPixmap, QPainter
from PyQt5.QtCore    import Qt                             # +++
import TempWidget

class Menu(QMainWindow):
    def __init__(self):
        super().__init__()
        newAct = QAction('New', self)
        newAct.triggered.connect(self.new_image_window)
        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(newAct)

        self.opac_rect = TempWidget.TempOpacWidget(self)   # +++ self

        self.imageShow()                                   # +++  

    def imageShow(self):
        self.setWindowFlags(Qt.WindowStaysOnTopHint)       # +++ 

        self.image = QPixmap("background.png")
        self.setGeometry(100, 100, 500, 300)
        self.resize(self.image.width(), self.image.height())
        self.show()

    def new_image_window(self):
        self.opac_rect.start()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawPixmap(self.rect(), self.image)

    # +++    
    def closeEvent(self, event):
        self.opac_rect.close()
        event.accept()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainMenu = Menu()
    sys.exit(app.exec_())

TempWidget.py

import tkinter as tk
from PyQt5        import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import Qt                                 # +++

class TempOpacWidget(QtWidgets.QWidget):

#    def __init__(self):
#        super().__init__()
    def __init__(self, parent=None):
        super(TempOpacWidget, self).__init__()              # no (parent)
        self.parent = parent                                # +++
        print("self=``, \nparent=``".format(self, self.parent))
        self.setWindowFlags(Qt.WindowStaysOnTopHint)        # +++

        root = tk.Tk()
        screen_width  = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        self.setGeometry(0, 0, screen_width, screen_height)
#        self.setWindowTitle('')
        self.begin = QtCore.QPoint()
        self.end   = QtCore.QPoint()
        self.busy  = False

    def start(self):
        self.setWindowFlags(Qt.WindowStaysOnTopHint)        # +++
        self.busy = True
        self.setWindowOpacity(0.5) # 0.3
        self.show()

    def paintEvent(self, event):
        if self.busy:
            brush_color = (128, 128, 255, 100)
            opacity = 0.5          # 0.3
        else:
            brush_color = (0, 0, 0, 0)

            opacity = 0.5          # 0       <<<---------
            # or try  `0`, how suits you !?  <<<---------
            #opacity = 0                    #<<<--------- 

        self.setWindowOpacity(opacity)
        qp = QtGui.QPainter(self)
        qp.setBrush(QtGui.QColor(*brush_color))
        qp.drawRect(QtCore.QRectF(self.begin, self.end))

    def mousePressEvent(self, event):
        #self.parent.hide()
        self.begin = event.pos()
        self.end   = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        print("def mouseReleaseEvent(self, event):---")
        self.busy = False
        self.repaint()
        self.parent.imageShow()                         # +++

【讨论】:

【参考方案2】:

如果我理解正确,您希望Menu.opac_rect 在按下鼠标按钮时在单独的窗口中打开。我看到这有几个问题。首先,如果您希望 QWidget 在单独的窗口中打开,您应该将 Qt.windowFlags 参数传递给 QWidget 构造函数,即

self.opac_rect = TempWidget.TempOpacWidget(None, QtCore.Qt.Window)

不要忘记导入 QtCore 命名空间。

其次,QAction 没有您需要的信号。只有从QWidget 继承的对象才会有mousePressEventmouseReleaseEvent。如果你必须在工具栏中保留东西,QToolBarQToolBar.addWidget,所以你可以使用QLabel 而不是QAction,并覆盖必要的事件处理程序。

self.opac_rect = TempWidget.TempOpacWidget(None, QtCore.Qt.Window)
newAct = QLabel('New')
newAct.mousePressEvent = lambda e: self.opac_rect.start()
newAct.mouseReleaseEvent = lambda e: self.opac_rect.stop() # You'll need to write this
self.toolbar.addWidget(newAct)

您可能不得不摆弄QLabel 样式以使其看起来正确,但我认为这将实现您所寻找的。​​p>

【讨论】:

不完全是。当我按下 New 时,我希望它为整个屏幕着色,并且只有在不同的按钮释放后屏幕才会恢复正常(与按下 New 无关)。我修改了代码示例,如果你运行它,它会更有意义。 @TheCrystalShip 与新闻和发布相关联而不是点击有多重要?彩色小部件(部分透明?)是否覆盖了您正在绘制的图像? 这很重要。我更新了我的代码。按New,选择一个矩形,然后再按New,你就会明白是什么问题了。

以上是关于PyQt5 - 在 QMainMenu 中,如何使 QWidget 成为父级(暂时)?的主要内容,如果未能解决你的问题,请参考以下文章

pyqt5如何使复选框将两个Qslider锁定在一起[关闭]

如何使用 PYQT5 使 QTableWidget 单元格只读?

如何使 Pyqt5 QLineEdit 只接受数字

如何使小部件随 PyQt5 中的窗口缩放?

如何在 PyQt5 中反转旋转框选择?

如何在 PyQt5 中制作嵌套的 StackedLayouts?