Accessing widgets inside QStackedWidget - c++

I am developing a Qt app with QtDesigner.
Previously it was quite easy to access specific widgets to do something with them like connecting signals. After I added QStackedWidget I can no longer access specific widgets with something like ui->stack->page1->widget.
Is there a way to do it somehow? Or should I always call findChild method? Or maybe it is possible to at least assign some of the nested widgets in stack widget to properties of the main windwo class?

QStackedWidget provides a method to get child widgets by index, as well as the current widget.
A quick example is as follows:
MOCed Header
class MyWidget: QWidget
{
Q_OBJECT
public:
using QWidget::QWidget
QWidget *ptr;
};
Source File
QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(new MyWidget); // index 0
stackedWidget->addWidget(new QWidget); // index 1
stackedWidget->addWidget(new MyWidget); // index 2
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(stackedWidget);
setLayout(layout);
// do something specific with the first widget's ptr element
auto* widget = stackedWidget->widget(0);
auto* mywidget = qobject_cast<MyWidget*>(widget);
if (mywidget) {
mywidget->ptr->setObjectName("FirstPage");
}
Now, Qt uses virtual interfaces by default, so if you have a custom subwidget you need to extract, you can use qobject_cast. qobject_cast is basically a fast dynamic_cast, and works even without RTTI. In template-driven code, dynamic_cast is a bit of a code-smell: it means you lost useful type information too early. With virtual interfaces, the exact opposite is true: you should use qobject_cast as needed.

Why you get the widget layer by layer, if your widgets are added in Qt designer, you can get it by ui->widget directly.

Related

Replace a widget in Qt

I have a base class which has some gui items that i have set positions of using the designer in Qt creator. Those items are:
QWidget* w1;
QWidget* w2;
QWidget* w3;
Now in a class that inherits that base class, I would like to "transform" those widgets into lineEdit items, that would keep all the geometrical parameters of that widgets. So I do something like this:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1);
leAmplitude->setGeometry(ui->w1->geometry());
ui->glControls->addWidget(leAmplitude);
But the added QLineEdit item doesn't appear in the exact same place as w1 item. Its just added at the bottom of other controls in the QGridLayout glControls. How to make the lineEdit to take all geometric parameters from w1?
Layout takes care of the widgets placed in the layout, according to the hints given by the widget, so calling setGeometry, then doing addLayout is not useful. Also, adding widget to layout resets it parent, so you setting new widget's parent to ui->w1 is not useful either.
Fortunately, there is QLayout::replaceWidget method! Just use that. Example:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit;
QLayoutItem *previous = ui->glControls->replaceWidget(ui->w1, leAmplitude);
// possibly assert that previous is ui->w1, or just delete it, or whatever
This method was added as late as in Qt 5.2 it seems, so if you need to support older versions, I can expand this answer to cover how to (try to) do the same manually. But in short, you have to use the right QGridLayout::addWidget overload and make sure relevant properties (including at least sizeHint and sizePolicy) match.
try this, it is works:
QLineEdit* leAmplitude;
leAmplitude = new QLineEdit(ui->w1->parentWidget());
ui->w1->parentWidget()->layout()->replaceWidget(ui->w1, leAmplitude);
ui->w1 = leAmplitude;

Using a factory to create QT promoted widgets

Promoting widgets is one of the best features in the QT Designer. But when using the designer, it 'new's the object there it self ( inside setupUi ). This limits the ability to extend the interface file.
Is it possible to use a factory of some sort to get the promoted widgets, so that when a sub class of a promoted widget is required, this could be achieved via changing the factory and returning the sub class of the widget.
requirement:
NamePanel* myPanel;
void setupUi(QWidget* pWidget)
{
myPanel = Factory::CreateNamePanel();
}
Is there a work around to achieve this?
Unfortunately it seems not to be possible to add a factory into the Ui files. A workaround would be to have a QWidget with an attached layout in the ui and add the factory generated widget to this layout.
The code would look like this:
ui->setupUi(this);
QWidget *panel = Factory::CreateNamePanel();
ui->namePanelContainer->layout()->addWidget(panel);

How to extend QDockWidget functions in Qt?

I'm new to Qt, tried several widgets and found that QDockWidget is the most modern/interactive one to work with
But I've found a little limitation "about where to dock the widget only in the 4 sides, left/right/top/bottom"
I want to do one of the two following things and any one should work.
Add more areas to dock widget "for example it can recognize separators between widgets and get its data from there , then resize itself depending on that"
Reimplement the whole functions of QDockWidget into QWidget and do
it like option 1
Thanks in advance
In Qt you can pretty much inherit any class into a new class of your own and extend it yourself. for example:
// New class that inherits QDockWidget and extends its functionality
ExtendedQDockWidget : public QDockWidget
{
public:
ExtendedQDockWidget(QWidget * parent = 0) :
QDockWidget(parent)
{
// ... do any extra initialisations here
}
ExtendedFunc(/* some params */)
{
// code here
}
}
You can also re-implement or overload existing functions to do exactly what you want.

Why do we pass "this" pointer to setupUi function?

I'm fairly new in QT. Taking below fairly simply explain from qt docs :
class CalculatorForm : public QWidget
{
Q_OBJECT
public:
CalculatorForm(QWidget *parent = 0);
private slots:
void on_inputSpinBox1_valueChanged(int value); //why is that slots are private?
private:
Ui::CalculatorForm ui;
};
and implementation of constructor
CalculatorForm::CalculatorForm(QWidget *parent)
: QWidget(parent) {
ui.setupUi(this); // <-- Question below
}
Q: I was wondering why do we pass this pointer to setupUi function?, what does it do ?
So that the dialog will have the caller as parent, so that eg when the parent is closed the dialog can be closed automatically. Generally all gui elements have a pointer to their parent.
private slots:
void on_inputSpinBox1_valueChanged(int value); //why is that slots are private?
These are auto generated slots which exactly match the naming of the gui elments in QtDesigner. They are only meant to do the direct hookup to those gui elements and so should be dealt with in this class. If these signals were extended to other classes then any change in the gui would require changing a lot of other code which doesn't need to know details of the gui.
In the handler slot for the specific gui element you can then emit another more general signal to the rest of the app.
The only widget that setupUi doesn't create is the widget at the top of the hierarchy in the ui file, and as the Ui::CalculatorForm class instance doesn't know the widget it has to fill, it (this) has to be passed explicitly to the class at some point.
this or any other widget you would pass to it, is used as the parent to all other subwidgets. For example, you could fill a widget without inheritance like this:
QWidget *widget = new QWidget;
Ui::CalculatorForm *ui = new Ui::CalculatorForm;
ui->setupUi(widget);
widget->show();
But really, it would be easier to understand if you read the content of the uic generated file (probably named ui_calculatorform.h).
setupUi creates the instances of widgets (QLabel, QTextEdit and so on). The [user interface compiler] (http://qt-project.org/doc/qt-4.8/uic.html) gets information for you from the .UI form and generates widget-creation code in the generated moc source files.
The manual way of creating widgets without using the Qt Designer or a UI file would be like so:
QWidget* pWidget = new QWidget(this);
I think it is to add the caller widget to the layout of this UI.
This widget will be the toplevel widget.
Martin Beckett answer might be correct as well, as what he described is a common behavior in Qt (cf the 'parent' argument in most of widget's derived class constructor)
Note that you have alternative ways how designer can auto-generate code.
In this case you have a separate 'UI' class for this code which is not QObject so it also is not a QWidget.
Auto generated code needs information about parent widget and to make auto-conections of slots and signals so this is why you have to pass this.
This pater is less intrusive then other pasterns (that is why it is default). You can also try alternative patters (check Qt Creator Designer options), but I recommend you to see what is generated by designer tools in default settings.

How to specialize a widget according to a file type?

I'm looking for a way to specialize a widget at runtime. I have a form created with Qt Designer. In this form there is a widget that displays user data, like name, age and so on. Then the user chooses a file and according to the type the widget shall display additional information (like to OO example person -> student, teacher).
I tried to put an empty QWidget on my form, assigned a person widget to it and if the user clicks a button I call deleteLater() on person and assign a new Student(). This works but the new widget then doesn't follow the layout.
I also found QStackedWidget. This seems to work but because all possible widgets are created when the form is shown, even if they are never used, this way doesn't feel right.
What would be the way to accomplish this?
Edit: I feel that my question is a bit unclear. What Qt mechanism should be utilized if one wants to replace a QWidget with a specialized (inherited) version of the original widget? I'm looking for the Qt way to do this.
You need to add a widget dynamically to the widget you have drawn in the designer.
// in UI file
QWidget *wdgFromForm;
// in cpp file
QHBoxLayout *const layout(new QHBoxLayout(wdgFromForm));
SpecializedWidget * specializedWidget( new SpecializedWidget(wdgFromForm));
layout->addWidget(specializedWidget);
Maybe the problem is not that the widget is not suitable, but simply that you're not giving time for the widget to update.
You could take a look at processEvents:
http://web.mit.edu/qt-dynamic/www/qapplication.html#details
This looks like a use case for the factory pattern.
#include <map>
#include <string>
struct Whatever;
struct QWidget;
typedef QWidget*(*WidgetCtor)(Whatever*);
typedef std::map<std::string, WidgetCtor> FileFactory;
QWidget* createFoo(Whatever*);
QWidget* createBar(Whatever*);
QWidget* createDefault(Whatever*);
void factory_init(FileFactory& ff)
{
ff["foo"] = createFoo;
ff["bar"] = createBar;
}
QWidget* create_by_factory(const FileFactory& ff, const std::string t, Whatever* w)
{
FileFactory::const_iterator it(ff.find(t));
if(it != ff.end())
{
return it->second(w);
}
else
{
return createDefault(w);
}
}
Adding widgets dynamically to a layout is not a problem.
You might need to call updateGeometry on the containing widget though.