Consider the following Python2/Python3 example program, example.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
try:
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot
from PyQt5.uic import loadUi
except:
try:
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtCore import Signal, Slot
from uic import loadUi
except:
raise ModuleNotFoundError("No Qt5 support.")
def _iterate_all_qobjects(obj):
yield obj
for subobj in obj.children():
for childobj in _iterate_all_qobjects(subobj):
yield childobj
class Ui(QtCore.QObject):
"""Graphical User interface class"""
def __init__(self, uifile, parent=None):
QtCore.QObject.__init__(self, parent)
self.ui = loadUi(uifile)
def show(self):
self.ui.show()
def allTextWidgets(self):
for widget in _iterate_all_qobjects(self.ui):
if len(widget.objectName()) < 1:
continue
if isinstance(widget, QtWidgets.QLineEdit):
yield (widget.objectName(), widget.text())
elif isinstance(widget, QtWidgets.QPlainTextEdit):
yield (widget.objectName(), widget.toPlainText())
elif isinstance(widget, QtWidgets.QTextEdit):
yield (widget.objectName(), widget.toPlainText())
if __name__ == '__main__':
if len(sys.argv) < 2 or '-h' in sys.argv[1:] or '--help' in sys.argv[1:]:
if len(sys.argv) > 0 and len(sys.argv[0]) > 0:
this = sys.argv[0]
else:
this = '(this)'
sys.stderr.write('\n')
sys.stderr.write('Usage: %s [ -h | --help ]\n' % this)
sys.stderr.write(' %s UI-FILE [ textwidgetname=contents ]\n' % this)
sys.stderr.write('\n')
sys.stderr.write('When you close the window, the contents of all named text widgets are shown.\n')
sys.stderr.write('\n')
sys.exit(0)
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
app = QtWidgets.QApplication(sys.argv)
win = Ui(sys.argv[1])
win.show()
# Set text widget contents
if len(sys.argv) > 2:
for arg in sys.argv[2:]:
if '=' in arg:
name, value = arg.split('=', 2)
widget = win.ui.findChild(QtCore.QObject, name)
if widget is None:
sys.stderr.write("%s: No such widget in %s." % (name, sys.argv[1]))
elif isinstance(widget, (QtWidgets.QLineEdit, QtWidgets.QLabel)):
widget.setText(value)
elif isinstance(widget, (QtWidgets.QPlainTextEdit, QtWidgets.QTextEdit)):
widget.setDocument(QtGui.QTextDocument(value))
else:
sys.stderr.write("%s: Widget is %s, not a text widget.\n" % (name, type(widget)))
status = app.exec_()
for pair in win.allTextWidgets():
print('%s: "%s"' % pair)
del win, app
sys.exit(status)
If you use PySide2, put the uic.py I showed earlier in this thread into the same directory.
When you run it, give it the path to an .ui file, and optionally one or more name=text pairs.
Here is again the simple main.ui I used for testing:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Ui</class>
<widget class="QWidget" name="Ui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>368</width>
<height>172</height>
</rect>
</property>
<property name="windowTitle">
<string>Serial Example</string>
</property>
<layout class="QGridLayout" name="layout" rowstretch="0,0,1" columnstretch="0,1,0,0">
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="deviceLabel">
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="responseLabel">
<property name="text">
<string>Response:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="deviceEdit">
<property name="text">
<string>/dev/ttyACM0</string>
</property>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="commandLabel">
<property name="text">
<string>Command:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="connectButton">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="disconnectButton">
<property name="text">
<string>Disconnect</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="commandEdit"/>
</item>
<item row="2" column="1" colspan="3">
<widget class="QTextEdit" name="responseText">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Run it using e.g. python example.py main.ui. Note the initial contents of the fields.
In example.py, function _iterate_all_qobjects(obj) is a generator function which yields each object in the qobject tree recursively.
When you close the window, the allTextWidgets() generator method of the Ui class iterates over all widgets in the Ui hierarchy, skipping widgets without names, and generates tuples of (text widget name, editable text widget contents). In the main, these are simply printed to standard output.
When the command line also contains name=text pairs, the interesting magic happens. For example, try running
python example.py main.ui deviceEdit=/dev/null commandEdit=Nothing responseText=Hey
This is done in the snippet beginning with comment # Set text widget contents.
The first four lines split the command-line parameter to a name=value pair.
The fifth line finds the QObject in the UI hierarchy having that name.
QLineEdit and QLabel widgets provide a setText() method that we use to set the contents.
QPlainTextEdit and QTextEdit widgets provide a setDocument() method that we use, first constructing a suitable QtGui.QTextDocument for it (its constructor takes the plain text contents as a string).
So, as you can see, with uic.loadUi() this works just fine. Note that PySide2 nor the uic.py shim that I've shown in this thread, *does not* provide loadUiType(), only loadUi().