Is there a way to do this in the widget cpp file instead of using QT Designer?
Generally speaking: no. Qt Designer generates a Xyz.ui
file, a simple XML description of the object tree and object properties. The uic
code generator takes that .ui
file and generates ui_Xyz.h
. The types of its members are set: you cannot programmatically change them, just as you can't programmatically change the type of any other member.
So, use the correct type of the object in the Designer. If you promote some base type (say a QProgressBar
) to your own derived type, the setupUi
will create an instance of your type. Thus, the whole problem disappears.
But you don't need to change the .ui
file using Designer. You can trivially change it manually to promote the widgets you need. Suppose we start with a simple widget that has a progress bar in it:
<?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>
To change the type of the progressBar
item, you have to make two changes to the XML file. First, change the type of the item itself:
<widget class="MyProgressBar" name="placeholder">
<property name="value">
<number>24</number>
</property>
</widget>
Then add your type to the <customwidgets>
item:
<customwidgets>
<customwidget>
<class>MyProgressBar</class>
<extends>QProgressBar</extends>
<header>myprogressbar.h</header>
</customwidget>
</customwidgets>
If you intend to, in effect, have an incorrect .ui
file, you can do the widget swap at runtime.
There are two major aspects of this:
Do you actually need a custom type?
In many cases, you can do everything without deriving from the base widget. Whether it makes sense for you is hard to tell: I don't understand why you can't use the proper type (MyProgressBar
) in your .ui
file.
// 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));
Doing the replacement.
You need access to the widget through a pointer/reference/value of a correct type. Ideally, store the new widget directly by value.
class Form : public QWidget, private Ui::Form {
MyProgressBar m_bar;
...
}
Then, replace the placeholder widget in its layout with an instance of the proper type.
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
}