以编程方式推广 QWidget
Programmatically promote QWidget
我有一个 ui 文件,在 QWidget
中有一个 QProgressBar
。此外,我还创建了继承自 QProgressBar
的自定义进度条组件。在 QT Designer 中,我可以将 QProgressBar
小部件提升为我的自定义小部件。有没有办法在小部件 cpp 文件中执行此操作而不是使用 QT Designer?
换句话说,有没有办法以编程方式将 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
构造函数中)?
Qt Designer 中的"promote" 操作改变了widget 的实际类型,并在.cpp 文件中使用提升的widget class。编译器永远不会将原始的、未提升的 class 名称视为小部件的类型。在 C++ 中,您必须执行相同的操作:将小部件的类型更改为所谓的 "promoted" 类型。
如果您有一个任意 QWidget*
,您可以使用 qobject_cast<>()
将其转换为您的 "promoted" 类型。这只有在你知道指向的 QWidget
是你的 "promoted" class 的实例时才有效,否则转换将 return NULL
.
Is there a way to do this in the widget cpp file instead of using QT Designer?
一般来说:没有。 Qt Designer 生成一个 Xyz.ui
文件,一个简单的 XML 对象树和对象属性的描述。 uic
代码生成器获取 .ui
文件并生成 ui_Xyz.h
。其成员的类型已设置:您无法以编程方式更改它们,就像您无法以编程方式更改任何其他成员的类型一样。
因此,请在设计器中使用正确的对象类型。如果您将某些基类型(例如 QProgressBar
)提升为您自己的派生类型,则 setupUi
将创建您的类型的一个实例。这样,整个问题就消失了。
但是您不需要使用 Designer 更改 .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));
正在进行替换。
您需要通过正确类型的 pointer/reference/value 访问小部件。理想情况下,直接按值存储新小部件。
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
}
我有一个 ui 文件,在 QWidget
中有一个 QProgressBar
。此外,我还创建了继承自 QProgressBar
的自定义进度条组件。在 QT Designer 中,我可以将 QProgressBar
小部件提升为我的自定义小部件。有没有办法在小部件 cpp 文件中执行此操作而不是使用 QT Designer?
换句话说,有没有办法以编程方式将 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
构造函数中)?
Qt Designer 中的"promote" 操作改变了widget 的实际类型,并在.cpp 文件中使用提升的widget class。编译器永远不会将原始的、未提升的 class 名称视为小部件的类型。在 C++ 中,您必须执行相同的操作:将小部件的类型更改为所谓的 "promoted" 类型。
如果您有一个任意 QWidget*
,您可以使用 qobject_cast<>()
将其转换为您的 "promoted" 类型。这只有在你知道指向的 QWidget
是你的 "promoted" class 的实例时才有效,否则转换将 return NULL
.
Is there a way to do this in the widget cpp file instead of using QT Designer?
一般来说:没有。 Qt Designer 生成一个 Xyz.ui
文件,一个简单的 XML 对象树和对象属性的描述。 uic
代码生成器获取 .ui
文件并生成 ui_Xyz.h
。其成员的类型已设置:您无法以编程方式更改它们,就像您无法以编程方式更改任何其他成员的类型一样。
因此,请在设计器中使用正确的对象类型。如果您将某些基类型(例如 QProgressBar
)提升为您自己的派生类型,则 setupUi
将创建您的类型的一个实例。这样,整个问题就消失了。
但是您不需要使用 Designer 更改 .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));
正在进行替换。
您需要通过正确类型的 pointer/reference/value 访问小部件。理想情况下,直接按值存储新小部件。
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 }