Search code examples
pythonqt-designerpyside2qchartqchartview

Getting PySide2 QCharts to show up when using Qt Designer


I've have a similar problem to the question here: How to insert QChartView in form with Qt Designer?

So I created my MainWindow of my UI in Qt Designer and I'm trying to get a QtChart to show up inside one of the tabs in my window. I'm importing the .ui file into a python program, and so far I can get the main window to display fine, but not the chart. I've spent most of the day trying to follow the instructions listed in the above answer, but so far I haven't been able to get my promoted widget to correctly import into my main program. I've been wondering if part of the reason is that I'm trying do this in the python version of Qt (i.e. PySide2) instead of the C++ version this answer was probably written for.

I've tried using both a QWidget and the recommended QGraphicsView as my base to promote my QChartView to, but so far every attempt gives me an error that reads: "QFormBuilder was unable to create a custom widget of the class 'QChartView'; defaulting to base class 'QWidget'." What am I doing wrong?

Like the linked question/answer states, I set my promoted widget with a "Promoted Class Name" of QChartView and the "Header File" to QtCHarts. Below is the base code of my Python program (ignore my weird imports at the beginning, as that's a separate issue I'm dealing with where my program won't find the class imports unless I format them that way.)

Any help would be greatly appreciated! Thanks!

import sys
import PySide2.QtCore as Qt_Core
import PySide2.QtGui as Qt_Gui
import PySide2.QtWidgets as Qt_Widgets
import PySide2.QtUiTools as Qt_UiTools
import PySide2.QtCharts as Qt_Chart
from PySide2.QtCharts import QtCharts
import Pico_Image_Resources
import Chart_UI

# Having import issues with submodules.
# Need to explicitly import submodules.
QMainWindow = Qt_Widgets.QMainWindow
QApplication = Qt_Widgets.QApplication
QGraphicsView = Qt_Widgets.QGraphicsView
QUiLoader = Qt_UiTools.QUiLoader
QFile = Qt_Core.QFile
QStyleFactory = Qt_Widgets.QStyleFactory
QtCharts = Qt_Chart.QtCharts
QChartView = QtCharts.QChartView
QPainter = Qt_Gui.QPainter
#-----------------------------------------

class MainWindow(QMainWindow):

    def __init__(self, ui_file, parent=None):
        super(MainWindow, self).__init__(parent)
        ui_file = QFile(ui_file)
        ui_file.open(QFile.ReadOnly)
 
        chartUI = Chart_UI.generateChart()
        self.chart = QtCharts.QChart()

        """
        Code that defines my chart setup is here

        """
        
        loader = QUiLoader()
        self.window = loader.load(ui_file)
        QApplication.setStyle(QStyleFactory.create('Fusion'))
        ui_file.close()
      
        #Normal Way to set up 'ChartView'
        # self.chartView = QChartView(self.chart)

        self.chartView = self.window.findChild(QChartView, 'calibrationChart')
        self.chartView.setChart(self.chart)
        self.chartView.setRenderHint(QPainter.Antialiasing)

        self.window.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow('My_Main_Window.ui')
    sys.exit(app.exec_())

[EDIT]: Here is the .ui file code that I'm loading, as requested. It's not the full file as the original is too long to post, but it should be enough to get the idea:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <author></author>
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="enabled">
   <bool>true</bool>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1280</width>
    <height>720</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>1280</width>
    <height>720</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="styleSheet">
   <string notr="true"/>
  </property>
  <property name="toolButtonStyle">
   <enum>Qt::ToolButtonFollowStyle</enum>
  </property>
  <widget class="QWidget" name="centralwidget">
   <property name="styleSheet">
    <string notr="true"/>
   </property>
   <widget class="QTabWidget" name="tabWidget">
    <property name="geometry">
     <rect>
      <x>400</x>
      <y>10</y>
      <width>871</width>
      <height>641</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <pointsize>10</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="focusPolicy">
     <enum>Qt::TabFocus</enum>
    </property>
    <property name="currentIndex">
     <number>0</number>
    </property>
    <widget class="QWidget" name="tabCalib">
     <property name="enabled">
      <bool>true</bool>
     </property>
     <attribute name="title">
      <string>Chart 1</string>
     </attribute>
     <widget class="QChartView" name="calibrationChart" native="true">
      <property name="geometry">
       <rect>
        <x>19</x>
        <y>19</y>
        <width>821</width>
        <height>571</height>
       </rect>
      </property>
     </widget>
    </widget>
    <widget class="QWidget" name="tabDynamic">
     <attribute name="title">
      <string>Chart 2</string>
     </attribute>
     <widget class="QGraphicsView" name="graphicsView_2">
      <property name="geometry">
       <rect>
        <x>10</x>
        <y>10</y>
        <width>861</width>
        <height>591</height>
       </rect>
      </property>
      <property name="frameShape">
       <enum>QFrame::StyledPanel</enum>
      </property>
      <property name="frameShadow">
       <enum>QFrame::Sunken</enum>
      </property>
      <property name="renderHints">
       <set>QPainter::Antialiasing|QPainter::TextAntialiasing</set>
      </property>
     </widget>
    </widget>
   </widget>
   <widget class="QWidget" name="verticalLayoutWidget">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>10</y>
      <width>361</width>
      <height>451</height>
     </rect>
    </property>
    <layout class="QVBoxLayout" name="verticalLayout">
     <property name="spacing">
      <number>10</number>
     </property>
     <property name="sizeConstraint">
      <enum>QLayout::SetDefaultConstraint</enum>
     </property>
     <property name="leftMargin">
      <number>0</number>
     </property>
     <item>
      <widget class="QLabel" name="label_20">
       <property name="minimumSize">
        <size>
         <width>0</width>
         <height>32</height>
        </size>
       </property>
       <property name="maximumSize">
        <size>
         <width>16777215</width>
         <height>32</height>
        </size>
       </property>
       <property name="font">
        <font>
         <family>Calibri</family>
         <pointsize>11</pointsize>
         <weight>75</weight>
         <bold>true</bold>
         <strikeout>false</strikeout>
        </font>
       </property>
       <property name="frameShape">
        <enum>QFrame::StyledPanel</enum>
       </property>
       <property name="frameShadow">
        <enum>QFrame::Plain</enum>
       </property>
       <property name="text">
        <string>Test Title Here</string>
       </property>
       <property name="alignment">
        <set>Qt::AlignCenter</set>
       </property>
       <property name="wordWrap">
        <bool>false</bool>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QGroupBox" name="groupBox">
       <property name="title">
        <string>Please fill in the following data boxes:</string>
       </property>
       <widget class="QWidget" name="layoutWidget">
        <property name="geometry">
         <rect>
          <x>10</x>
          <y>40</y>
          <width>341</width>
          <height>271</height>
         </rect>
        </property>
        <layout class="QFormLayout" name="formLayout">
         <property name="labelAlignment">
          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
         </property>
         <property name="horizontalSpacing">
          <number>10</number>
         </property>
         <property name="verticalSpacing">
          <number>10</number>
         </property>
         <item row="0" column="0">
          <widget class="QLabel" name="label_2">
           <property name="text">
            <string>Field One:</string>
           </property>
          </widget>
         </item>
         <item row="0" column="1">
          <widget class="QLineEdit" name="lineEdit_8"/>
         </item>
         <item row="1" column="0">
          <widget class="QLabel" name="label_3">
           <property name="text">
            <string>Field Two:</string>
           </property>
          </widget>
         </item>
         <item row="1" column="1">
          <widget class="QLineEdit" name="lineEdit"/>
         </item>
         <item row="2" column="0">
          <widget class="QLabel" name="label_4">
           <property name="text">
            <string>Field Three:</string>
           </property>
          </widget>
         </item>
         <item row="2" column="1">
          <widget class="QLineEdit" name="lineEdit_2"/>
         </item>
         <item row="4" column="0">
          <widget class="QLabel" name="label_5">
           <property name="text">
            <string>Field Five:</string>
           </property>
          </widget>
         </item>
         <item row="4" column="1">
          <widget class="QLineEdit" name="lineEdit_3"/>
         </item>
         <item row="5" column="0">
          <widget class="QLabel" name="label_6">
           <property name="text">
            <string>Field Six:</string>
           </property>
          </widget>
         </item>
         <item row="5" column="1">
          <widget class="QLineEdit" name="lineEdit_4"/>
         </item>
         <item row="6" column="0">
          <widget class="QLabel" name="label_7">
           <property name="text">
            <string>Selection One:</string>
           </property>
          </widget>
         </item>
         <item row="6" column="1">
          <widget class="QComboBox" name="comboBox">
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
             <string>123</string>
            </property>
           </item>
           <item>
            <property name="text">
             <string>456</string>
            </property>
           </item>
           <item>
            <property name="text">
             <string>789</string>
            </property>
           </item>
          </widget>
         </item>
         <item row="7" column="0">
          <widget class="QLabel" name="label_8">
           <property name="text">
            <string>Selection Two:</string>
           </property>
          </widget>
         </item>
         <item row="7" column="1">
          <widget class="QComboBox" name="comboBox_2">
           <property name="currentIndex">
            <number>3</number>
           </property>
           <item>
            <property name="text">
             <string>123</string>
            </property>
           </item>
           <item>
            <property name="text">
             <string>456</string>
            </property>
           </item>
           <item>
            <property name="text">
             <string>789</string>
            </property>
           </item>
          </widget>
         </item>
         <item row="8" column="0">
          <widget class="QLabel" name="label_9">
           <property name="text">
            <string>Output:</string>
           </property>
          </widget>
         </item>
         <item row="8" column="1">
          <widget class="QLineEdit" name="lineEdit_7">
           <property name="enabled">
            <bool>false</bool>
           </property>
          </widget>
         </item>
         <item row="3" column="0">
          <widget class="QLabel" name="label_21">
           <property name="text">
            <string>Field Four:</string>
           </property>
          </widget>
         </item>
         <item row="3" column="1">
          <widget class="QLineEdit" name="lineEdit_5">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="readOnly">
            <bool>true</bool>
           </property>
           <property name="clearButtonEnabled">
            <bool>false</bool>
           </property>
          </widget>
         </item>
        </layout>
       </widget>
      </widget>
     </item>
    </layout>
   </widget>
   <widget class="Line" name="line">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>470</y>
      <width>361</width>
      <height>21</height>
     </rect>
    </property>
    <property name="orientation">
     <enum>Qt::Horizontal</enum>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>140</x>
      <y>500</y>
      <width>111</width>
      <height>41</height>
     </rect>
    </property>
    <property name="font">
     <font>
      <family>Calibri</family>
      <pointsize>14</pointsize>
      <weight>75</weight>
      <bold>true</bold>
     </font>
    </property>
    <property name="styleSheet">
     <string notr="true">background-color: rgb(85, 255, 0)</string>
    </property>
    <property name="text">
     <string>START TEST</string>
    </property>
    <property name="checkable">
     <bool>false</bool>
    </property>
    <property name="checked">
     <bool>false</bool>
    </property>
    <property name="default">
     <bool>false</bool>
    </property>
    <property name="flat">
     <bool>false</bool>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1280</width>
     <height>21</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionSettings"/>
    <addaction name="separator"/>
    <addaction name="actionClose"/>
   </widget>
   <widget class="QMenu" name="menuAbout">
    <property name="title">
     <string>About</string>
    </property>
   </widget>
   <addaction name="menuFile"/>
   <addaction name="menuAbout"/>
  </widget>
  <widget class="QStatusBar" name="statusbar">
   <property name="enabled">
    <bool>true</bool>
   </property>
   <property name="autoFillBackground">
    <bool>false</bool>
   </property>
   <property name="styleSheet">
    <string notr="true">background-color: rgb(0, 132, 203);</string>
   </property>
   <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
  </widget>
  <action name="actionSettings">
   <property name="text">
    <string>Settings</string>
   </property>
  </action>
  <action name="actionClose">
   <property name="text">
    <string>Close</string>
   </property>
  </action>
 </widget>
 <customwidgets>
  <customwidget>
   <class>QChartView</class>
   <extends>QWidget</extends>
   <header>QtCharts</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources>
 </resources>
 <connections/>
</ui>


Solution

  • QWidget as a container:

    In this case I do not recommend applying the promotion because it complicates the task a lot but I will show how to apply the solution shown in my other answer: Use a QWidget as a container in the .ui.

    For the example I have created a .ui with several components and where you want to place the QChartView, place a QWidget (to make it visible I have set a green background color):

    enter image description here

    design.ui

    <?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>636</width>
        <height>480</height>
       </rect>
      </property>
      <property name="windowTitle">
       <string>MainWindow</string>
      </property>
      <widget class="QWidget" name="centralwidget">
       <layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
        <item row="0" column="0">
         <widget class="QListWidget" name="listWidget"/>
        </item>
        <item row="1" column="0">
         <widget class="QTableWidget" name="tableWidget"/>
        </item>
        <item row="0" column="1" rowspan="2">
         <widget class="QWidget" name="widget" native="true">
          <property name="styleSheet">
           <string notr="true">background-color: rgb(138, 226, 52);</string>
          </property>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QMenuBar" name="menubar">
       <property name="geometry">
        <rect>
         <x>0</x>
         <y>0</y>
         <width>636</width>
         <height>24</height>
        </rect>
       </property>
      </widget>
      <widget class="QStatusBar" name="statusbar"/>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    Then the logic is simple: get the QWidget through the objectName which in this case is "widget", then place a layout inside that widget, and then place the QChartView inside the widget through the layout.

    import random
    import sys
    
    from PySide2 import QtCore, QtGui, QtUiTools, QtWidgets
    from PySide2.QtCharts import QtCharts
    
    
    def ui_to_window(filename, parent=None):
        file = QtCore.QFile(filename)
        if not file.open(QtCore.QFile.ReadOnly):
            return
        loader = QtUiTools.QUiLoader()
        window = loader.load(file, parent)
        return window
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = ui_to_window("design.ui")
        window.widget.setContentsMargins(0, 0, 0, 0)
        lay = QtWidgets.QVBoxLayout(window.widget)
        lay.setContentsMargins(0, 0, 0, 0)
    
        chartview = QtCharts.QChartView()
        chartview.setContentsMargins(0, 0, 0, 0)
        lay.addWidget(chartview)
    
        series = QtCharts.QLineSeries()
    
        for i in range(10):
            series << QtCore.QPointF(i, random.uniform(0, 10))
    
        # Create Chart and set General Chart setting
        chart = QtCharts.QChart()
        chart.addSeries(series)
        chart.setAnimationOptions(QtCharts.QChart.SeriesAnimations)
    
        # X Axis Settings
        axisX = QtCharts.QValueAxis()
        chart.addAxis(axisX, QtCore.Qt.AlignBottom)
        series.attachAxis(axisX)
    
        # Y Axis Settings
        axisY = QtCharts.QValueAxis()
        chart.addAxis(axisY, QtCore.Qt.AlignLeft)
        series.attachAxis(axisY)
    
        chartview.setChart(chart)
    
        window.show()
        sys.exit(app.exec_())
    

    enter image description here


    QChartView Promotion:

    If you still want to use the promotion method then you must implement a custom QUiLoader override the createWidget method by returning the QChartView if the className is QChartView:

    import random
    import sys
    
    from PySide2 import QtCore, QtGui, QtUiTools, QtWidgets
    from PySide2.QtCharts import QtCharts
    
    
    class UiLoader(QtUiTools.QUiLoader):
        def createWidget(self, className, parent=None, name=""):
            if className == "QChartView":
                return QtCharts.QChartView(parent)
            return super().createWidget(className, parent, name)
    
    
    def ui_to_window(filename, parent=None):
        file = QtCore.QFile(filename)
        if not file.open(QtCore.QFile.ReadOnly):
            return
        loader = UiLoader()
        window = loader.load(file, parent)
        return window
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        window = ui_to_window("Pico_LaserCal_MainWindow.ui")
        series = QtCharts.QLineSeries()
    
        for i in range(10):
            series << QtCore.QPointF(i, random.uniform(0, 10))
    
        # Create Chart and set General Chart setting
        chart = QtCharts.QChart()
        chart.addSeries(series)
        chart.setAnimationOptions(QtCharts.QChart.SeriesAnimations)
    
        # X Axis Settings
        axisX = QtCharts.QValueAxis()
        chart.addAxis(axisX, QtCore.Qt.AlignBottom)
        series.attachAxis(axisX)
    
        # Y Axis Settings
        axisY = QtCharts.QValueAxis()
        chart.addAxis(axisY, QtCore.Qt.AlignLeft)
        series.attachAxis(axisY)
    
        window.calibrationChart.setChart(chart)
    
        window.show()
        sys.exit(app.exec_())