我在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
文件,您可以在运行时进行小部件交换。
有两个主要方面:
你真的需要自定义类型吗?
在许多情况下,您可以在不从基本小部件派生的情况下完成所有操作。这对你来说是否有意义很难判断:我不明白为什么你不能在你的
.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));
正在更换。
您需要通过正确类型的指针/引用/值来访问小部件。理想情况下,直接按值存储新小部件。
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
。