PySide2 problem with ParameterTree.

309 views
Skip to first unread message

tjan...@gmail.com

unread,
Jan 29, 2021, 7:29:39 PM1/29/21
to pyqtgraph
HI,

(Sorry about the formatting - not used to google groups posting.)

I have an application I'm trying to get working with PySide2. I made changes following the example designerExample.py .
Everything seems to work fine with PyQt5, but when I try to use PySide everytime I try to do a ParameterTree.setParmeters() I get an error like this:

  File "C:\Users\Tim\pyside2_env\lib\site-packages\pyqtgraph\parametertree\ParameterTree.py", line 48, in setParameters
    self.clear()
  File "C:\Users\Tim\pyside2_env\lib\site-packages\pyqtgraph\parametertree\ParameterTree.py", line 85, in clear
    self.invisibleRootItem().takeChildren()
  File "C:\Users\Tim\pyside2_env\lib\site-packages\pyqtgraph\widgets\TreeWidget.py", line 390, in takeChildren
    childs = self._real_item.takeChildren()
RuntimeError: Internal C++ object (PySide2.QtWidgets.QTreeWidgetItem) already deleted.

I'm using PySide2.QUiTools.QuiLoader().load to load my UI file for PySide2, and 
uic.loadUI for PyQt5.

with importlib.resources.path('nvfieldcap.resources.ui', 'paramtreeDialog.ui') as uifilename:
if 'PySide2' in sys.modules:
self.dialog = loader.load(uifilename.as_posix())
if 'PyQt5' in sys.modules:
self.dialog = QtWidgets.QDialog()
uic.loadUi(uifilename.as_posix(), self.dialog)

Tim Williams

unread,
Jan 29, 2021, 7:43:33 PM1/29/21
to pyqt...@googlegroups.com
As a follow-up, I'm using registerCustomWidget for the promoted widgets:
loader = QUiLoader()
loader.registerCustomWidget(pg.GraphicsLayoutWidget)
loader.registerCustomWidget(pg.widgets.TreeWidget)
loader.registerCustomWidget(pg.parametertree.ParameterTree)

----
Tim Williams


--
You received this message because you are subscribed to a topic in the Google Groups "pyqtgraph" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pyqtgraph/uv2uyQCu9MM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pyqtgraph+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/f04abdf3-068a-41c4-a993-6076713ca61an%40googlegroups.com.

Patrick

unread,
Jan 31, 2021, 6:12:08 AM1/31/21
to pyqtgraph
Hi,

I'm not sure if this will help you, but I've been using PySide2 for one of my latest projects and I remember having all sorts of issues getting .ui to load, as the uic.loadUi() method I used to use for PyQt5 didn't directly translate. It turns out PySide2 only fairly recently implemented the PySide2.QtUiTools.loadUiType() method which was the only way I got it to work, but it wasn't (isn't?) documented. I believe it should also work using PyQt5.uic.loadUiType(), though haven't tried it. My UI classes look something like:

# ...
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtUiTools import loadUiType  
# ...

class DataPanel(QtWidgets.QWidget, loadUiType(__file__.split(".py")[0] + ".ui")[0]):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)
        # ...

# ...

The .ui file is created as normal (named the same as the python file but with .ui extension), with the GraphicsLayoutWidget extended from QGraphicsView as described in the pyqtgraph documentation. The relevant section in the .ui XML then looks like:

 <customwidgets>
  <customwidget>
   <class>GraphicsLayoutWidget</class>
   <extends>QGraphicsView</extends>
   <header>mypackage.pyqtgraph</header>
  </customwidget>
 </customwidgets>

where "mypackage" is my application package name, and I have pyqtgraph in a directory under it as a subpackage. If you are using a system installation of pyqtgraph, then the header field should just be "pyqtgraph".

Hopefully that might give you some ideas anyway.

Patrick

Tim Williams

unread,
Jan 31, 2021, 10:39:30 AM1/31/21
to pyqt...@googlegroups.com
Thanks Patrick,

I already have a Ui file from Designer. If I understand correctly, it looks like you're basically doing the reverse of what pysideuic does, that is to take a .py file and create al .ui and then do a loadUiType().
Your <customwidgets> section did prompt me to look at that section in my Ui file.

 <customwidgets>
  <customwidget>
   <class>LayoutWidget</class>
   <extends>QWidget</extends>
   <header>pyqtgraph</header>
   <container>1</container>
  </customwidget>
 </customwidgets>

I noticed that the Designer showed my ParameterTree as unused. (I'm not sure where that came from.I edited that file, then used Git to get it back to before I edited it, and Designer is not showing that message anymore)
My ParameterTree is actually added in to my main to a Layout from another Ui file that does have ParameterTree as a promoted widget.

(in parametertreeDialog.ui:)
 <customwidgets>
  <customwidget>
   <class>ParameterTree</class>
   <extends>QTreeWidget</extends>
   <header>pyqtgraph.parametertree</header>
  </customwidget>
 </customwidgets>

This is part of a QDialog that I pop up when I want to change or view the parameters.  I think I need to add 
  <customwidget>
   <class>ParameterTree</class>
   <extends>QTreeWidget</extends>
   <header>pyqtgraph.parametertree</header>
  </customwidget>

to my main Ui file even though the widget

I do use pyqtgraph.Qt.loadUiType for the MainWindow like in the designerExample.py example, but I would like to use uic.loadUi(), loader.load() for my other Ui files. I'm trying to avoid creating *.py files using pysideuic. 
I'm going to try adding in ParameterTree as a promoted widget and see what happens.

----
Tim Williams


Tim Williams

unread,
Jan 31, 2021, 11:05:40 AM1/31/21
to pyqt...@googlegroups.com
Well, that didn't work.

I guess I have figure out how to properly use pg.Qt.loadUiType().
----
Tim Williams

tjan...@gmail.com

unread,
Jan 31, 2021, 12:26:55 PM1/31/21
to pyqtgraph
So why doesn't this work as I expect?

Template, BaseClass = pg.Qt.loadUiType(uifilename.as_posix())
self.FileNamingWidget = Template()
widget = BaseClass()
self.FileNamingWidget.setupUi(widget)

When I try to add self.FileNamingWidget to my layout, I get an error.

self.ui.PluginLayout.addWidget(self.FileNamingWidget)
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2020.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
  File "D:\anaconda3\envs\fieldcapenv_pyside\lib\site-packages\pyqtgraph\widgets\LayoutWidget.py", line 74, in addWidget
    self.layout.addWidget(item, row, col, rowspan, colspan)
TypeError: 'PySide2.QtWidgets.QGridLayout.addWidget' called with wrong argument types:
  PySide2.QtWidgets.QGridLayout.addWidget(Ui_Filename_Folder_Widget, int, int, int, int)
Supported signatures:
  PySide2.QtWidgets.QGridLayout.addWidget(PySide2.QtWidgets.QWidget, int, int, PySide2.QtCore.Qt.Alignment = Default(Qt.Alignment))
  PySide2.QtWidgets.QGridLayout.addWidget(PySide2.QtWidgets.QWidget, int, int, int, int, PySide2.QtCore.Qt.Alignment = Default(Qt.Alignment))
  PySide2.QtWidgets.QGridLayout.addWidget(PySide2.QtWidgets.QWidget)

self.FilenamingWidget is a 
<Ui_Filename_Folder_Widget object at 0x000001DAF81762C8>

but if I use loader.load(), it is
loader.load(uifilename.as_posix())
<PySide2.QtWidgets.QTabWidget(0x1dafaf6c200, name="Filename_Folder_Widget") at 0x000001DAFC3179C8>

and I can add this to my Layout. I get the same error under PyQt5 with loadUiType, but uic.loadUi() works fine. 

self.FileNamingWidget
Out[4]: <Ui_Filename_Folder_Widget at 0x22a97fa9c88>
uic.loadUi(uifilename.as_posix())
Out[3]: <PyQt5.QtWidgets.QTabWidget at 0x22a999fd9d8>

Patrick

unread,
Jan 31, 2021, 8:31:17 PM1/31/21
to pyqtgraph
It could be that with this code:

Template, BaseClass = pg.Qt.loadUiType(uifilename.as_posix())
self.FileNamingWidget = Template()
widget = BaseClass()
self.FileNamingWidget.setupUi(widget)

then "self.FileNamingWidget" and "widget" are different objects, while defining a new class with:

class DataPanel(QtWidgets.QWidget, loadUiType(__file__.split(".py")[0] + ".ui")[0]):

then an instance of DataPanel is both a subclass of QWidget *and* whatever loadUiType()[0] returns through multiple inheritance. The type returned by the loadUiType(...)[0] magic lets the QWidget use the setupUi() method as if it were "derived from user interface descriptions created using uic" as described in the QWidget.setupUi() method documentation.

And no, I don't use the command line uic/pysideuic tools to generate python files from the .ui, I just build them in QtDesigner and use the loadUiType(...) method. If I have a subwidget which is inserted programmatically into a larger form, then I'll have a small class definition though. So something like this:

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtUiTools import loadUiType

class MainPanel(QtWidgets.QWidget, loadUiType("mainpanel.ui")[0]):
    def __init__(self, parent=None):
    super().__init__(parent)
    self.setupUi(self)
    # ...
    self.subPanel = SubPanel()
    self.layout().addItem(self.subPanel)
    # ...
    # dynamic ui setup for SubPanelWidget, connect signals etc
# ...
class SubPanel(QtWidgets.QFrame, loadUiType("subpanel.ui")[0]):
    def __init__(self, parent=None):
    super().__init__(parent)
    self.setupUi(self)
    # ...
    # other boilerplate ui setup for SubPanelWidget

It's not as nice as being able to use the uic.load() method, but this was the first/only way I got dynamic ui loading with PySide2, so stopped messing around trying to figure out why load() was giving so much trouble.

tjan...@gmail.com

unread,
Feb 1, 2021, 9:07:27 AM2/1/21
to pyqtgraph
Patrick,

(Commenting embedded in your reply)

On Sunday, January 31, 2021 at 8:31:17 PM UTC-5 Patrick wrote:
It could be that with this code:

Template, BaseClass = pg.Qt.loadUiType(uifilename.as_posix())
self.FileNamingWidget = Template()
widget = BaseClass()
self.FileNamingWidget.setupUi(widget)

then "self.FileNamingWidget" and "widget" are different objects, while defining a new class with:

class DataPanel(QtWidgets.QWidget, loadUiType(__file__.split(".py")[0] + ".ui")[0]):

then an instance of DataPanel is both a subclass of QWidget *and* whatever loadUiType()[0] returns through multiple inheritance. The type returned by the loadUiType(...)[0] magic lets the QWidget use the setupUi() method as if it were "derived from user interface descriptions created using uic" as described in the QWidget.setupUi() method documentation.

 
The __file__.split(".py")[0] + ".ui")[0]) is what threw me on my misunderstanding on using pysideuic.  Your loadUiType(__file__.split(".py")[0] + ".ui")[0] is basically my Template, and I think your QtWidgets.QWidget is my BaseClass 

I was trying to  follow the examples on loadUiType() that returns a form and base class  and trying also to use pyqtgraph's Qt.py to handle supporting PyQt5 and PySide2. My MainWIndow gets created fine following the designerExample.py :

with importlib.resources.path('nvfieldcap.resources.ui', 'NVFieldCap.ui') as uiFile:
WindowTemplate, TemplateBaseClass = pg.Qt.loadUiType(uiFile.as_posix())

class MainWindow(TemplateBaseClass):

 def __init__(self, config=None):
TemplateBaseClass.__init__(self)
# Create the main window
self.ui = WindowTemplate()
self.ui.setupUi(self)

 (uiFileName = 'FileNamingWidget.ui' )
uiFileName = self.get_uiFileName(config)
with importlib.resources.path('nvfieldcap.resources.ui', uiFileName) as uifilename:
if 'PySide2' in sys.modules:
    Template, BaseClass = pg.Qt.loadUiType(uifilename.as_posix())

 Template and BaseClass are both classes
Template,BaseClass
(<class 'Ui_Filename_Folder_Widget'>, <class 'PySide2.QtWidgets.QTabWidget'>)
Looking at your code again though, I think I should be doing something like

Template, BaseClass = QtWidgets.QTabWidget.loadUiType(uifilename.as_posix())

My "FileNamingWidget.ui" file starts with:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Filename_Folder_Widget</class>
 <widget class="QTabWidget" name="Filename_Folder_Widget">

Ok, so QTabWidget doesn't have a loadUiType().  Scratch that. I'm reading this uifile inside my MainWIndow.__init__ , so I don't know what my class is yet. (Well, I guess I do know it's a QTabWidget.)

And no, I don't use the command line uic/pysideuic tools to generate python files from the .ui, I just build them in QtDesigner and use the loadUiType(...) method. If I have a subwidget which is inserted programmatically into a larger form, then I'll have a small class definition though. So something like this:

from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtUiTools import loadUiType

class MainPanel(QtWidgets.QWidget, loadUiType("mainpanel.ui")[0]):
    def __init__(self, parent=None):
    super().__init__(parent)
    self.setupUi(self)
    # ...
    self.subPanel = SubPanel()
    self.layout().addItem(self.subPanel)
    # ...
    # dynamic ui setup for SubPanelWidget, connect signals etc
# ...
class SubPanel(QtWidgets.QFrame, loadUiType("subpanel.ui")[0]):
    def __init__(self, parent=None):
    super().__init__(parent)
    self.setupUi(self)
    # ...
    # other boilerplate ui setup for SubPanelWidget

It's not as nice as being able to use the uic.load() method, but this was the first/only way I got dynamic ui loading with PySide2, so stopped messing around trying to figure out why load() was giving so much trouble.


I'll look over this more and see if I can follow your pattern. Thanks.
 
(snip)
TIm
 
 

tjan...@gmail.com

unread,
Feb 1, 2021, 11:28:10 AM2/1/21
to pyqtgraph
I'm starting to think my problem is with the version of PySide2 on my conda environment. I went to compare pyqtgraph.Qt.loadUiType with PySide2.QtUiTools.loadUiType. It's not there:

from PySide2.QtUiTools import loadUiType
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2020.1\plugins\python-ce\helpers\pydev\_pydevd_bundle\pydevd_exec2.py", line 3, in Exec
    exec(exp, global_vars, local_vars)
  File "<input>", line 1, in <module>
ImportError: cannot import name 'loadUiType' from 'PySide2.QtUiTools' (D:\anaconda3\envs\fieldcapenv_pyside\lib\site-packages\PySide2\QtUiTools.cp37-win_amd64.pyd)

PySide2.__version__
'5.13.2'

I need to be at 5.14. 

tjan...@gmail.com

unread,
Feb 1, 2021, 11:32:11 AM2/1/21
to pyqtgraph
pyqtgraph issue #1102

Ognyan Moore

unread,
Feb 1, 2021, 11:35:41 AM2/1/21
to pyqt...@googlegroups.com
On pyside2 5.14.0-5.14.2.1 there were pyuic issues, would recommend going to 5.15.0+ or 5.14.2.2.

--
You received this message because you are subscribed to the Google Groups "pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/2db65699-7066-4807-985e-d1bdec077998n%40googlegroups.com.

tjan...@gmail.com

unread,
Feb 1, 2021, 11:56:33 AM2/1/21
to pyqtgraph
Thanks. Working on that now. The latest version for PySide2 on conda is at 5.13.2 (at least that's what I'm finding - need to check my  channel order)
My python version is at 3.7.8 for other reasons. I'm current trying out a venv and have pip installed PySide2 v5.15.2. 

Still in debugger, but...

from PySide2.QtUiTools import loadUiType as ps2_loadUiType

(PySide2)
ps2_loadUiType(uifilename.as_posix())
(<class '__main__.Ui_Filename_Folder_Widget'>, <class 'PySide2.QtWidgets.QTabWidget'>)

pyqtgraph
pg.Qt.loadUiType(uifilename.as_posix())
(<class 'Ui_Filename_Folder_Widget'>, <class 'PySide2.QtWidgets.QTabWidget'>)

I haven't finished stepping through the debugger, but I'm hopeful.

tjan...@gmail.com

unread,
Feb 1, 2021, 1:46:11 PM2/1/21
to pyqtgraph
Success !!!

        uiFileName = self.get_uiFileName(config)
        with importlib.resources.path('nvfieldcap.resources.ui', uiFileName) as uifilename:
            uifilename = uifilename.as_posix()
            class Filename_Folder_Widget(QtWidgets.QTabWidget, pg.Qt.loadUiType(uifilename)[0]):
                def __init__(self, parent=None):
                    super().__init__(parent)
                    self.setupUi(self)

            if 'PySide2' in sys.modules:
                self.FileNamingWidget = Filename_Folder_Widget()

Probably also works with PyQt5 too. Haven't tested that yet. 
Thanks for all your help!

tjan...@gmail.com

unread,
Feb 1, 2021, 1:49:07 PM2/1/21
to pyqtgraph
Fixed indenting

        with importlib.resources.path('nvfieldcap.resources.ui', uiFileName) as uifilename:
            uifilename = uifilename.as_posix()
        class Filename_Folder_Widget(QtWidgets.QTabWidget, pg.Qt.loadUiType(uifilename)[0]):
            def __init__(self, parent=None):
                super().__init__(parent)
                self.setupUi(self)

        if 'PySide2' in sys.modules:
            self.FileNamingWidget = Filename_Folder_Widget()


Reply all
Reply to author
Forward
0 new messages