Qt Dynamically create QWidget from QString - c++

I am trying to create a program that reads information from a database, and accordingly set up the layout. Specifically, I want to read two date fields and depending on the difference between the days, create a day(s) number of elements. Have anyone got an idea on how this could be done? I have tried to create an element using the QString->text() property with no success for obvious reasons and I have managed to write a function to create an element, but my problem is that I cannot control the name of the element, making it impossible for me with my rubbish knowledge about c++ to then interact with the given element.
Thank you for your time,
Cheers.

I think a QHash would be the perfect tool for your needs. It allows storing and lookup of pretty much anything through a unique key. That means you can store the widgets with their title as a key and then later retrieve a widget with a certain title from that hash.
Here is how to define such a hash:
// .h file
#include <QtCore/QHash>
#include <QtGui/QWidget>
class MyWidget : public QWidget
{
// ...
private:
QHash< QString, QWidget* > m_dynamicWidgetHash;
};
The Widgets (or any QWidget subclass) can then be stored in the hash like this, assuming the titles will always be unique:
// .cpp file
void MyWidget::someMethod()
{
QList< QString > widgetTitles = getWidgetTitlesFromSomewhere();
foreach( QString title, widgetTitles )
{
SomeWidgetSubclass* widget = new SomeWidgetSubclass( this );
widget->setTitle( title );
// Note: This will not work if two widgets can have the same title
Q_ASSERT( !m_dynamicWidgetHash.contains( title ) );
m_dynamicWidgetHash.insert( title, widget );
}
}
You can then later find your widgets knowing only the name like this:
// .cpp file
void MyWidget::someOtherMethod( const QString& title )
{
SomeWidgetSubclass* widget = m_dynamicWidgetHash.value( title );
if( !widget )
{
// TODO: Error Handling
return;
}
// Do whatever you want with the widget here
}

Also, it might be interested for you how to create object by class name using QMetaType. There is QMetaType::construct method. It requires that qRegisterMetaType function is should be called before. Detaild description is here.

Related

Qt and C++: Overwrite or Expand Class

I want to add some properties (like an ID) to a QPushButton. Therefore, I need to expand or overwrite the class Q_WIDGETS_EXPORT QPushButton : public QAbstractButton
How do I do that?
Thanks for the help.
you dont need to extend the class to just put an id in it ... instead make use of the property system.
as specified in the official doc here:
A property can be read and written using the generic functions QObject::property() and QObject::setProperty(), without knowing anything about the owning class except the property's name.
you just have to do:
ui->myButton->setProperty("Id", 123456);
can also be another object e.g a string (or even your own class if you define it to do that)
ui->myButton->setProperty("_name", "123456");
to read the property is the method property() there for you but read the doc because you get a QVariant as return example:
QVariant(int, 123456)
It really depends on the use case. There is no problem (and often the intended way) in inheriting from Qt-(Widget) Classes (correct me, if I am wrong).
So you could do:
class MyQPushButton : public QPushButton
{
Q_OBJECT
public:
MyQPushButton() : QPushButton(...) {}
private:
int ID = -1;
}
Qt has a very good documentation and you can look at the sources to see what to override.
You could also extend a new class with QPushButton, but than you always have to deal with the QPushButton reference in your class, if you want e.g. connect something. In the inherited class you can connect the slots and so on. But for example you could do this:
class MyQPushButton
{
public:
MyQPushButton() {}
const QPushButton& const GetQPushButton() { return pushButton; }
const QPushButton* const GetQPushButtonPtr() { return &pushButton; }
private:
QPushButton pushButton;
int ID = -1;
}
There is no right and wrong. But I would use the inheritance for Qt-classes.

how to create static QLabel in Qt

Is it possible to create some static QLabels in one class, and other classes can access its QLabels variable and apply changes to the QLabels without creating its object?
I found some answers online like if you want to access one class variables without creating its object in another class, you have to make its data static.
So basically what I am trying to do here is accessing and changing one class variables, for me it is QLabels, in another class without creating its object.
I know how to create static variables, but when comes to declare a staic QLabel, I found it difficult to achieve it.
I think you may just make the label accessible, i.e. expose it as a public member. Say you have a Form class, and a label QLabel in its ui. Add this method to the class:
public:
QLabel * label();
the implementation is just:
QLabel *Form::label()
{
return ui->label;
}
If all you need to expose is the label text property, just add these two accessors methods:
public:
QString labelText();
void setLabelText(QString & text);
in implementation file:
QString Form::labelText()
{
return ui->label->text();
}
void Form::setLabelText(QString &text)
{
ui->label->setText(text);
}
These last strategy fits encapsulation better.
About having it static: what if you have more than one instance of the Form class? Which label is supposed to be pointed to by the static member? If you are 100% sure you will have only one instance of the widget, you can add a static public QLabel * member:
public:
static QLabel * label;
in implementation file, on top:
QLabel *Form::label = 0;
in Form constructor:
ui->setupUi(this);
if(label == 0)
{
label = ui->label;
}
Again, this makes sense if you have one Form instance only. Otherwise, the static pointer will point forever to the label of the widget which was created first (and, dangerously, to nothing when that instance gets destroyed).

Translate String Representation of Enum Values Qt

Is it possible to translate the string representation of an enum (i.e. via QMetaEnum) using Qt's translation system?
What I think I need is for some way to get lupdate to pick up the enum strings for translation, either by emitting some QT_TR_NOOP() code if and only if the file is being processed by lupdate, or by modifying the behavior of lupdate/moc themselves.
For example, my application presents "settings" options to users via a dialog box. All of the settings are defined as Q_PROPERTY 's. Options which use an enum are presented as a combo box and the text options for the combo box use the QMetaEnum::key() as the displayed text. Below is some pseudo-code to get the point across.
Widgets will have some enum property defined like:
class SomeWidget : public QWidget
{
Q_OBJECT
Q_ENUMS( Configuration );
enum Configuration
{
Config_Blue = 0,
Config_Green,
Config_Invisible,
Config_Backwards
};
Q_PROPERTY( Configuration READ Configuration WRITE SetConfiguration );
};
The combo widget creation is done by a separate settings manager and goes something like this:
QWidget* SettingsItem::CreateWidget()
{
const QMetaObject* pMetaObj = this->m_pWidget->metaObject();
const QMetaProperty& rcProp = pMetaObj->property( this->m_iProp );
QMetaEnum cEnum = rcProp.enumerator();
if( cEnum.isValid() )
{
QComboBox* pRetWidget = new QComboBox;
for( int i = 0; i < cEnum.keyCount(); ++i )
{
int iVal = cEnum.value( i );
QString strKey = cEnum.key( i ); // Translate here?
pRetWidget->addItem( strKey, iVal );
}
}
...
return pRetWidget;
}
The combo box in the settings dialog shows "Config_Blue", "Config_Green", etc. I would like it to just say "Blue", "Green", etc. in whatever the current language is.
For the rest of the code we use Qt's translation system and the linguist utility which works really nice. I was hoping that I could enable this type of translation for the enum 's as well without having to add string literals and tr() 's into the code manually. Basically all of the information that a translator would need is already there in the code, I just need linguist to be able to identify these enum values as translatable. I can then invoke tr() when the combo box is populated.
What I think I need is for some way to get lupdate to pick up the enum strings for translation, either by emitting some QT_TR_NOOP() code if and only if the file is being processed by lupdate, or by modifying the behavior of lupdate/moc themselves.
I was thinking maybe a macro would work, might not be possible though. Perhaps modifying some Qt code is necessary?
An enum identifier shouldn't be user-visible to begin with. These are identifiers that should be meaningful to the developer, not to the user. Exposing them directly to the user adds a strong coupling between the internal design of the code and user-visible behavior. That is best avoided.
You need to map between enum values and user-visible strings. The values in the map should be subject to translation, and the map should be re-populated upon retranslation. E.g.:
class Object : public QObject
{
Q_OBJECT
Q_ENUMS(Configuration);
Q_PROPERTY(Configuration READ Configuration WRITE SetConfiguration);
enum Configuration {
Config_Blue, Config_Green, Config_Invisible, Config_Backwards
};
public:
static QString toString(Configuration c) {
switch (c) {
case Config_Blue: return tr("Blue", "Configuration");
case Config_Green: return tr("Green", "Configuration");
case Config_Invisible: return tr("Invisible", "Configuration");
case Config_Backwards: return tr("Backwards", "Configuration");
}
}
};
You can now translate the configuration values.
I'm using a dedicated function within the class in which I define my enum. Is it helpful? (Qt5.13).
I call it like MyClass::toTrString(myEnumVal);
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr);
enum MyEnum {
myKey1,
myKey2,
...
};
Q_ENUM(MyEnum)
static QString toTrString(MyEnum t) {
return tr(qPrintable(QVariant::fromValue(t).toString()));
}
}

Classes, Objects, Treewidget and QlistWidget

I'm trying to create a program for a project in school (University).
The program is basically supposed to have a QTreeWidget with a bunch of components, the QTreeWidget will update when you click on a button (for example Chassis-button will change the QTreeWidget into a bunch of different chassis).
From the QTreeWidget, you're then supposed to be able to mark one that you want and click on a "choose-button" which will transfer that row to a QListWidget. One example of a row could be :
"Fractal Design"
"R3"
"100euro"
"ATX"
I have a bunch of classes for each component. One of the classes is Chassis and it has a function named addChassis which looks like this :
void ChassisHandler::addChassis(string manufacturer, string model, int price, string size, string color, int fanSpots) {
Chassis **temp = new Chassis*[this->nrOfChassis + 1];
for (int i = 0; i < nrOfChassis; i++)
{
temp[i] = this->chassis[i];
}
delete[] this->chassis;
this->chassis = temp;
this->chassis[this->nrOfChassis] = new Chassis(manufacturer, model, price, size, color, fanSpots);
this->nrOfChassis++;
}
This function works fine if I want to create a class object and add a few chassis into the object and then print out the object, but I can not use it to add chassis into the treewidget. It needs to be QString instead of string and int and Qt seems to have a problem with me making a class object and then transfer the object to the treewidget. I simply do not have enough knowledge to be able to put all the chassis into the QTreeWidget. Right now I've created an additional function in my .cpp file that belongs to the .ui file which look like this :
void Computer::AddChassi(QString manufacturer, QString model, QString price, QString size, QString color, QString fanSpots){
QTreeWidgetItem *itm = new QTreeWidgetItem(ui->treeWidget);
itm->setText(0, manufacturer);
itm->setText(1, model);
itm->setText(2, price);
itm->setText(3, size);
itm->setText(4, color);
itm->setText(5, fanSpots);
}
But if I try to put this function in the Chassiclass, it says that "UI is not defined". It's very important that we use classes in this project.
So my two problems are :
How to create proper addfunctions to put strings into my TreeWidget?
How to transfer the wanted string from my TreeWidget to my ListWidget?
if I try to put this function in the Chassi class, it says that "UI is
not defined".
Your AddChassi() method looks mostly correct, except of course you are trying to dereference a variable named "ui" and (it appears that) there is no member variable named "ui" that is part of your Computer class, which is why you get that compiler error. Presumably the pointer "ui" is one that is available for use only in other contexts (e.g. because it is a member variable of another class), so making it available for use inside AddChassi() is just a matter of passing it in -- you could pass it every time as one of the arguments to AddChassi() if you want, or you could pass it in to the Computer class's constructor and hold it as a member variable of the Computer class for later use. Or, perhaps better yet, instead of passing in the ui pointer, just pass in the pointer to the QTreeWidget object, since that is the only thing you really need to pass in to the QTreeWidgetItem constructors anyway.
For example:
void Computer::AddChassi(QTreeWidget * tw, QString manufacturer, QString model, QString price, QString size, QString color, QString fanSpots){
QTreeWidgetItem *itm = new QTreeWidgetItem(tw);
[...]
my Second problem is to transwer the wanted string from my TreeWidget
to my ListWidget
The QTreeWidget class has various accessor functions (such as currentItem() and topLevelItem()) that you can use to obtain a pointer to one of the QTreeWidgetItems objects current attached to the QTreeWidget. Once you have that pointer, you can call the QTreeWidgetItem::text(int) method on it to extract the QString representing the text in the nth column of that item's row. Once you have that QString you can use it to create a new QListWidgetItem with that QString as its constructor argument.

Properties editor design pattern?

Warning: This is super in-depth. I understand if you don't even want to read this, this is mostly for me to sort out my thought process.
Okay, so here's what I'm trying to do. I've got these objects:
When you click on one (or select several) it should display their properties on the right (as shown). When you edit said properties it should update the internal variables immediately.
I'm trying to decide on the best way to do this. I figure the selected objects should be stored as a list of pointers. It's either that, or have an isSelected bool on each object, and then iterate over all of them, ignoring the non-selected ones, which is just inefficient. So we click on one, or select several, and then the selectedObjects list is populated. We then need to display the properties. To keep things simple for the time being, we'll assume that all objects are of the same type (share the same set of properties). Since there aren't any instance-specific properties, I figure we should probably store these properties as static variables inside the Object class. Properties basically just have a name (like "Allow Sleep"). There is one PropertyManager for each type of property (int,bool,double). PropertyManagers store all the values for properties of their respective type (this is all from the Qt API). Unfortunately, because PropertyManagers are required to create Properties I can't really decouple the two. I suppose this means that I have to place the PropertyManagers with the Properties (as static variables). This means we have one set of properties, and one set of property managers to manage all the variables in all the objects. Each property manager can only have one callback. That means this callback has to update all the properties of its respective type, for all objects (a nested loop). This yields something like this (in pseudo-code):
function valueChanged(property, value) {
if(property == xPosProp) {
foreach(selectedObj as obj) {
obj->setXPos(value);
}
} else if(property == ...
Which already bothers me a little bit, because we're using if statements where we shouldn't need them. The way around this would be to create a different property manager for every single property, so that we can have unique callbacks. This also means we need two objects for each property, but it might be a price worth paying for cleaner code (I really don't know what the performance costs are right now, but as I know you'll also say -- optimize when it becomes a problem). So then we end up with a ton of callbacks:
function xPosChanged(property, value) {
foreach(selectedObj as obj) {
obj->setXPos(value);
}
}
Which eliminates the entire if/else garbage but adds a dozen more event-listeners. Let's assume I go with this method. So now we had a wad of static Properties, along with their corresponding static PropertyManagers. Presumably I'd store the list of selected objects as Object::selectedObjects too since they're used in all the event callbacks, which logically belong in the object class. So then we have a wad of static event callbacks too. That's all fine and dandy.
So now when you edit a property, we can update the interal variables for all the selected objects via the event callback. But what happens when the internal variable is updated via some other means, how do we update the property? This happens to be a physics simulator, so all the objects will have many of their variables continuously updated. I can't add callbacks for these because the physics is handled by another 3rd party library. I guess this means I just have to assume all the variables have been changed after each time step. So after each time step, I have to update all the properties for all the selected objects. Fine, I can do that.
Last issue (I hope), is what values should we display when multiple objects are selected an there is an inconsistency? I guess my options are to leave it blank/0 or display a random object's properties. I don't think one option is much better than the other, but hopefully Qt provides a method to highlight such properties so that I can at least notify the user. So how do I figure out which properties to "highlight"? I guess I iterate over all the selected objects, and all their properties, compare them, and as soon as there is a mismatch I can highlight it. So to clarify, upon selected some objects:
add all objects to a selectedObjects list
populate the properties editor
find which properties have identical values and update the editor appropriately
I think I should store the properties in a list too so that I can just push the whole list onto the properties editor rather than adding each property individually. Should allow for more flexibility down the road I think.
I think that about covers it... I'm still not certain how I feel about having so many static variables, and a semi-singleton class (the static variables would be initialized once when the first object is created I guess). But I don't see a better solution.
Please post your thoughts if you actually read this. I guess that's not really a question, so let me rephrase for the haters, What adjustments can I make to my suggested design-pattern to yield cleaner, more understandable, or more efficient code? (or something along those lines).
Looks like I need to clarify. By "property" I mean like "Allow Sleeping", or "Velocity" -- all objects have these properties -- their VALUES however, are unique to each instance. Properties hold the string that needs to be displayed, the valid range for the values, and all the widget info. PropertyManagers are the objects that actually hold the value. They control the callbacks, and the value that's displayed. There is also another copy of the value, that's actually used "internally" by the other 3rd party physics library.
Trying to actually implement this madness now. I have an EditorView (the black area drawing area in the image) which catches the mouseClick event. The mouseClick events then tells the physics simulator to query all the bodies at the cursor. Each physics body stores a reference (a void pointer!) back to my object class. The pointers get casted back to objects get pushed onto a list of selected objects. The EditorView then sends out a signal. The EditorWindow then catches this signal and passes it over to the PropertiesWindow along with the selected objects. Now the PropertiesWindow needs to query the objects for a list of properties to display... and that's as far as I've gotten so far. Mind boggling!
The Solution
/*
* File: PropertyBrowser.cpp
* Author: mark
*
* Created on August 23, 2009, 10:29 PM
*/
#include <QtCore/QMetaProperty>
#include "PropertyBrowser.h"
PropertyBrowser::PropertyBrowser(QWidget* parent)
: QtTreePropertyBrowser(parent), m_variantManager(new QtVariantPropertyManager(this)) {
setHeaderVisible(false);
setPropertiesWithoutValueMarked(true);
setIndentation(10);
setResizeMode(ResizeToContents);
setFactoryForManager(m_variantManager, new QtVariantEditorFactory);
setAlternatingRowColors(false);
}
void PropertyBrowser::valueChanged(QtProperty *property, const QVariant &value) {
if(m_propertyMap.find(property) != m_propertyMap.end()) {
foreach(QObject *obj, m_selectedObjects) {
obj->setProperty(m_propertyMap[property], value);
}
}
}
QString PropertyBrowser::humanize(QString str) const {
return str.at(0).toUpper() + str.mid(1).replace(QRegExp("([a-z])([A-Z])"), "\\1 \\2");
}
void PropertyBrowser::setSelectedObjects(QList<QObject*> objs) {
foreach(QObject *obj, m_selectedObjects) {
obj->disconnect(this);
}
clear();
m_variantManager->clear();
m_selectedObjects = objs;
m_propertyMap.clear();
if(objs.isEmpty()) {
return;
}
for(int i = 0; i < objs.first()->metaObject()->propertyCount(); ++i) {
QMetaProperty metaProperty(objs.first()->metaObject()->property(i));
QtProperty * const property
= m_variantManager->addProperty(metaProperty.type(), humanize(metaProperty.name()));
property->setEnabled(metaProperty.isWritable());
m_propertyMap[property] = metaProperty.name();
addProperty(property);
}
foreach(QObject *obj, m_selectedObjects) {
connect(obj, SIGNAL(propertyChanged()), SLOT(objectUpdated()));
}
objectUpdated();
}
void PropertyBrowser::objectUpdated() {
if(m_selectedObjects.isEmpty()) {
return;
}
disconnect(m_variantManager, SIGNAL(valueChanged(QtProperty*, QVariant)),
this, SLOT(valueChanged(QtProperty*, QVariant)));
QMapIterator<QtProperty*, QByteArray> i(m_propertyMap);
bool diff;
while(i.hasNext()) {
i.next();
diff = false;
for(int j = 1; j < m_selectedObjects.size(); ++j) {
if(m_selectedObjects.at(j)->property(i.value()) != m_selectedObjects.at(j - 1)->property(i.value())) {
diff = true;
break;
}
}
if(diff) setBackgroundColor(topLevelItem(i.key()), QColor(0xFF,0xFE,0xA9));
else setBackgroundColor(topLevelItem(i.key()), Qt::white);
m_variantManager->setValue(i.key(), m_selectedObjects.first()->property(i.value()));
}
connect(m_variantManager, SIGNAL(valueChanged(QtProperty*, QVariant)),
this, SLOT(valueChanged(QtProperty*, QVariant)));
}
With a big thanks to TimW
Did you have a look at Qt's (dynamic) property system?
bool QObject::setProperty ( const char * name, const QVariant & value );
QVariant QObject::property ( const char * name ) const
QList<QByteArray> QObject::dynamicPropertyNames () const;
//Changing the value of a dynamic property causes a
//QDynamicPropertyChangeEvent to be sent to the object.
function valueChanged(property, value) {
foreach(selectedObj as obj) {
obj->setProperty(property, value);
}
}
Example
This is an incomplete example to give you my idea about the property system.
I guess SelectableItem * selectedItem must be replaced with a list of items in your case.
class SelectableItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName );
Q_PROPERTY(int velocity READ velocity WRITE setVelocity);
public:
QString name() const { return m_name; }
int velocity() const {return m_velocity; }
public slots:
void setName(const QString& name)
{
if(name!=m_name)
{
m_name = name;
emit update();
}
}
void setVelocity(int value)
{
if(value!=m_velocity)
{
m_velocity = value;
emit update();
}
}
signals:
void update();
private:
QString m_name;
int m_velocity;
};
class MyPropertyWatcher : public QObject
{
Q_OBJECT
public:
MyPropertyWatcher(QObject *parent)
: QObject(parent),
m_variantManager(new QtVariantPropertyManager(this)),
m_propertyMap(),
m_selectedItem(),
!m_updatingValues(false)
{
connect(m_variantManager, SIGNAL(valueChanged(QtProperty*, QVariant)), SLOT(valueChanged(QtProperty*,QVariant)));
m_propertyMap[m_variantManager->addProperty(QVariant::String, tr("Name"))] = "name";
m_propertyMap[m_variantManager->addProperty(QVariant::Int, tr("Velocity"))] = "velocity";
// Add mim, max ... to the property
// you could also add all the existing properties of a SelectableItem
// SelectableItem item;
// for(int i=0 ; i!=item.metaObject()->propertyCount(); ++i)
// {
// QMetaProperty metaProperty(item.metaObject()->property(i));
// QtProperty *const property
// = m_variantManager->addProperty(metaProperty.type(), metaProperty.name());
// m_propertyMap[property] = metaProperty.name()
// }
}
void setSelectedItem(SelectableItem * selectedItem)
{
if(m_selectedItem)
{
m_selectedItem->disconnect( this );
}
if(selectedItem)
{
connect(selectedItem, SIGNAL(update()), SLOT(itemUpdated()));
itemUpdated();
}
m_selectedItem = selectedItem;
}
private slots:
void valueChanged(QtProperty *property, const QVariant &value)
{
if(m_updatingValues)
{
return;
}
if(m_selectedItem && m_map)
{
QMap<QtProperty*, QByteArray>::const_iterator i = m_propertyMap.find(property);
if(i!=m_propertyMap.end())
m_selectedItem->setProperty(m_propertyMap[property], value);
}
}
void itemUpdated()
{
m_updatingValues = true;
QMapIterator<QtProperty*, QByteArray> i(m_propertyMap);
while(i.hasNext())
{
m_variantManager->next();
m_variantManager->setValue(
i.key(),
m_selectedItem->property(i.value()));
}
m_updatingValues = false;
}
private:
QtVariantPropertyManager *const m_variantManager;
QMap<QtProperty*, QByteArray> m_propertyMap;
QPointer<SelectableItem> m_selectedItem;
bool m_updatingValues;
};
Calm down, your code has not O(n^2) complextity. You have a nested loop, but only one counts to N (the number of objects), the other counts to a fixed number of properties, which is not related to N. So you have O(N).
For the static variables, you write "there aren't any instance-specific properties", later you write about updates of the individual properties of your objects, which are exactly instance-specific properties. Maybe you are confusing the "class Properties" (which is of course shared among all properties) with the individual properties? So I think you don't need static members at all.
Do you want to display changes to the objects only if they appear, or do you want a continuos display? If your hardware is able to handle the latter, I would recommend going that way. In that case, you have to iterate over all objects anyway and update them along the way.
Edit: The difference is that in the former (update on change) the drawing is initiated by the operation of changing the values, for example a object movement. For the latter, a continuos display, you would add a QTimer, which fires say 60 times a second and calls a SLOT(render()) which does the actual rendering of all objects. Depending on the rate of changes this may actually be faster. And it is probably easier to implement.
Another possibilty is let Qt handle the whole drawing, using a Graphics View, which handles the objects-to-draw internally in a very efficient tree structure. Take a look at
http://doc.trolltech.com/4.5/graphicsview.html
If you want to display only the changes, you could use individual callbacks for each properties value. Each time the value of a property is changed (in this case making the properties vlaues private and using setSomeThing(value)), you call the update function with an emit(update()). If you are absolutly concernd about emit being slow, you could use "real" callbacks via function pointers, but I don't recommend that, Qt's connect/signal/slot is so much easier to use. And the overhead is in most cases really neglible.