【问题标题】:How to use custom widget and uic.loadUi without importing custom widget class package?如何在不导入自定义小部件类包的情况下使用自定义小部件和 uic.loadUi?
【发布时间】:2016-08-05 07:48:28
【问题描述】:

使用 PyQt4 的 uic.loadUi,我想加载一个 .ui 文件并在其中使用自定义小部件。这意味着使用uic.loadUi 的第三个package 参数将导入包含自定义小部件类的包。

但是,我希望在与我调用uic.loadUi 的文件相同的文件中定义自定义小部件的类。我正在尝试这样实现:

class MyCustomClass(QtWidgets.QPushButton):
    """ This is my custom class for my custom widget """
    def __init__(self, *args):
        QtWidgets.QPushButton.__init__(self, *args)

...

sys.modules['mycustompackage'] = MyCustomClass
uic.loadUi('my_ui.ui', self, 'mycustompackage')  # Loads .ui file which contains the MyCustomWidget widget

但是,这会返回以下错误:

AttributeError: type object 'MyCustomClass' has no attribute 'MyCustomWidget'

我可以做些什么来让它真正发挥作用吗?我怀疑MyCustomClass 的定义不是uic.loadUi 所期望的。

在 Qt Designer 中,我提升了MyCustomWidget

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="MyCustomWidget" name="customWidget">
    <property name="geometry">
     <rect>
      <x>50</x>
      <y>70</y>
      <width>113</width>
      <height>32</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>MyCustomWidget</class>
   <extends>QPushButton</extends>
   <header>MyCustomClass</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

解决方案

我使用上面的 .ui 文件解决了这个问题:

class MyCustomClasses(object):
    class MyCustomWidget(QtWidgets.QPushButton):
        def __init__(self, *args):
            QtWidgets.QPushButton.__init__(self, *args)

...

sys.modules['MyCustomClasses'] = MyCustomClasses
uic.loadUi('my_ui.ui', self)  # Loads .ui file which contains MyCustomWidget

【问题讨论】:

    标签: python pyqt pyqt4 pyqt5


    【解决方案1】:

    引用您链接到的文档,loadUi 的第三个参数是:

    作为任何相对导入自定义小部件的基础包的可选包[强调]

    导入自定义类的实际模块名称必须在ui 文件本身中指定。在Qt Designer中,这是通过将“头文件”设置为适当的值来实现的,它将存储在ui文件内的&lt;header&gt;标签中。请注意,此值可以是模块的完全限定包路径(例如“pkg.mymodule”) - 在这种情况下,没有必要使用loadUi 的第三个参数。永远不需要sys.module hacks。

    loadUi 函数非常简单。它只是以与命令行工具完全相同的方式生成一个 python 模块,然后使用exec 加载它。

    【讨论】:

      【解决方案2】:

      三种可能的方式。 例如,您有模块 QtCustomWidgets.widgets.mybutton 它是您项目中的文件 QtCustomWidgets/widgets/mybutton.pyQtCustomWidgets/python/mybuttonplugin.py,其中包含 MyButton 类。

      第一种方式QtCustomWidgets/python/mybuttonplugin.py 中的 includeFile 方法定义为:

      def includeFile(self):
          return "QtCustomWidgets.widgets.mybutton"
      

      第二种方法是使用带有包路径的 uic.loadUi: uic.loadUi('my_ui.ui', self, packadge='QtCustomWidgets.widgets')

      但您必须在模块名称中使用点(includeFile 是来自 QtCustomWidgets/python/mybuttonplugin.py 的方法):

      def includeFile(self):
          return ".mybutton"
      

      ,所以在标题中它必须是这样的:

      <customwidgets>
        <customwidget>
         <class>MyButton</class>
         <extends>QPushButton</extends>
         <header>.mybutton</header>
        </customwidget>
       </customwidgets>
      

      结果方式仍然是 "QtCustomWidgets.widgets" + ".mybutton"

      有源码PyQt代码自定义小部件加载器(qobjectcreator.py),你可以自己找:

      class _CustomWidgetLoader(object):
          def __init__(self, package):
              # should it stay this way?
              if '.' not in sys.path:
                  sys.path.append('.')
      
              self._widgets = {}
              self._modules = {}
              self._package = package
      
          def addCustomWidget(self, widgetClass, baseClass, module):
              assert widgetClass not in self._widgets
              self._widgets[widgetClass] = module
      
          def search(self, cls):
              module_name = self._widgets.get(cls)
              if module_name is None:
                  return None
      
              module = self._modules.get(module_name)
              if module is None:
                  if module_name.startswith('.'):
                      if self._package == '':
                          raise ImportError(
                                  "relative import of %s without base package specified" % module_name)
      
                      if self._package.startswith('.'):
                          raise ImportError(
                                  "base package %s is relative" % self._package)
      
                      mname = self._package + module_name
                  else:
                      mname = module_name
      
                  try:
                      module = __import__(mname, {}, {}, (cls,))
                  except ValueError:
                      # Raise a more helpful exception.
                      raise ImportError("unable to import module %s" % mname)
      
                  self._modules[module_name] = module
      
              return getattr(module, cls)
      

      第三种方式: 要在 sys.path 中添加小部件的路径(您必须是 import sys):

      sys.path.append( "./QtCustomWidgets/widgets" )
      uic.loadUi('my_ui.ui', self)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-01
        • 1970-01-01
        • 2020-12-12
        • 2014-06-24
        • 2015-07-27
        • 2021-12-29
        • 2014-05-06
        • 2015-02-02
        相关资源
        最近更新 更多