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