Python QT findChildren 未从 UI 文件中找到任何子项 [英] Python QT findChildren does not find any children from UI file

查看:65
本文介绍了Python QT findChildren 未从 UI 文件中找到任何子项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个这样的 UI 文件

<string>Rover : </string></属性></小部件></项目><项目><widget class="QComboBox" name="cb_rover"><项目><属性名称=文本"><string>哈士奇</string></属性></项目><项目><属性名称=文本"><string>ArgoJ5</string></属性></项目><项目><属性名称=文本"><string>Husky(模拟)</string></属性></项目></小部件></项目></布局></项目><项目><layout class="QHBoxLayout" name="hl_path_creator"><项目><widget class="QLabel" name="lbl_path_creator"><属性名称=文本"><string>Path Creator</string></属性></小部件></项目><项目><widget class="QPushButton" name="btn_add_to_path"><属性名称=文本"><string>添加到路径</string></属性></小部件></项目></布局></项目><项目><layout class="QHBoxLayout" name="hl_segment_options"><项目><layout class="QVBoxLayout" name="vl_segment_type"><项目><widget class="QRadioButton" name="radio_circle"><属性名称=文本"><string>圆</string></属性><属性名称=已检查"><bool>true</bool></属性></小部件></项目><项目><widget class="QRadioButton" name="radio_line"><属性名称=文本"><string>行</string></属性></小部件></项目></布局></项目><项目><layout class="QVBoxLayout" name="vl_segment_extra_options"><项目><widget class="QCheckBox" name="chk_backwards"><属性名称=文本"><string>向后</string></属性></小部件></项目><项目><widget class="QCheckBox" name="chk_clock"><属性名称=文本"><string>顺时针</string></属性><属性名称=已检查"><bool>true</bool></属性></小部件></项目></布局></项目></布局></项目><项目><layout class="QHBoxLayout" name="hl_radius"><项目><widget class="QLabel" name="lbl_radius"><属性名称=文本"><string>圆半径 (m)</string></属性></小部件></项目><项目><widget class="QDoubleSpinBox" name="spin_radius"><属性名称="singleStep"><double>0.0100000000000000</double></属性><属性名称=值"><double>1.000000000000000</double></属性></小部件></项目></布局></项目><项目><layout class="QHBoxLayout" name="hl_arc_angle"><项目><widget class="QLabel" name="lbl_arc_angle"><属性名称=文本"><string>弧角(弧度)</string></属性></小部件></项目><项目><widget class="QDoubleSpinBox" name="spin_arc_angle"><属性名称=小数"><数字>5</数字></属性><属性名称="singleStep"><double>0.0100000000000000</double></属性><属性名称=值"><double>0.785400000000000</double></属性></小部件></项目></布局></项目></布局></项目><项目><layout class="QVBoxLayout" name="vl_segments"><项目><widget class="QLabel" name="lbl_path"><属性名称=文本"><string>路径</string></属性></小部件></项目><项目><widget class="QTreeView" name="tree_paths"/></项目><项目><layout class="QHBoxLayout" name="horizo​​ntalLayout"><项目><widget class="QPushButton" name="btn_save_paths"><属性名称=文本"><string>保存路径</string></属性></小部件></项目><项目><widget class="QPushButton" name="btn_load_paths"><属性名称=文本"><string>加载路径</string></属性></小部件></项目></布局></项目></布局></项目></布局></小部件></小部件><layoutdefault spatial="6" margin="11"/><资源/><连接/></ui>

下面是一些 Python 代码,用于 RQT ROS 插件.

<预><代码>导入操作系统进口玫瑰花导入 rospkg从 PySide2.QtWidgets 导入 QTreeView、QPushButton、QFileDialog从 python_qt_binding 导入 loadUifrom qt_gui.plugin 导入插件从 python_qt_binding.QtWidgets 导入 QWidget从 argparse 导入 ArgumentParserdef get_file():打印(你好世界!")dlg = QFileDialog()dlg.setFileMode(QFileDialog.AnyFile)dlg.setFilter("XML 文件 (*.xml)")如果 dlg.exec_():文件名 = dlg.selectedFiles()f = 打开(文件名 [0],'r')与 f:数据 = f.read()# self.contents.setText(data)类 RoverPlanner(插件):def __init__(self, context):super(RoverPlanner, self).__init__(context)self.setObjectName('RoverPlanner')解析器 = ArgumentParser()parser.add_argument("-q", "--quiet", action="store_true",dest="安静",help="将插件置于静默模式")参数,未知数 = parser.parse_known_args(context.argv())如果不是 args.quiet:打印('参数:',args)打印('未知数:',未知数)self.widget = QWidget()ui_file = os.path.join(rospkg.RosPack().get_path('cuarl-rover-planner'), 'resource', 'rover_planner_widget.ui')loadUi(ui_file, self.widget)self.widget.setObjectName('RoverPlannerUI')# 在每个插件的左上角显示 widget.windowTitle(当# 它在小部件中设置).当您打开多个# 一次插件.此外,如果您打开多个实例#插件一次,这些行添加数字以使其易于# 从窗格告诉窗格.如果 context.serial_number() >1:self.widget.setWindowTitle(self.widget.windowTitle() + (' (%d)' % context.serial_number()))# 向用户界面添加小部件context.add_widget(self.widget)self.tree_paths = self.widget.findChild(QTreeView)self.btn_load_paths = self.widget.findChildren(QPushButton)打印(self.tree_paths)打印(self.btn_load_paths)def shutdown_plugin(self):# TODO 在此处取消注册所有发布者经过def save_settings(self, plugin_settings, instance_settings):# TODO 保存内部配置,通常使用:# instance_settings.set_value(k, v)经过def restore_settings(self, plugin_settings, instance_settings):# TODO 恢复内在配置,通常使用:# v = instance_settings.value(k)经过

根据文档,看来我的代码应该能找到这些孩子.但是这段代码的输出是

('参数:', Namespace(quiet=False))('未知数:', [])没有任何[]

我不知道我是否没有正确使用 findChildren/findChild 函数,或者我遗漏了什么

解决方案

我曾经遇到过类似的问题,原因是库使用了一些像 python_qt_binding 这样的包装器,但另一部分代码使用了 PyQt5 或 PySide2.为什么会这样?因为包装器创建了使用相同代码库的新类,但最终它们是不同的类.

我的建议是,如果您打算使用 python_qt_binding,则不再直接使用 pyqt5 或 pyside2,而是使用包装器,在您的情况下,您应该使用以下内容:

导入操作系统从 argparse 导入 ArgumentParser进口玫瑰花导入 rospkgfrom qt_gui.plugin 导入插件从 python_qt_binding.QtWidgets 导入 QWidget、QTreeView、QPushButton、QFileDialog从 python_qt_binding 导入 loadUi# ....

I have a UI file like this

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>RoverPlanner</class>
 <widget class="QWidget" name="RoverPlanner">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>752</width>
    <height>555</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>RoverPlanner</string>
  </property>
  <widget class="QWidget" name="horizontalLayoutWidget_6">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>10</y>
     <width>641</width>
     <height>501</height>
    </rect>
   </property>
   <layout class="QHBoxLayout" name="hl_rover_planner">
    <item>
     <layout class="QVBoxLayout" name="vl_path_config">
      <item>
       <layout class="QHBoxLayout" name="hl_rover_select">
        <item>
         <widget class="QLabel" name="lbl_rover">
          <property name="text">
           <string>Rover : </string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QComboBox" name="cb_rover">
          <item>
           <property name="text">
            <string>Husky</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>ArgoJ5</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>Husky (Simulated)</string>
           </property>
          </item>
         </widget>
        </item>
       </layout>
      </item>
      <item>
       <layout class="QHBoxLayout" name="hl_path_creator">
        <item>
         <widget class="QLabel" name="lbl_path_creator">
          <property name="text">
           <string>Path Creator</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QPushButton" name="btn_add_to_path">
          <property name="text">
           <string>Add to path</string>
          </property>
         </widget>
        </item>
       </layout>
      </item>
      <item>
       <layout class="QHBoxLayout" name="hl_segment_options">
        <item>
         <layout class="QVBoxLayout" name="vl_segment_type">
          <item>
           <widget class="QRadioButton" name="radio_circle">
            <property name="text">
             <string>Circle</string>
            </property>
            <property name="checked">
             <bool>true</bool>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QRadioButton" name="radio_line">
            <property name="text">
             <string>Line</string>
            </property>
           </widget>
          </item>
         </layout>
        </item>
        <item>
         <layout class="QVBoxLayout" name="vl_segment_extra_options">
          <item>
           <widget class="QCheckBox" name="chk_backwards">
            <property name="text">
             <string>Backwards</string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QCheckBox" name="chk_clockwise">
            <property name="text">
             <string>Clockwise</string>
            </property>
            <property name="checked">
             <bool>true</bool>
            </property>
           </widget>
          </item>
         </layout>
        </item>
       </layout>
      </item>
      <item>
       <layout class="QHBoxLayout" name="hl_radius">
        <item>
         <widget class="QLabel" name="lbl_radius">
          <property name="text">
           <string>Circle Radius (m)</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QDoubleSpinBox" name="spin_radius">
          <property name="singleStep">
           <double>0.010000000000000</double>
          </property>
          <property name="value">
           <double>1.000000000000000</double>
          </property>
         </widget>
        </item>
       </layout>
      </item>
      <item>
       <layout class="QHBoxLayout" name="hl_arc_angle">
        <item>
         <widget class="QLabel" name="lbl_arc_angle">
          <property name="text">
           <string>Arc Angle (rad)</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QDoubleSpinBox" name="spin_arc_angle">
          <property name="decimals">
           <number>5</number>
          </property>
          <property name="singleStep">
           <double>0.010000000000000</double>
          </property>
          <property name="value">
           <double>0.785400000000000</double>
          </property>
         </widget>
        </item>
       </layout>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QVBoxLayout" name="vl_segments">
      <item>
       <widget class="QLabel" name="lbl_path">
        <property name="text">
         <string>Paths</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QTreeView" name="tree_paths"/>
      </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout">
        <item>
         <widget class="QPushButton" name="btn_save_paths">
          <property name="text">
           <string>Save Paths</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QPushButton" name="btn_load_paths">
          <property name="text">
           <string>Load Paths</string>
          </property>
         </widget>
        </item>
       </layout>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

And some Python code below, which is for an RQT ROS plugin.


import os
import rospy
import rospkg
from PySide2.QtWidgets import QTreeView, QPushButton, QFileDialog
from python_qt_binding import loadUi

from qt_gui.plugin import Plugin
from python_qt_binding.QtWidgets import QWidget
from argparse import ArgumentParser

def get_file():
    print("Hello World!")
    dlg = QFileDialog()
    dlg.setFileMode(QFileDialog.AnyFile)
    dlg.setFilter("XML files (*.xml)")

    if dlg.exec_():
        filenames = dlg.selectedFiles()
        f = open(filenames[0], 'r')

        with f:
            data = f.read()
            # self.contents.setText(data)


class RoverPlanner(Plugin):
    def __init__(self, context):
        super(RoverPlanner, self).__init__(context)
        self.setObjectName('RoverPlanner')
        parser = ArgumentParser()
        parser.add_argument("-q", "--quiet", action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print('arguments: ', args)
            print('unknowns: ', unknowns)

        self.widget = QWidget()
        ui_file = os.path.join(rospkg.RosPack().get_path('cuarl-rover-planner'), 'resource', 'rover_planner_widget.ui')
        loadUi(ui_file, self.widget)
        self.widget.setObjectName('RoverPlannerUI')
        # Show widget.windowTitle on left-top of each plugin (when
        # it's set in widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self.widget.setWindowTitle(self.widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self.widget)
        self.tree_paths = self.widget.findChild(QTreeView)
        self.btn_load_paths = self.widget.findChildren(QPushButton)
        print(self.tree_paths)
        print(self.btn_load_paths)

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

According to the documentation, it seems that my code should find these children. But the output of this code is

('arguments: ', Namespace(quiet=False))
('unknowns: ', [])
None
[]

I don't know if I'm not using the findChildren/findChild functions correctly or there's something that I am missing

解决方案

I ever encountered a similar problem and the cause was that the library used some wrapper like python_qt_binding but another part of the code used PyQt5 or PySide2. Why does that happen? because wrappers create new classes that use the same code base but in the end they are different classes.

My recommendation is that if you are going to use python_qt_binding then no longer use pyqt5 or pyside2 directly but rather the wrapper, in your case you should use the following:

import os

from argparse import ArgumentParser

import rospy
import rospkg

from qt_gui.plugin import Plugin

from python_qt_binding.QtWidgets import QWidget, QTreeView, QPushButton, QFileDialog
from python_qt_binding import loadUi

# ....

这篇关于Python QT findChildren 未从 UI 文件中找到任何子项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆