Using a factory to create QT promoted widgets - c++

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);

Related

Creating custom buttons in QT

I have my class, which should represent buttons (pads, if I need to be specific). Right now I am wondering if it's possible to draw them in my QMainWindow class.
In first version of app I created buttons in QtCreators designer. But right now I Would like to create them by code. As said before, already got class Pad, where I want to have position, text and other behaviors. Right now my class is pretty poor:
#include <QObject>
class Pad
{
public:
Pad(int x, int y);
private:
int m_xPosition;
int m_yPosition;
};
And here is how I create them (where m_pads is std::vector )
void PadsWindow::createPads(const int &numberOfPads)
{
enablePadsWindow();
for (int index = 0; index < numberOfPads; ++index)
{
m_Pads.push_back(new Pad(10, 100));
}
}
Exactly here I would like to draw buttons in my QMainWindow and have it like normal QPushButton.
Could you help guys ?
To use a custom class as UI widget it must inherit from QWidget, which provides the necessary interface and already a lot of implementations (which you can override to customize the behaviour).
To make a widget behave like a button it is recommandable to use QAbstractButton or one of its derivations as base class, because these already provide much of the behaviour, and customizing is easier than adding all functionality to a plain QWidget.
This class can be added as custom widget to Qt Designer, so you can use it like any original Qt widget in dialogs and layouts.
You can find a lot of tutorials and FAQs regarding custom widgets on the net and here, so getting started should be easy enough for anyone who's got a grasp of the basics of Qt and C++.

Accessing widgets inside QStackedWidget

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.

How to provide user options with expanding layouts QVBoxLayout in QT

I currently have 3 Vertical Layouts in my form. I want to provide the user the option of resizing them to their liking by stretching them. Can this be achieved with Vertical layouts ?
You can use QSplitter as said before. Even being not possible to add it by Designer, there is a way to solve. Create those frames in your widget (which have no layout), and in your cpp and h file you will do that:
in header, write
class YourClass : public QSplitter
instead
class YourClass : public QWidget
and replace in your cpp these declaration on constructor
QWidget(parent)
for
QSplitter(parent)
I guess it does (or almost, perhaps some other changes will be necessary, as add include files).
EDIT: at this time usin code in a QMainWindow class:
QSpliter *splitter = new QSplitter(this) //or declare 'splitter' in .h file.
splitter->setOrientation(Qt::Vertical);
splitter->addWidget(widget1);
splitter->addWidget(widget2);
splitter->addWidget(widget3);
setCentralWidget(splitter);
As of 4.8.6, it's possible to add a QSplitter in Qt Designer by using the Lay out [horizontally/vertically] in splitter layout option. It's somewhat surprising that it's not listed in the widget box, but you can use it.

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.