Qt: when can I access dynamic properties from qtcreator? - c++

I have a couple of widgets in QtCreator that are promoted from the same class. However, I'd like them to have subtle differences between the two, so I'd like to pass some differences in the UI file that the promoted class can use to distinguish itself. Dynamic properties seem like the way to go so in the UI editor I've assigned a dynamic property to each promoted widget. In the code I tried accessing the property, but noticed it seems to only be available post construction (probably because Qt is calling setProperty() after the object is created.
MyWidget::MyWidget(QWidget* parent) : QGLWidget(parent)
{
this->property("someProperty").toString(); // returns blank
}
void MyWidget::initializeGL()
{
this->property("someProperty").toString(); // returns string set in UI file
}
So my question how do people use these properties for constructor-type stuff? I could just do that in initializeGL, but that seems odd since these properties might not be related to initializing OpenGL. I imagine I could also connect to the property changed signal and do it there. Is that the common way to handle this?

If the generated code for setupUi() from your .ui file does something like this:
MyWidget *w = new MyWidget;
w->setProperty(...);
then your constructor is accessing a meta property that does not yet exist.
You can reimplement QObject::event() to capture QDynamicPropertyChangeEvents, letting you act once the property is initialized.
bool MyWidget::event(QEvent *ev)
{
if (ev->type() == QEvent::DynamicPropertyChange) {
if (QDynamicPropertyChangeEvent *propEv = static_cast<QDynamicPropertyChangeEvent *>(ev)) {
if (propEv->propertyName() == "someProperty")
...
}
}
}
Bear in mind that this code will be called every time there is a dynamic property change.
A better approach may be to create a function to perform the necessary initilization on the widget after setupUi() etc. are called and the dynamic property is created.
void setupMyWidget(MyWidget *w)
{
QString s = w->property("someProperty").toString();
...
}
Typically, dynamic properties are assigned a default value in the constructor so that they are always available and non-null later.
setProperty("someProperty", defaultValue);

Related

QML: C++ classes with "bring your own component"

I'm trying to develop a Qt C++ application, with a QML frontend, but I hit a roadblock.
This is what I have so far:
A Factory class that outputs a choice of objects. These objects, that I'm going to call "controllers", control different pieces of hardware.
The Factory would be exposed to the QML layer with setContextProperty.
The controller would be chosen basically with a combo box controlling the factory.
Now, for the tricky bit. I want that the "controllers" behave in a "bring your own component" way. This means that they would have a method returning the respective QML file for their controller. That shouldn't be to hard to do, it's basically biding a Loader to a method of the Factory/Manager saying the file with the component to load into a placeholder.
But the problem is: how can this newly created component and this newly created controller know and talk to each other? This is something I did before with QWidgets, just having pointers between the classes. Quite trivial.
I tried an architecture like this before for QWidgets, but seems to not be ideal for QML.
I made this drawing of what I would ultimately like to happen:
This architecture allows for a very trivial plugin system (at least in the QWidgets world) and I would very much like to keep that. Not a massive singleton and account for every possible action...
I'd appreciate ideas!
I think this is actually very easy, if you return a QQuickItem from the C++ side. If you do so you can create it with a specific context, in which you can set your "specific hardware controller" as a property
QQmlComponent *qml_controller = new QQmlComponent(qengine, "some_file.qml");
QQmlContext *context = new QQmlContext(); //should probably give a pointer to owning object
context->setContextProperty("controller", pointer_to_hw_cont);
return qml_controller->create(context);
The Loader setSource method have additional parameter you could pass to provide initial value for some property. Something like this:
ComboBox {
model: controlerFactory.specificHWListModel
onCurrentTextChanged: {
var specificHWControler = controlerFactory.getObjectFor( currentText );
loader1.setSource(
specificHWControler.qml_file,
{ "controler": specificHWControler }
);
}
}
Loader {
id: loader1
}
The specificHWListModel cold be QStringList or some custom QAbstractListModel.
And getObjectForcould be just a invokable function.
Q_INVOKABLE QObject* getObjectFor(QString hwName);
The object returned from Q_INVOKABLE function will be managed by QQmlEngine by default if you don't set by the QQmlEngine::setObjectOwnership. Remember to register your SpecificHWControler class to QQmlEngine.
The qml_file SpecificView.ui.qml, should have property controler, and could be edited with Designer:
import SpecificHWControlerModule 1.0
Item {
property SpecificHWControler controler
}
https://doc.qt.io/qtcreator/quick-connections-backend.html

Q_PROPERTY with parameterized read and write accessors

I have a Configuration that holds some basic information about file locations like download, install, picture, music, document etc. location.
These are currently exposed to QML using Q_PROPERTY. They all have their own accessors:
Q_PROPERTY(QUrl download_location READ download_location WRITE set_download_location NOTIFY download_location_changed)
These accessors basically do all the same stuff and I'd like to get rid of all this redundant code I have to write.
My first idea is to have a little nested class FileLocation that provides get, set and validation functions. But then, how would I connect these to the Q_PROPERTY?
Also, if I had something like a static functions which took parameters, (e.g. check_validity( QUrl location )), how would I hand over this parameter from QML side?
I think I'm on the wrong path here, so my question is how to keep redundant code in the context of Q_PROPERTY within reasonable limits, avoiding to write loads and loads of get, set and changed functions for very similar objects?
Here's some more code:
class Configuration : public QObject
{
QObject
Q_PROPERTY(QUrl download_location READ download_location WRITE set_download_location NOTIFY download_location_changed)
Q_PROPERTY(QUrl music_location READ music_location WRITE set_music_location NOTIFY music_location_changed)
...
signals:
void download_location_changed();
void music_location_changed();
...
public slots:
void set_download_location(QUrl location)
{
download_location = location;
emit download_location_changed(download_location);
}
void set_music_location(QUrl location)
{
music_location = location;
emit music_location_changed(music_location);
}
...
private:
QUrl download_location,
music_location,
...;
}
So as you see there's a lot of recurring code that does the same, and I'd like to damp that down a little. How do I do that? I was thinking about some general functions set, get, changed etc. that get the member to work on was a parameter. But then I didn't know how to hand over which member to work on from qml.
I just found out about the possibility to expose C++ classes to qml via qmlRegisterType(...) - maybe this is the way to go here?
You could go for an evil macro:
#define IMPL(data, name) \
inline decltype(data) name() const { return data; } \
inline void set_##name(decltype(data) value) { if (value != data) { data = value; emit name##Changed();} }
Of course, if you don't need any extra stuff, you can simply use a MEMBER property and have Qt auto generate accessors for you.
This will however not work if you need to do custom things in the accessors, the macro will, just add your stuff to it.
Lastly, when you declare a Q_PROPERTY, you can right-click the property, go to refactoring, and select "generate missing members...", Qt will generate the default stubs for the accessors, and you will only have to add your custom stuff in. The downside to this is it has the nasty habit of putting the generated code in the silliest place possible, so if you want your class to look neat, you have to move it by hand.
qmlRegisterType() is when you want to register a type so it can be created in QML, you generally don't have to do anything to access a QObject derived object from QML - it works for QObject derivatives, you only need to register a metatype for types which do not have meta information generated.
Last but not least - I don't see any point in having your configuration as a C++ object, you can just as well do it in QML, and use Qt.labs.settings to make your settings persistent. C++ is optimal for performance critical parts only. If you do your configuration in QML, you don't need to worry about any of that boilerplate code, as it is all automatic in QML, no need to write accessors, no need to recompile your project on every tiny change.

Qt: changing user variable indicates the change of control's state

Is it possible to simply indicate the change of some Qt control's property by changing value of user defined variable (perform an action on changing value). Eg. I declared int a which is frequently used by multiple functions. Now I need to construct a relation: if ( a == 0 ) then my control is inactive else my control is active. (I was inspired by C# data bindings.)
In Qt, controls are enabled/disabled with QWidget::setEnabled. What you'll need to do is create a subclass from which all your gui windows are derived, in which you define a function that sets this variable and calls setEnabled. Like this:
class Widget : public QWidget
{
...
MySetEnabled(bool b)
{
a = b;
setEnabled(b);
}
int a;
};
Signals/Slots
In your accessor methods emit a signal that connect to actions you like to happen.

Using the GroupBoxes in Qt

I'm a student programmer and I am using Qt to build a GUI application. I'm trying to ensure that some check boxes are checked in order to proceed. These check boxes enable or disable the group box itself and are part of the QGroupBox class. Accepted combinations could be either or both. The problem I am running into is getting the boolean value(at least that's what I think it is) from the QGroupBox member function QGroupBox::setChecked(bool) and use it to determine whether or not to display an error message. I have tried several methods and reference Qts documentation hoping for a good example. Because the QGroupBox I'm trying to use is a member of my ui class I tried creating a new instance of QGroupBox and setting it to the values of the ui. Then; using an if statement, find out whether or not the boxes are check or not. Here's my code for this:
QGroupBox activeParticleInjection = ui->groupBoxParticleInjection;
QGroupBox activeFluidInjection = ui->groupBoxFluidInjection;
if (activeParticleInjection::setChecked(false) && activeFluidInjection(false));
{
QMessageBox noInjectionSelectedError;
noInjectionSelectedError.setText("Error: No injection type selected");
noInjectionSelectedError.exec();
}
else
{
transData.particleInjectionActive = activeParticleInjection::setChecked();
transData.fluidInjectionActive = activeFluidInjection::setChecked();
This doesn't work; starting with the way I'm trying to pass the Ui properties to the new instance of QGroupBox. I know that is question is relatively generic question but I tried passing the ui checkbox directly and that caused even more issues. I looked through the documentation and that led me to the way im trying to do it know; with no luck. I was hoping for some feedback on a better method of handling QGroupBox. Being a student sometimes its hard to see the answer especially when dealing with such unique members as the ones put together in QT.
Prior to changes I was using this method to build this process; and I received errors for the way my if parameters were setup. Compile error was : no matching function for call to 'QGroupBox::isChecked(bool)'
if (ui->groupBoxFluidInjection->isChecked(false) && ui->groupBoxParticleInjection->isChecked(false));
{
QMessageBox noInjectionSelectedError;
noInjectionSelectedError.setText("Error: No injection type selected");
noInjectionSelectedError.exec();
}
else
{
transData.particleInjectionActive = ui->groupBoxParticleInjection->isChecked();
transData.fluidInjectionActive = ui->groupBoxFluidInjection->isChecked();
}
I have been using these sites for most help:
QGroupBox
QCheckBox
You're mixing several things:
The QGroupBox instances in ui are pointers, so you must assign them to a QGroupBox* (pointer), not a QGroupBox (object on the stack).
:: is used with methods only if the method you call is static (class method), which is not the case here.
setChecked() is a setter setting the checked state of the group box. It doesn't return anything (void), so you cannot use them as conditions. What you want there is the getter bool QGroupBox::isChecked().
Your code snippet cleansed:
QGroupBox* activeParticleInjection = ui->groupBoxParticleInjection;
QGroupBox* activeFluidInjection = ui->groupBoxFluidInjection;
if (!activeParticleInjection->isChecked() && !activeFluidInjection->isChecked())
{
QMessageBox::critical(this, tr("Error"), tr("No injection type selected"));
}
else
{
transData.particleInjectionActive = activeParticleInjection->isChecked();
transData.fluidInjectionActive = activeFluidInjection->isChecked();
}
Not really sure what you tried to do in your code, but that's actually a lot easier to achieve. Also don't try to create copies (which you do in your example). Work with references or pointers in that case!
QGroupBox group("my group box"); // of course this might be a class member, too
group.setCheckable(true);
// do other things...
if(group.isChecked())
{
// do whatever if it's checked
}
else
{
// do stuff if it isn't checked
}
To check for at least one of multiple groups (or check boxes) being checked:
if(group1.isChecked() || group2.isChecked())

Qt4: The best way to access parent class (1 level up, 2 levels up .... )

I'm wondering how to access parent class in my Qt application.
Lets say my project has following structure:
mainWindow: MainWindow
tabWidget: QTabWidget
tab1: MySubClass1
tab2: MySubClass2
tabWidget: QTabWidget
xtab1: MySubSubClass1
xtab2: MySubSubClass2
It is a little simplified.
What I want to do is to access mainWindows object from one of xtab2 slot functions.
(1) What would be the best method ?
I tried to pass the pointer to mainWindow along the tree but I get runtime errors.
(2) Should I include mainwindow.h in xtab.h file or should I do it in xtab.cpp file ?
Thanks for help :)
If you really need the mainwindow, passing the MainWindow pointer is the best way to do it. A static method has the drawback that it will stop working with more than one mainwindow.
I would suggest to avoid accessing the mainwindow from the contained widgets though and use signals instead. E.g.:
class MainWindow {
public:
explicit MainWindow( QWidget* parent=0 ) {
tab = new TabWidget;
...
MySubSubClass1* ssw1 = new MySubSubClass;
connect( ssw1, SIGNAL(textChanged(QString)), this, SLOT(page1TextChanged(QString));
tab->addWidget( ssw1 );
}
private Q_SLOTS:
void page1TextChanged( const QString& ) {
//do something...
}
};
MySubSubClass1 then emits textChanged(), addresseeChanged() (e.g. in Addressbook), or whatever level of abstraction or detail makes sense on the higher level. That way MySubSubClass is generic and doesn't have to know about MainWindow at all. It can be reused in any other context. If MySubSubClass itself contains other widgets, it can again connect to their signals.
You could create a static method and object inside MainWindow that would return mainwindow object.
Something like this:
private:
static MainWindow* _windowInstance
public:
static MainWindow* windowInstance()
{
return _windowInstance;
}
This seems to be the simples solution in most cases. Now you just have to include mainwindow.h whenever you need to access this object.
And don't forget to initialize _windowInstance in the contructor, like this;
_windowInstance = this;
By parent class, I assume you mean parent widget?
If you want to find the top level widget, QWidget::window() will point you to it. Use dynamic_cast or qobject_cast to turn it into your MainWindow object.
If you want to go up some arbitrary level, use paerntWidget().
There are a variety of different solutions to this problem, the one you chose as the answer is in terms of object orientation and encapsulation one of the worse ones. Some thoughts
Encapsulation: if you find yourself having to provide access accross a large distance in relation (down a long chain of containers or subclasses) you might want to look at the functionality that you are trying to distribute. I might be that it should be encapsulated in a class by itself that can passed around easier than where it is currently located (the main window in your case).
Abstraction: Unless it is actually functionality of QMainWindow that you need to access don't pass a pointer to your MainWindow class, create an interface for the functionality that you need, have your MainWindow implement that interface and pass around and object of the interface type instead of your MainWindow type.
Signals and Slots: As Frank noted, implement the appropriate functionality using Qt's signalling mechanism, this makes the connection between the caller and callee into a dynamic one, again separating it from the actual MainWindow class
QApplication: If you absolutely have to have global information restrict the entry point, you already have one singleton the QApplication object, make it the maintainer of all the other objects that need to be globally accessible, derive your own QApplication class and maintain global references in there. Your QApplication class can then create or destroy the needed global objects.
With more information about what you need to do with the MainWindow instance or what needs to be communicated you will also get better answers
QWidget* parent = this ;
while (parent -> parentWidget()) parent = parent -> parentWidget() ;