由设计师创建的小部件上的 PySide2 绘制 [英] PySide2 paint on widget created by designer

查看:49
本文介绍了由设计师创建的小部件上的 PySide2 绘制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题与 Win10 和 Python 3.6.6 上的 VS Code 有关.我是 Python 和 PySide2 的新手.

我在 StackOverflow 上阅读了很多关于此的主题,这可能是另一个主题的副本,但我无法绘制我的小部件.

我知道必须以某种方式覆盖小部件对象的paintEvent().大多数示例都在主窗口上进行了一些绘制,但我无法从 ui.file 将其传输到小部件上.

我在我的 .py 文件中创建了两个类,MainForm 和 Drawer.MainForm 包含 UI 的实现,我正在尝试绘制一个小部件(名为小部件").在我的 .ui 文件中有一个小部件和一个图形视图.我正在尝试在小部件上实现绘画.

paintEventTest.py 文件如下所示:

导入系统从 PySide2 导入 QtWidgets从 PySide2 导入 QtGui从 PySide2 导入 QtCore从 PySide2.QtUiTools 导入 QUiLoader从 PySide2.QtWidgets 导入(QApplication、QPushButton、QLineEdit、QTextEdit、QSpinBox、QMainWindow、QDesktopWidget、QTableWidget、QTableWidgetItem、QToolButton、QToolTip)从 PySide2.QtCore 导入 QFile、QObject、Qt类 MainForm(QMainWindow):def __init__(self, ui_file, parent=None):super(MainForm, self).__init__(parent)ui_file = QFile(ui_file)ui_file.open(QFile.ReadOnly)### 从设计器加载 UI 文件###加载器 = QUiLoader()self.ui_window = loader.load(ui_file)ui_file.close()self.ui_window.show()### 这不起作用(显然?)###小部件 = self.ui_window.widget抽屉 = 抽屉()drawer.paintEvent(小部件)类抽屉(QtWidgets.QWidget):defpaintEvent(self, e):'''方法paintEvent()被自动调用QPainter 类完成所有低级绘图在其方法 begin() 和 end() 之间编码'''qp = QtGui.QPainter()qp.begin(self)self.drawGeometry(qp)qp.end()def drawGeometry(self, qp):qp = QtGui.QPainter(self)qp.setPen(QtGui.QPen(Qt.green, 8, Qt.DashLine))qp.drawEllipse(40, 40, 400, 400)如果 __name__ == '__main__':app = QApplication(sys.argv)app.setStyle('融合')form = MainForm('./UI Designer/testUI.ui')sys.exit(app.exec_())

testUI.ui 看起来像这样,它在UI Designer"文件夹中实现:

我用上面的代码得到了这个.我不希望它工作,但真的不知道如何引用特定的小部件进行绘画.

QWidget::paintEngine: 不应再被调用QPainter::begin: 绘制设备返回引擎 == 0,类型:1QWidget::paintEngine: 不应再被调用QPainter::begin: 绘制设备返回引擎 == 0,类型:1QPainter::setPen:画家未激活QPainter::end:画家不活动,中止

我也对使用 graphicsscene 和 graphicsitem 在图形视图上绘制等效代码感兴趣.

解决方案

正如您所指出的,paintEvent 应该只被覆盖.因此,一种选择是推广小部件,您可以在这些答案中看到几个示例:

  • 按下添加按钮,然后按下升级按钮,生成以下 .ui 文件

    另一方面,QUiLoader 只加载 Qt 默认提供的小部件,所以如果你想使用新的小部件,你必须覆盖 createWidget 方法:

    ma​​in.py

    import os导入系统从 PySide2 导入 QtCore、QtGui、QtWidgets、QtUiTools从 mywidget 导入抽屉类 UiLoader(QtUiTools.QUiLoader):def createWidget(self, className, parent=None, name=""):如果 className == "抽屉":小部件 = 抽屉(父)widget.setObjectName(name)返回小部件return super(UiLoader, self).createWidget(className, parent, name)类 MainForm(QtCore.QObject):def __init__(self, ui_file, parent=None):super(MainForm, self).__init__(parent)ui_file = QtCore.QFile(ui_file)ui_file.open(QtCore.QFile.ReadOnly)### 从设计器加载 UI 文件###加载器 = UiLoader()self.ui_window = loader.load(ui_file)ui_file.close()self.ui_window.show()如果 __name__ == "__main__":app = QtWidgets.QApplication(sys.argv)app.setStyle("融合")文件 = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./UI Designer/testUI.ui")表单 = MainForm(文件)sys.exit(app.exec_())

    This issue is with VS Code on Win10 and Python 3.6.6. I'm new both to Python and PySide2.

    I've read a lot of topics on this here at StackOverflow and possibly this is a duplicate of another topic, but I'm not able to get my widget painted.

    I understand that the paintEvent() of the widget object have to be overridden somehow. Most of the examples out there does some painting on the main window but I'm not able to transfer this on widgets from an ui.file.

    I've created two classes in my .py-file, MainForm and Drawer. The MainForm contains implementation of UI and I'm trying to get a widget (named "widget") painted. In my .ui-file there's a widget and a graphicsview. I'm trying to implement painting on the widget.

    paintEventTest.py file looks like this:

    import sys
    
    from PySide2 import QtWidgets 
    from PySide2 import QtGui
    from PySide2 import QtCore
    from PySide2.QtUiTools import QUiLoader
    from PySide2.QtWidgets import (
        QApplication, QPushButton, QLineEdit, QTextEdit, QSpinBox, QMainWindow, QDesktopWidget, QTableWidget, 
        QTableWidgetItem, QToolButton, QToolTip)
    from PySide2.QtCore import QFile, QObject, Qt
    
    
    class MainForm(QMainWindow):
    
        def __init__(self, ui_file, parent=None):
            super(MainForm, self).__init__(parent)
            ui_file = QFile(ui_file)
            ui_file.open(QFile.ReadOnly)
    
    
            ### Load UI file from Designer ###
            loader = QUiLoader()
            self.ui_window = loader.load(ui_file)
            ui_file.close()
    
            self.ui_window.show()
    
            ### THIS IS NOT WORKING (OBVIOUSLY?) ###
    
            widget = self.ui_window.widget
            drawer = Drawer()
            drawer.paintEvent(widget)
    
    
    
    class Drawer(QtWidgets.QWidget):
    
        def paintEvent(self, e):
            '''
            the method paintEvent() is called automatically
            the QPainter class does all the low-level drawing
            coded between its methods begin() and end()
            '''
    
            qp = QtGui.QPainter()
            qp.begin(self)
            self.drawGeometry(qp)
            qp.end()
    
        def drawGeometry(self, qp):
            qp = QtGui.QPainter(self)
            qp.setPen(QtGui.QPen(Qt.green, 8, Qt.DashLine))
            qp.drawEllipse(40, 40, 400, 400)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        app.setStyle('Fusion')
        form = MainForm('./UI designer/testUI.ui')
        sys.exit(app.exec_())
    

    testUI.ui looks like this and it's implemented in a folder "UI designer":

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>731</width>
        <height>633</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QGraphicsView" name="graphicsView">
          <property name="minimumSize">
           <size>
            <width>0</width>
            <height>200</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QWidget" name="widget" native="true">
          <property name="minimumSize">
           <size>
            <width>0</width>
            <height>250</height>
           </size>
          </property>
          <property name="maximumSize">
           <size>
            <width>16777215</width>
            <height>300</height>
           </size>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>731</width>
         <height>21</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    I'm getting this with code above. I'm not expecting it to work, but have really no idea on how to reference the specific widget to paint on.

    QWidget::paintEngine: Should no longer be called
    QPainter::begin: Paint device returned engine == 0, type: 1
    QWidget::paintEngine: Should no longer be called
    QPainter::begin: Paint device returned engine == 0, type: 1
    QPainter::setPen: Painter not active
    QPainter::end: Painter not active, aborted
    
    
    

    I'm also interested in equivalent code painting on the graphicsview with graphicsscene and graphicsitem.

    解决方案

    As you point out, the paintEvent should only overridden. So one option is to promote the widget, you can see several examples in these answers:

    You must have the following structure:

    ├── main.py
    ├── mywidget.py
    └── UI designer
        └── testUI.ui
    

    In the mywidget.py file, implement the class you require:

    mywidget.py

    from PySide2 import QtCore, QtGui, QtWidgets
    
    
    class Drawer(QtWidgets.QWidget):
        def paintEvent(self, e):
            """
            the method paintEvent() is called automatically
            the QPainter class does all the low-level drawing
            coded between its methods begin() and end()
            """
            qp = QtGui.QPainter()
            qp.begin(self)
            self.drawGeometry(qp)
            qp.end()
    
        def drawGeometry(self, qp):
            qp.setPen(QtGui.QPen(QtCore.Qt.green, 8, QtCore.Qt.DashLine))
            qp.drawEllipse(40, 40, 400, 400)
    

    Then you have to open your .ui with Qt Designer, right click on the widget and select promote To... in the contextual menu, then fill in the dialog with the following:

    press the add button and then the promote button generating the following .ui file

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>MainWindow</class>
     <widget class="QMainWindow" name="MainWindow">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>731</width>
        <height>633</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QVBoxLayout" name="verticalLayout">
        <item>
         <widget class="QGraphicsView" name="graphicsView">
          <property name="minimumSize">
           <size>
            <width>0</width>
            <height>200</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="Drawer" name="widget" native="true">
          <property name="minimumSize">
           <size>
            <width>0</width>
            <height>250</height>
           </size>
          </property>
          <property name="maximumSize">
           <size>
            <width>16777215</width>
            <height>300</height>
           </size>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>731</width>
         <height>23</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <customwidgets>
      <customwidget>
       <class>Drawer</class>
       <extends>QWidget</extends>
       <header>mywidget</header>
       <container>1</container>
      </customwidget>
     </customwidgets>
     <resources/>
     <connections/>
    </ui>
    

    On the other the QUiLoader only loads the widgets that Qt provides by default so if you want to use new widget you must overwrite the createWidget method:

    main.py

    import os
    import sys
    from PySide2 import QtCore, QtGui, QtWidgets, QtUiTools
    
    from mywidget import Drawer
    
    
    class UiLoader(QtUiTools.QUiLoader):
        def createWidget(self, className, parent=None, name=""):
            if className == "Drawer":
                widget = Drawer(parent)
                widget.setObjectName(name)
                return widget
            return super(UiLoader, self).createWidget(className, parent, name)
    
    
    class MainForm(QtCore.QObject):
        def __init__(self, ui_file, parent=None):
            super(MainForm, self).__init__(parent)
            ui_file = QtCore.QFile(ui_file)
            ui_file.open(QtCore.QFile.ReadOnly)
    
            ### Load UI file from Designer ###
            loader = UiLoader()
            self.ui_window = loader.load(ui_file)
            ui_file.close()
            self.ui_window.show()
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        app.setStyle("Fusion")
        file = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "./UI designer/testUI.ui"
        )
        form = MainForm(file)
        sys.exit(app.exec_())
    

    这篇关于由设计师创建的小部件上的 PySide2 绘制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆