Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to save my form and load it later, but even after following the Qt docs (https://doc.qt.io/qt-5/qabstractformbuilder.html), I just get an empty window and an error message: "QFormBuilder was unable to create a widget of the class 'Window'." when loading the .ui file.

Here is a saver:

from PyQt5.QtDesigner import QFormBuilder
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class Window(QMainWindow):

    def __init__(self):
        super().__init__()
        
        label = QLabel(self, text = 'myForm')
        self.setWindowTitle('myTitle')
        
        formBuilder = QFormBuilder()
        file = QFile('line_up.ui')
        file.open(QFile.WriteOnly)
        formBuilder.save(file, self)
        file.close()

app = QApplication([])
mainWindow = Window()
mainWindow.show()
app.exec() 

and here is my loader:

from PyQt5.QtCore import QFile
from PyQt5.QtDesigner import QFormBuilder
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout

#from Qt docs
class MyForm(QWidget):
    def __init__(self):
        super().__init__()

        formBuilder = QFormBuilder()
        file = QFile('line_up.ui')
        file.open(QFile.ReadOnly)
        widget = QWidget()
        widget = formBuilder.load(file, self)
        file.close()

##

app = QApplication([])
mainWindow = MyForm()
mainWindow.show()
app.exec()

and here is the .ui file:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class></class>
 <widget class="Window">
  <property name="styleSheet">
   <string notr="true"/>
  </property>
  <property name="unifiedTitleAndToolBarOnMac">
   <bool>false</bool>
  </property>
  <property name="baseSize">
   <size>
    <width>0</width>
    <height>0</height>
   </size>
  </property>
  <property name="palette">
   <palette>
    <active/>
    <inactive/>
    <disabled/>
   </palette>
  </property>
  <property name="sizeIncrement">
   <size>
    <width>0</width>
    <height>0</height>
   </size>
  </property>
  <property name="windowModality">
   <enum>Qt::NonModal</enum>
  </property>
  <property name="tabShape">
   <enum>QTabWidget::Rounded</enum>
  </property>
  <property name="font">
   <font/>
  </property>
  <property name="maximumWidth">
   <number>16777215</number>
  </property>
  <property name="windowTitle">
   <string>myTitle</string>
  </property>
  <property name="size" stdset="0">
   <size>
    <width>640</width>
    <height>480</height>
   </size>
  </property>
  <property name="layoutDirection">
   <enum>Qt::LeftToRight</enum>
  </property>
  <property name="dockNestingEnabled">
   <bool>false</bool>
  </property>
  <property name="maximumSize">
   <size>
    <width>16777215</width>
    <height>16777215</height>
   </size>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>640</width>
    <height>480</height>
   </rect>
  </property>
  <property name="whatsThis">
   <string/>
  </property>
  <property name="cursor">
   <cursorShape>ArrowCursor</cursorShape>
  </property>
  <property name="sizePolicy">
   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
    <horstretch>0</horstretch>
    <verstretch>0</verstretch>
   </sizepolicy>
  </property>
  <property name="focusPolicy">
   <enum>Qt::NoFocus</enum>
  </property>
  <property name="enabled">
   <bool>true</bool>
  </property>
  <property name="autoFillBackground">
   <bool>false</bool>
  </property>
  <property name="windowModified">
   <bool>false</bool>
  </property>
  <property name="updatesEnabled">
   <bool>true</bool>
  </property>
  <property name="iconSize">
   <size>
    <width>24</width>
    <height>24</height>
   </size>
  </property>
  <property name="toolTipDuration">
   <number>-1</number>
  </property>
  <property name="tabletTracking">
   <bool>false</bool>
  </property>
  <property name="contextMenuPolicy">
   <enum>Qt::DefaultContextMenu</enum>
  </property>
  <property name="locale">
   <locale language="English" country="UnitedKingdom"/>
  </property>
  <property name="windowIconText">
   <string/>
  </property>
  <property name="toolTip">
   <string/>
  </property>
  <property name="accessibleName">
   <string/>
  </property>
  <property name="documentMode">
   <bool>false</bool>
  </property>
  <property name="pos" stdset="0">
   <point>
    <x>0</x>
    <y>0</y>
   </point>
  </property>
  <property name="minimumWidth">
   <number>0</number>
  </property>
  <property name="windowFilePath">
   <string/>
  </property>
  <property name="maximumHeight">
   <number>16777215</number>
  </property>
  <property name="objectName">
   <string notr="true"/>
  </property>
  <property name="toolButtonStyle">
   <enum>Qt::ToolButtonIconOnly</enum>
  </property>
  <property name="windowOpacity">
   <double>1.000000000000000</double>
  </property>
  <property name="mouseTracking">
   <bool>false</bool>
  </property>
  <property name="statusTip">
   <string/>
  </property>
  <property name="accessibleDescription">
   <string/>
  </property>
  <property name="inputMethodHints">
   <enum>Qt::ImhNone</enum>
  </property>
  <property name="acceptDrops">
   <bool>false</bool>
  </property>
  <property name="minimumSize">
   <size>
    <width>0</width>
    <height>0</height>
   </size>
  </property>
  <property name="minimumHeight">
   <number>0</number>
  </property>
  <property name="animated">
   <bool>true</bool>
  </property>
  <property name="visible">
   <bool>false</bool>
  </property>
  <layout class="QMainWindowLayout" name="_layout">
   <property name="objectName">
    <string notr="true">_layout</string>
   </property>
   <property name="margin">
    <number>11</number>
   </property>
   <property name="sizeConstraint">
    <enum>QLayout::SetDefaultConstraint</enum>
   </property>
   <property name="spacing">
    <number>6</number>
   </property>
  </layout>
  <widget class="QLabel">
   <property name="styleSheet">
    <string notr="true"/>
   </property>
   <property name="textFormat">
    <enum>Qt::AutoText</enum>
   </property>
   <property name="baseSize">
    <size>
     <width>0</width>
     <height>0</height>
    </size>
   </property>
   <property name="text">
    <string>myForm</string>
   </property>
   <property name="palette">
    <palette>
     <active/>
     <inactive/>
     <disabled/>
    </palette>
   </property>
   <property name="sizeIncrement">
    <size>
     <width>0</width>
     <height>0</height>
    </size>
   </property>
   <property name="windowModality">
    <enum>Qt::NonModal</enum>
   </property>
   <property name="font">
    <font/>
   </property>
   <property name="maximumWidth">
    <number>16777215</number>
   </property>
   <property name="margin">
    <number>0</number>
   </property>
   <property name="windowTitle">
    <string/>
   </property>
   <property name="size" stdset="0">
    <size>
     <width>100</width>
     <height>30</height>
    </size>
   </property>
   <property name="layoutDirection">
    <enum>Qt::LeftToRight</enum>
   </property>
   <property name="openExternalLinks">
    <bool>false</bool>
   </property>
   <property name="maximumSize">
    <size>
     <width>16777215</width>
     <height>16777215</height>
    </size>
   </property>
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>100</width>
     <height>30</height>
    </rect>
   </property>
   <property name="whatsThis">
    <string/>
   </property>
   <property name="cursor">
    <cursorShape>ArrowCursor</cursorShape>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="focusPolicy">
    <enum>Qt::NoFocus</enum>
   </property>
   <property name="frameShadow">
    <enum>QFrame::Plain</enum>
   </property>
   <property name="textInteractionFlags">
    <enum>Qt::LinksAccessibleByMouse</enum>
   </property>
   <property name="enabled">
    <bool>true</bool>
   </property>
   <property name="frameShape">
    <enum>QFrame::NoFrame</enum>
   </property>
   <property name="autoFillBackground">
    <bool>false</bool>
   </property>
   <pr

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
257 views
Welcome To Ask or Share your Answers For Others

1 Answer

First of all, keep in mind that QFormBuilder is not normally intended to be used to restore the state of a widget, as it was created to save and restore contents of Qt Designer plugins.

With this in mind, consider that using it is not guaranteed to work, especially if you're using widgets that are not part of the standard Qt library or if you're using promoted widgets (it can be done, but they must be registered in the QFormLayout instance before that).

Unless you're using a very complex and extended interface with advanced drag&drop features to create the UI, I would discourage you to use this approach.

Now, the problem is that QFormBuilder.load() returns a widget that has the last argument given as its parent. In your case, this a problem for the following reasons:

  1. A QMainWindow is not intended to be a children of another widget.
  2. QFormBuilder.save() saves the target widget with its class name, since you're using a custom class (Window), Qt doesn't know anything about the Window class.

The easy workaround to your issue is to import that class at the beginning of your loader:

from saver import Window

But I'd suggest you to use a better approach.

First of all, a QMainWindow should always use a central widget, and everything should be a child of that widget.

Then, use a QMainWindow for the main class of your loader too, and load the new widget as the new central widget.

Also, remember that "free widgets" (widgets that are not added to a layout manager) are not shown if the widget has already been shown, and considering that all properties of a widget are saved by the QFormBuilder and you saved the form before showing the window, the result is that the children won't be shown anyway (because they have the visible property explicitly set as false).

Finally, I'm assuming that you want to save the QMainWindow in its entirety. Unfortunately, doing what you tried is not valid, for two reasons:

  1. A QMainWindow should have no parent, so you cannot try to use a "subclassed" object to load it and possibly implement your own methods
  2. Even if you try to create it as a child of the MyForm instance, you'll face lots of problems, most importantly you'll loose many of the features of a main window and will probably have unconsistent or unexpected behavior.

Depending on your needs, you could use a centralized QMainWindow subclass that can do both saving and restoring, or you could differentiate them and implement what you need.

In any case, remember that the saved and loaded object should be the central widget.

from PyQt5.QtDesigner import QFormBuilder
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from random import randrange

class Window(QMainWindow):
    startPos = None
    def __init__(self):
        super().__init__()
        self.setMinimumSize(400, 300)

        fileMenu = self.menuBar().addMenu('File')
        restoreAction = fileMenu.addAction('Restore')
        restoreAction.triggered.connect(self.restore)
        saveAction = fileMenu.addAction('Save')
        saveAction.triggered.connect(self.save)

        central = QWidget()
        self.setCentralWidget(central)

        for i in range(5):
            label = QLabel('label {}'.format(i + 1), central)
            label.move(randrange(300), randrange(200))
            label.installEventFilter(self)

    def restore(self):
        file = QFile('line_up.ui')
        file.open(QFile.ReadOnly)
        newWidget = QFormBuilder().load(file, self)
        self.setCentralWidget(newWidget)

    def save(self):        
        file = QFile('line_up.ui')
        file.open(QFile.WriteOnly)
        QFormBuilder().save(file, self.centralWidget())
        file.close()

    def eventFilter(self, source, event):
        if event.type() == event.MouseButtonPress and event.button() == Qt.LeftButton:
            self.startPos = event.pos()
        elif event.type() == event.MouseMove and self.startPos:
            source.move(source.pos() + (event.pos() - self.startPos))
            return True
        elif event.type() == event.MouseButtonRelease:
            self.startPos = None
        return super().eventFilter(source, event)

app = QApplication([])
mainWindow = Window()
mainWindow.show()
app.exec()

A revised version of your examples:

# saver.py
class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        central = QWidget()
        self.setCentralWidget(central)

        label = QLabel(central, text='myForm')
        self.setWindowTitle('myTitle')
        self.show()
        
        formBuilder = QFormBuilder()
        file = QFile('line_up.ui')
        file.open(QFile.WriteOnly)
        formBuilder.save(file, central)
        file.close()


# loader.py
class MyForm(QMainWindow):
    def __init__(self):
        super().__init__()

        formBuilder = QFormBuilder()
        file = QFile('line_up.ui')
        file.open(QFile.ReadOnly)
        widget = formBuilder.load(file, self)
        file.close()
        self.setCentralWidget(widget)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...