如何从 qtreeWidgetItem 中获取 comboBox.currentText() 和 SpinBox.text()

How to fetch comboBox.currentText() and SpinBox.text() from within a treeWidgetItem

上下文:

我有一个 QTreeWidgetItem,我想将其另存为 JSON 文件。 QTreeWidgetItem 由三列组成["Name"、"Type"、"Value"]。名称是行编辑,类型是组合框,值是旋转框。我想检索每个字段和每一行的值,以将它们保存在 JSON

尝试次数:

检索名称没有问题,但我找不到如何从组合框和旋转框检索值。

我首先尝试以最简单的形式检索数据(节点是 QTreeWidgetItem):

type = node.text(1)
value= node.text(2)

我试过 QTreeWidgetItem.data(int column, int role) 但不太理解此方法所需的参数 "role"。

一些代码:

python 文件:

from qgis.PyQt import uic, QtCore, QtGui
import os, sys
import qgis.core
import logging

try:
    from qgis.PyQt.QtGui import (QWidget,QDialog,QMainWindow,QApplication,QTreeWidgetItem,
                                 QMenu,QToolButton,QSpinBox,QDoubleSpinBox,QHeaderView,QLineEdit,
                                 QTextBrowser,QTreeWidgetItemIterator, QComboBox, QLabel, UserRole)
except ImportError:
    from qgis.PyQt.QtWidgets import (QWidget,QDialog,QMainWindow,QApplication,QTreeWidgetItem,
                                     QMenu,QToolButton,QSpinBox,QDoubleSpinBox,QHeaderView,QLineEdit,
                                     QTextBrowser,QTreeWidgetItemIterator, QComboBox, QLabel)

import pprint
import json

class AMCWindow(QDialog):

    def __init__(self, parent=None, dbase=None):
        super(AMCWindow, self).__init__(parent=parent)
        uipath = os.path.join(os.path.dirname(__file__), 'amcwindow1.ui')
        uic.loadUi(uipath, self)
        self.dict = {}

        # treeWidget
        self.treeWidget.setColumnCount(3)
        self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeWidget.customContextMenuRequested.connect(self.openMenu)

        self.maintreewdgitem = QTreeWidgetItem(self.treeWidget.invisibleRootItem(), ['note'])

        headerlist = ["Name", "Type", "Value"]
        self.treeWidget.setHeaderItem(QTreeWidgetItem(headerlist))

        # Right click on criteria
        self.menu = QMenu()
        self.menu.addAction(self.tr("Add criteria"))
        self.menu.addAction(self.tr("Remove criteria"))
        self.menu.triggered.connect(self.menuAction)

        # Click on save
        self.buttonSave.clicked.connect(self.saveClicked)

        self.menuitem = None
        self.itemnameediting = None

    def openMenu(self, position):
        self.menuitem = self.treeWidget.currentItem()
        self.menu.exec_(self.treeWidget.viewport().mapToGlobal(position))

    def menuAction(self, actionname):
        if actionname.text() == self.tr("Add criteria"):
            qtreewidgetitm = QTreeWidgetItem(self.menuitem, ["New criteria"])

            # Type
            qComboBox = QComboBox()
            qComboBox.addItems(["Pondération", "Classe"])
            self.treeWidget.setItemWidget(qtreewidgetitm, 1, qComboBox)
            # qComboBox.setProperty("widgetitem", qtreewidgetitm)

            # Value
            spinbox = QDoubleSpinBox()
            self.treeWidget.setItemWidget(qtreewidgetitm, 2, spinbox)
            self.menuitem.setExpanded(True)

            return qtreewidgetitm

        elif actionname.text() == self.tr("Remove criteria"):
            self.menuitem.parent().removeChild(self.menuitem)

    def saveClicked(self):
        print("> Save clicked")
        self.dict.clear()
        self.visitTree(self.maintreewdgitem, self.dict)

        # Define directory
        directory = "C://Users//jean.robertou//Desktop//file.json"
        # Creates file and writes queryDict as json
        with open(directory, "w") as file:
            json.dump(self.dict, file)

    def visitTree(self, node, dct):
        """
        Iterate over the tree and build dct
        :param node: QTreeWidgetItem, current node
        :param dct: dict, contain elements from current node
        :return:
        """

        print("Enter visitTree")
        print("> visitTree:", node.text(0))

        # Get number of child
        childNr = node.childCount()

        # If node is leaf
        if childNr == 0:
            print(">", node.text(0), "has no child")
            type = node.text(1) # <----- Need help here
            value= node.text(2) # <----- Need help here

            # Set default dict values
            dct.setdefault("Type", type)
            dct.setdefault("Value", value)

        # If node has children
        else:
            print(">", node.text(0), "has children")
            # Add children to dct
            for childItem in range(childNr):
                child = node.child(childItem)
                dct.setdefault(child.text(0), {})
                # Loop through children recursively
                self.visitTree(child, dct[child.text(0)])


        print("Exit visitTree")

GUI 文件(amcwindow1.ui):

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>941</width>
    <height>518</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout" stretch="1,10,1,1">
   <item>
    <widget class="QFrame" name="frame">
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
       <widget class="QLabel" name="label">
        <property name="text">
         <string>Nom</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLineEdit" name="lineEdit"/>
      </item>
      <item>
       <widget class="QToolButton" name="buttonSave">
        <property name="text">
         <string>Save</string>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <widget class="QTreeWidget" name="treeWidget">
     <property name="dragEnabled">
      <bool>true</bool>
     </property>
     <property name="dragDropMode">
      <enum>QAbstractItemView::InternalMove</enum>
     </property>
     <property name="alternatingRowColors">
      <bool>true</bool>
     </property>
     <property name="animated">
      <bool>true</bool>
     </property>
     <property name="wordWrap">
      <bool>true</bool>
     </property>
     <property name="columnCount">
      <number>3</number>
     </property>
     <attribute name="headerDefaultSectionSize">
      <number>50</number>
     </attribute>
     <attribute name="headerStretchLastSection">
      <bool>false</bool>
     </attribute>
     <column>
      <property name="text">
       <string notr="true">1</string>
      </property>
     </column>
     <column>
      <property name="text">
       <string notr="true">2</string>
      </property>
     </column>
     <column>
      <property name="text">
       <string notr="true">3</string>
      </property>
     </column>
    </widget>
   </item>
   <item>
    <widget class="QFrame" name="frame_2">
     <property name="frameShape">
      <enum>QFrame::StyledPanel</enum>
     </property>
     <property name="frameShadow">
      <enum>QFrame::Raised</enum>
     </property>
     <layout class="QGridLayout" name="gridLayout">
      <item row="0" column="1">
       <widget class="QLineEdit" name="lineEdit_sqlfinal"/>
      </item>
      <item row="0" column="0">
       <widget class="QLabel" name="label_2">
        <property name="text">
         <string>Final SQL</string>
        </property>
       </widget>
      </item>
      <item row="0" column="2">
       <widget class="QToolButton" name="toolButton_testsql">
        <property name="text">
         <string>Test SQL</string>
        </property>
       </widget>
      </item>
      <item row="1" column="0" colspan="3">
       <widget class="QTextBrowser" name="textBrowser_res">
        <property name="maximumSize">
         <size>
          <width>16777215</width>
          <height>90</height>
         </size>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <widget class="QDialogButtonBox" name="buttonBox">
     <property name="orientation">
      <enum>Qt::Horizontal</enum>
     </property>
     <property name="standardButtons">
      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>buttonBox</sender>
   <signal>accepted()</signal>
   <receiver>Dialog</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>248</x>
     <y>254</y>
    </hint>
    <hint type="destinationlabel">
     <x>157</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>buttonBox</sender>
   <signal>rejected()</signal>
   <receiver>Dialog</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>316</x>
     <y>260</y>
    </hint>
    <hint type="destinationlabel">
     <x>286</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

预期输出:

单行QTreeWidgetItem,例如: "nameExample" |思考 | 2

JSON 应如下所示:

{"nameExample": {"Type": "Ponderation", "Value": 2}

我对您的代码有以下评论:

  • 我认为您在以下行中有错误:self.treeWidget.setItemWidget(qtreewidgetitm, 2, qComboBox)self.treeWidget.setItemWidget(qtreewidgetitm, 3, spinbox) 因为列是 2 和 3 但 Qt 中的索引从 0 开始所以它们必须分别为 1 和 2。

  • 添加子项时,使用与之前不同的文本,因为在构建字典时会出现问题,因为不会显示重复的元素。对于你的项目的实现可能不是必须的,但是在测试你当前的代码时,看看这个问题。

  • 如果要获取widget显示的数据,必须先使用itemWidget()方法获取widget。

综合以上,解决方案是:

class AMCWindow(QDialog):
    def __init__(self, parent=None, dbase=None):
        super(AMCWindow, self).__init__(parent=parent)
        uipath = os.path.join(os.path.dirname(__file__), "amcwindow1.ui")
        uic.loadUi(uipath, self)
        self.dict = {}

        # treeWidget
        self.treeWidget.setColumnCount(3)
        self.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeWidget.customContextMenuRequested.connect(self.openMenu)

        self.maintreewdgitem = QTreeWidgetItem(
            self.treeWidget.invisibleRootItem(), ["note"]
        )

        headerlist = ["Name", "Type", "Value"]
        self.treeWidget.setHeaderItem(QTreeWidgetItem(headerlist))

        # Right click on criteria
        self.menu = QMenu()
        self.menu.addAction(self.tr("Add criteria"))
        self.menu.addAction(self.tr("Remove criteria"))
        self.menu.triggered.connect(self.menuAction)

        # Click on save
        self.buttonSave.clicked.connect(self.saveClicked)

        self.menuitem = None
        self.itemnameediting = None

        self.counter = 0

    def openMenu(self, position):
        self.menuitem = self.treeWidget.currentItem()
        self.menu.exec_(self.treeWidget.viewport().mapToGlobal(position))

    def menuAction(self, actionname):
        if actionname.text() == self.tr("Add criteria"):
            qtreewidgetitm = QTreeWidgetItem(
                self.menuitem, ["New criteria(%s)" % (self.counter)]
            )
            self.counter += 1

            # Type
            qComboBox = QComboBox()
            qComboBox.addItems(["Pondération", "Classe"])
            self.treeWidget.setItemWidget(qtreewidgetitm, 1, qComboBox)
            # qComboBox.setProperty("widgetitem", qtreewidgetitm)

            # Value
            spinbox = QDoubleSpinBox()
            self.treeWidget.setItemWidget(qtreewidgetitm, 2, spinbox)
            self.menuitem.setExpanded(True)

            return qtreewidgetitm

        elif actionname.text() == self.tr("Remove criteria"):
            self.menuitem.parent().removeChild(self.menuitem)

    def saveClicked(self):
        print("> Save clicked")
        self.dict.clear()
        self.visitTree(self.maintreewdgitem, self.dict)

        # Define directory
        directory = "C://Users//jean.robertou//Desktop//file.json"
        # Creates file and writes queryDict as json
        with open(directory, "w") as file:
            json.dump(self.dict, file)

    def visitTree(self, node, dct):
        """
        Iterate over the tree and build dct
        :param node: QTreeWidgetItem, current node
        :param dct: dict, contain elements from current node
        :return:
        """

        print("Enter visitTree")
        print("> visitTree:", node.text(0))

        # Get number of child
        childNr = node.childCount()

        # If node is leaf
        if childNr == 0:
            print(">", node.text(0), "has no child")
            qComboBox = self.treeWidget.itemWidget(node, 1)
            type = qComboBox.currentText()
            spinbox = self.treeWidget.itemWidget(node, 2)
            value = spinbox.value()
            dct.setdefault("Type", type)
            dct.setdefault("Value", value)
        # If node has children
        else:
            print(">", node.text(0), "has children")
            # Add children to dct
            for childItem in range(childNr):
                child = node.child(childItem)
                dct.setdefault(child.text(0), {})
                # Loop through children recursively
                self.visitTree(child, dct[child.text(0)])

        print("Exit visitTree")