从字典中动态创建PYQT5 UI表单,然后使用新字典更新UI



摘要:

我正在尝试制作一个pyqt5 UI,它从json文件读取字典并动态创建一个可编辑的表单。然后我希望能够更改json文件并更新我的表单。

我尝试过的:

我认为最简单的解决方案就是以某种方式销毁ui,并用一个新的字典重新初始化ui,我已经试着理解了这篇文章,但我不确定如何修改这个答案以重新启动,然后用一个新字典实例化一个新ui类?

我也读过一些类似于这篇文章的帖子,我认为我可以使用这些帖子首先删除我的所有小部件,然后删除我的布局,然后从新字典中重新添加新的小部件和布局,但我想知道这是否只是使问题过于复杂了?

一些示例代码:

import sys
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QFormLayout
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QApplication
class JsonEditor(QMainWindow):
def __init__(self, dictionary):
super().__init__()
self.dictionary = dictionary
self.setWindowTitle("Main Window")
self.setGeometry(200, 200, 800, 100)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.main_layout = QVBoxLayout(self.main_widget)
self.createDynamicForm()
self.createUpdateButton()
def createDynamicForm(self):
self.dynamiclayout = QFormLayout()
self.dynamic_dictionary = {}
for key, value in self.dictionary.items():
self.dynamic_dictionary[key] = QLineEdit(value)
self.dynamiclayout.addRow(key, self.dynamic_dictionary[key])
self.main_layout.addLayout(self.dynamiclayout)
def createUpdateButton(self):
self.update_button = QPushButton('update')
self.main_layout.addWidget(self.update_button)
self.update_button.clicked.connect(self.updateDictionary)
def updateDictionary(self):
dictionary2 = {}
dictionary2['foo2'] = 'foo_string2'
dictionary2['bar2'] = 'bar_string2'
dictionary2['foo_bar'] = 'foo_bar_string2'
self.dictionary = dictionary2

dictionary1 = {}
dictionary1['foo'] = 'foo_string'
dictionary1['bar'] = 'bar_string'

if __name__ == "__main__":
test_app = QApplication(sys.argv)
MainWindow = JsonEditor(dictionary1)
MainWindow.show()
sys.exit(test_app.exec_())

我想我正处于学习的阶段,我可能不知道该问什么正确的问题,也不知道如何正确描述术语,所以我希望这是有意义的。

只要您使用QFormLayout,就可以考虑使用removeRow(),并在添加新窗口小部件(即使是第一次)之前执行。请注意,布局必须在该函数之外创建,这样您就可以随时根据需要重用它。

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QFormLayout, 
QMainWindow, QLineEdit, QPushButton)
class JsonEditor(QMainWindow):
def __init__(self, dictionary):
super().__init__()
self.dictionary = dictionary.copy()
self.setWindowTitle("Main Window")
central = QWidget(self)
self.setCentralWidget(central)
self.main_layout = QVBoxLayout(central)
self.form_layout = QFormLayout()
self.main_layout.addLayout(self.form_layout)
self.createDynamicForm()
self.createUpdateButton()
def createDynamicForm(self):
while self.form_layout.rowCount():
self.form_layout.removeRow(0)
self.dynamic_dictionary = {}
for key, value in self.dictionary.items():
self.dynamic_dictionary[key] = QLineEdit(value)
self.form_layout.addRow(key, self.dynamic_dictionary[key])
QApplication.processEvents()
self.adjustSize()
def createUpdateButton(self):
self.update_button = QPushButton('update')
self.main_layout.addWidget(self.update_button)
self.update_button.clicked.connect(self.updateDictionary)
def updateDictionary(self):
dictionary2 = {}
dictionary2['foo2'] = 'foo_string2'
dictionary2['bar2'] = 'bar_string2'
dictionary2['foo_bar'] = 'foo_bar_string2'
self.dictionary = dictionary2
self.createDynamicForm()

正如评论中所建议的,我试用了Qtable小部件。在这个脚本中,表生成器刷新、删除所有表行,然后从字典中构建表。

它将第一张桌子作为一个表演活动,尽管我认为最终并不需要这样做。我认为这可能是一种在关闭和重新打开窗口时重建桌子的方法,正如评论中所建议的那样。最后,我认为刷新按钮本身就足够了,而无需关闭窗口。

我所做的研究来自Chris Zurbrigg在pyside2中的Table小部件上的一段训练视频。

from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMainWindow, QLineEdit, QPushButton,
QTableWidget, QTableWidgetItem, QHeaderView)
class TableEditUI(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('table edit')
self.setMinimumWidth(500)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.create_widgets()
self.create_layouts()
self.toggle = True
self.set_dictionary()
self.refresh_table()
self.create_connections()
def create_widgets(self):
self.table = QTableWidget()
self.table.setColumnCount(2)
self.table.setHorizontalHeaderLabels(["Attribute", "Data"])
header_view = self.table.horizontalHeader()
header_view.setSectionResizeMode(1, QHeaderView.Stretch)
self.toggle_dictionary_btn = QPushButton('toggle dictionary')
def refresh_table(self):
self.table.setRowCount(0)
self.dynamic_dictionary = {}
for count, (key, value) in enumerate(self.dictionary.items()):
print(count, key, value)
self.dynamic_dictionary[key] = QLineEdit(value)
self.table.insertRow(count)
self.insert_item(count, 0, str(key))
self.table.setCellWidget(count, 1, self.dynamic_dictionary[key])
def insert_item(self, row, column, text):
item = QTableWidgetItem(text)
self.table.setItem(row, column, item)
def showEvent(self, e):
super(TableEditUI, self).showEvent(e)
self.refresh_table
def create_layouts(self):
self.main_layout = QVBoxLayout(self.main_widget)
self.main_layout.addWidget(self.table)
self.main_layout.addWidget(self.toggle_dictionary_btn)
def create_connections(self):
self.toggle_dictionary_btn.clicked.connect(self.set_dictionary)
def set_dictionary(self):
self.toggle = not self.toggle
dictionary1 = {}
dictionary1['foo'] = 'foo_string'
dictionary1['bar'] = 'bar_string'
dictionary2 = {}
dictionary2['foo2'] = 'foo_string2'
dictionary2['bar2'] = 'bar_string2'
dictionary2['foo_bar'] = 'foo_bar_string2'
if self.toggle:
self.dictionary = dictionary1
else:
self.dictionary = dictionary2
self.refresh_table()

if __name__ == "__main__":
app = QApplication(sys.argv)
TableEditUIWindow = TableEditUI()
TableEditUIWindow.show()
sys.exit(app.exec())

解决方案1

我修改你的代码,删除self.dynamiclayout并重新构建它。只需使用你发布的链接。

您只需要在updateDictionary()函数中添加deleteItemsOfLayout(self.dynamiclayout)self.createDynamicForm()的调用。另一个小的修改是将createDynamicForm()函数中的self.main_layout.addLayout(self.dynamiclayout)更改为self.main_layout.insertLayout(0, self.dynamiclayout),以保持self.main_layout中元素的顺序。

import sys
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QFormLayout
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QApplication

# copy from https://stackoverflow.com/a/45790404/9758790
def deleteItemsOfLayout(layout):
if layout is not None:
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget is not None:
widget.setParent(None)
else:
deleteItemsOfLayout(item.layout())
class JsonEditor(QMainWindow):
def __init__(self, dictionary):
super().__init__()
self.dictionary = dictionary
self.setWindowTitle("Main Window")
self.setGeometry(200, 200, 800, 100)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.main_layout = QVBoxLayout(self.main_widget)
self.createDynamicForm()
self.createUpdateButton()
def createDynamicForm(self):
self.dynamiclayout = QFormLayout()
self.dynamic_dictionary = {}
for key, value in self.dictionary.items():
self.dynamic_dictionary[key] = QLineEdit(value)
self.dynamiclayout.addRow(key, self.dynamic_dictionary[key])
self.main_layout.insertLayout(0, self.dynamiclayout)
def createUpdateButton(self):
self.update_button = QPushButton('update')
self.main_layout.addWidget(self.update_button)
self.update_button.clicked.connect(self.updateDictionary)
def updateDictionary(self):
dictionary2 = {}
dictionary2['foo2'] = 'foo_string2'
dictionary2['bar2'] = 'bar_string2'
dictionary2['foo_bar'] = 'foo_bar_string2'
self.dictionary = dictionary2
deleteItemsOfLayout(self.dynamiclayout)
self.createDynamicForm()
dictionary1 = {}
dictionary1['foo'] = 'foo_string'
dictionary1['bar'] = 'bar_string'

if __name__ == "__main__":
test_app = QApplication(sys.argv)
MainWindow = JsonEditor(dictionary1)
MainWindow.show()
sys.exit(test_app.exec_())

解决方案2

您也可以重新构建整个test_app。然而,

  1. 通过这种方式,我认为必须使dictionary成为全局变量,而不是类JsonEditor的属性。老实说,我认为这并不高雅
  2. 此外,整个应用程序的重新启动可能是不必要的,也许您只需要关闭()窗口并按照@musicamante的建议创建一个新窗口。我不知道如何再次创建它。我想知道小部件是否有可能关闭()并重新构建自己,如果没有,您需要将JsonEditor添加到父小部件中,并在那里关闭/重建JsonEditor。我认为这会很麻烦
  3. 注意,窗口将"打开";闪光;(消失并再次出现)在溶液2中
import sys
from PyQt5.QtWidgets import QWidget, qApp
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QFormLayout
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore
class JsonEditor(QMainWindow):
def __init__(self, dictionary):
super().__init__()
self.dictionary = dictionary
self.setWindowTitle("Main Window")
self.setGeometry(200, 200, 800, 100)
self.main_widget = QWidget(self)
self.setCentralWidget(self.main_widget)
self.main_layout = QVBoxLayout(self.main_widget)
self.createDynamicForm()
self.createUpdateButton()
def createDynamicForm(self):
self.dynamiclayout = QFormLayout()
self.dynamic_dictionary = {}
for key, value in self.dictionary.items():
self.dynamic_dictionary[key] = QLineEdit(value)
self.dynamiclayout.addRow(key, self.dynamic_dictionary[key])
self.main_layout.addLayout(self.dynamiclayout)
def createUpdateButton(self):
self.update_button = QPushButton('update')
self.main_layout.addWidget(self.update_button)
self.update_button.clicked.connect(self.updateDictionary)
def updateDictionary(self):
dictionary2 = {}
dictionary2['foo2'] = 'foo_string2'
dictionary2['bar2'] = 'bar_string2'
dictionary2['foo_bar'] = 'foo_bar_string2'
global dictionary
dictionary = dictionary2
qApp.exit(EXIT_CODE_REBOOT)

dictionary1 = {}
dictionary1['foo'] = 'foo_string'
dictionary1['bar'] = 'bar_string'
dictionary = dictionary1
EXIT_CODE_REBOOT = -11231351
if __name__ == "__main__":
exitCode = 0
while True:
test_app = QApplication(sys.argv)
MainWindow = JsonEditor(dictionary)
MainWindow.show()
exitCode = test_app.exec_()
test_app = None
if exitCode != EXIT_CODE_REBOOT:
break

事实上,这是我第一次尝试重新启动QtGUI。我提到了这个答案,但它并没有像我在下面的评论中所说的那样直接起作用。这个答案很好,在我的答案中被采纳了。我也尝试过这个答案,但是,如果使用这个实现,应用程序配置将保持不变。

最新更新