以编程方式推广QWidget

Programmatically promote QWidget

本文关键字:QWidget 方式推 编程      更新时间:2023-10-16

我在QWidget中有一个QProgressBar的ui文件。此外,我已经创建了从QProgressBar继承的自定义进度条组件。在QT设计器中,我可以将QProgressBar小部件提升到我的自定义小部件。有没有办法做到这一点,在小部件的cpp文件,而不是使用QT设计器?换句话说,是否有一种方法可以通过编程方式将QWidget提升为另一个相同类型的自定义小部件(一种变形)?

下面是一个例子:

class MyProgressBar : public QProgressBar
{
    Q_OBJECT
public:
    explicit CmdProgressWidget(QWidget *parent = 0);
    ~CmdProgressWidget();
    int myCustomFunction();
};
class MyWidgetWithProgress : public QWidget, public Ui::MyWidget
{
    Q_OBJECT
    public:
        MyWidgetWithProgress (QWidget *parent = 0) {setupUi(this);}
        ~MyWidgetWithProgress() {;}
    inline int callMyCustomFunction() {progressBar->myCustomFunction();}
};

获得int callMyCustomFunction()编译代码的常见方法是将QT Designer中的小部件(QProgressBar)中的进度条提升到我的自定义小部件MyProgressBar

回到原来的问题:是否有一种方法可以以编程方式做到这一点(例如在setupUi(this);之后的MyWidgetWithProgress构造函数)?

有办法做到这一点在小部件cpp文件,而不是使用QT设计器?

一般来说:没有。Qt Designer生成一个Xyz.ui文件,一个对象树和对象属性的简单XML描述。uic代码生成器获取.ui文件并生成ui_Xyz.h。其成员的类型是已设置的:不能通过编程方式更改它们,就像不能通过编程方式更改任何其他成员的类型一样。

所以,在设计器中使用正确的对象类型。如果您将某些基类型(例如QProgressBar)提升为您自己的派生类型,那么setupUi将创建您的类型的实例。这样,整个问题就消失了。

但是您不需要使用设计器更改.ui文件。您可以手动简单地更改它以提升所需的小部件。假设我们从一个简单的小部件开始,其中有一个进度条:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>256</width>
    <height>40</height>
   </rect>
  </property>
  <layout class="QGridLayout" name="gridLayout">
   <item row="0" column="0">
    <widget class="QProgressBar" name="placeholder">
     <property name="value">
      <number>24</number>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections/>
</ui>

要更改progressBar项的类型,必须对XML文件进行两处更改。首先,更改项目本身的类型:

<widget class="MyProgressBar" name="placeholder">
 <property name="value">
  <number>24</number>
 </property>
</widget>

然后将您的类型添加到<customwidgets>项:

<customwidgets>
 <customwidget>
  <class>MyProgressBar</class>
  <extends>QProgressBar</extends>
  <header>myprogressbar.h</header>
 </customwidget>
</customwidgets>

如果您实际上想要一个不正确的.ui文件,您可以在运行时进行小部件交换。

有两个主要方面:

  1. 你真的需要自定义类型吗?

    在许多情况下,您可以在不从基本小部件派生的情况下完成所有操作。这对你来说是否有意义很难判断:我不明白为什么你不能在你的.ui文件中使用正确的类型(MyProgressBar)。

    // Would-Be Derived Class
    class MyProgressBar : public QProgressBar {
      int m_var;
    protected:
      void paintEvent(QPaintEvent * ev) {
        QProgressBar::paintEvent(event(ev)); // let the base class paint itself
        QPainter p(this);
        // do some overpainting, etc.
      }
    public:
      void doSomething() {
        m_var = 3;
      }
    };
    // Do the same using the base class instead:
    void doSomething(QProgressBar * bar) {
      bar.setProperty("m_var", 3);
    }
    void paintEvent(QWidget * w, QPaintEvent * ev) {
      w->event(ev); // let the base class paint itself
      QPainter p(w);
      // do some overpainting, etc.
    }
    struct Painter : public QObject {
      bool eventFilter(QObject * obj, QEvent * ev) {
        if (obj->isWidgetType() && ev->type() == QEvent::Paint)
          paintEvent(static_cast<QWidget*>(obj), static_cast<QPaintEvent*>(ev));
        return QObject::eventFilter(obj, ev);
      }
    }
    QProgressBar bar;
    bar.installEventFilter(new Painter(&bar));
    
  2. 正在更换。

    您需要通过正确类型的指针/引用/值来访问小部件。理想情况下,直接按值存储新小部件。

    class Form : public QWidget, private Ui::Form {
      MyProgressBar m_bar;
      ...
    }
    

    然后,用适当类型的实例替换布局中的占位符小部件。

    void replace(QWidget * & old, QWidget * replacement) {
      auto layout = old->parent()->layout();
      // name the new widget the same
      replacement->setObjectName(old->objectName());
      // swap the widgets and delete the layout item
      delete layout->replaceWidget(old, replacement);
      // delete the old widget
      delete old;
      // don't leave a dangling pointer
      old = nullptr;
    }
    Form:: Form(QWidget * parent) :
      QWidget(parent)
    {
      setupUi(this);
      replace(placeholder, &m_bar);
      // you have to manually connect slots for the m_bar widget
    }
    

Qt Designer中的"promote"操作改变了小部件的实际类型,并在.cpp文件中使用了被提升的小部件类。编译器永远不会将原始的、未提升的类名视为小部件的类型。在c++中,您必须做同样的事情:将小部件的类型更改为所谓的"提升"类型。

你可以,如果你有一个任意的QWidget*,通过使用qobject_cast<>()将它转换成你的"提升"类型。这只有在您知道指向的QWidget是您的"提升"类的实例化时才会起作用,否则强制转换将返回NULL