如何在不导入自定义窗口小部件类包的情况下使用自定义窗口小部件和uic.loadUi?

时间:2016-08-05 07:48:29

标签: python pyqt pyqt4 pyqt5

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

但是,我希望在与我调用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

2 个答案:

答案 0 :(得分:1)

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

  

自定义小部件相对导入基础包的可选包[强调添加]

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

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

答案 1 :(得分:1)

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

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

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

第二种方法是将uic.loadUi与packadge路径一起使用:     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中的小部件(您必须导入sys):

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