What is an appropriate place to initialize QWidgets? - qt

What is the best way (place) to initialize QWidgets, when you add them programmatically?
I can think of ways to do it:
1) Create instances in the definition file and then initialize them in the initializer list. This seems to be the right way, but it becomes kind of sloppy the more QWidgets you need to add to the Window.
class SomeWindow : QDialog{
...
private:
QLabel label1;
QLabel label2;
...
}
SomeWindow::SomeWindow(...) : QDialog(...),
label('label1 text'), label('label2 text')
{
...
layout.addWidget(&label1);
layout.addWidget(&label2);
...
}
2) Coming from C# I tend to like this, but it seem to generate memory leak...
SomeWindow::SomeWindow(...) : QDialog(...)
{
QLabel* label1 = new QLabel('label1 text');
QLabel* label2 = new QLabel('label2 text');
...
layout.addWidget(label1);
layout.addWidget(label2);
...
}
Is there a better way to do this that I'm missing?
I appologize for the newbie question.

Qt use his own system to delete parent-children QObject derived classes. When you delete object, all children are deleted too.
With the first code, you have 2 destructions (in destructor of SomeWindow and with QObject system), then this code is illegal (only with Qt ; with C++ standard code, it's good)
With the second code, labels are delete by QObject system, you don't have memory leak. No need to keep pointer on objects.
#Jairo
Setting parent in constructor is not the only way to add children to objects. In particular, here, QLayout::addWidget reparent objets (if layout is correctly child of object)
#msgmaxim
Be carefull, layout must not be local variable in constructor

An advantage to having pointers to widgets in the headers, rather than the actual objects, is that you don't need to include all the headers for the widgets, but just forward declare them. This increases compilation time, which can be considerably noticeable in large projects.
In addition, if you have a dialog and are just adding a lot of widgets, such as a QLabel, you can make the code neater by doing this in the implementation: -
layout.addWidget(new QLabel('label1 text'));
layout.addWidget(new QLabel('label2 text'));
layout.addWidget(new QLabel('label3 text'));
layout.addWidget(new QLabel('label4 text'));
As has been mentioned, Qt's parent system will take care of cleaning up the widgets when their parent is deleted.
Of-course, if you want to change a property, such as the text, you'd then need to find the widget from the layout's children, but often QLabel, as its name implies, just labels an item and its properties aren't changed.

Two ways are good in order to initialize a new widget.
First case you have labels as objects. So when SomeWindow is destroyed, they will be destroyed automatically too. Remember, if you have pointer to widgets instead objects, you will need (and can) to delete the labels into destructor of dialog.
SomeWindow::~SomeWindow()
{
delete label1;
label2.deleteLater(); // A safer way to delete a widget. Thread-safe.
}
And second case there will be a memory leak because you have no way to delete the widget into destructor. But if you define a parent for labels, they will be delete when parent is delete too. Take a look to QWidget documentation.
If parent is 0, the new widget becomes a window. If parent is another widget, this widget becomes a child window inside parent. The new widget is deleted when its parent is deleted.
Moreover, anytime a object constructor ask you for a parent QWidget or QObject, you can think that Qt will delete the object when the parent is deleted.
I am a newbey as well, but I hope this help you.

Related

How can I detect whether a tooltip is visible at a given moment?

I'm looking for a way to detect whether a Qt widget's ToolTip is visible at the moment when a particular key combination is pressed. If it is, I want to copy the ToolTip's text to the clipboard.
Specifically, I have a QListView containing abbreviated strings, which is set up (via the Qt::ToolTipRole of the associated model) to show the full string of the appropriate list item when the mouse is hovered over it. The behaviour I'm looking for is that if the user presses CTRL-C (as detected by a QShortcut) while the tooltip is visible, then the tooltip text is copied to the clipboard.
My original idea was to use the children() method of the QListView widget to see if there was a tooltip preset among them:
// Inisde the slot connected to QShortcut::activated...
auto children = _ui -> myListView -> children();
QString selectionText;
for (const auto & child : children)
{
if (qobject_cast<QToolTip *>(child))
{
selectionText = qobject_cast<QToolTip *>(child) -> text();
break;
}
}
...but this failed because it turns out that QToolTip does not inherit from QObject.
I've also thought of screening for QEvent::QToolTip events in the ListView's main event handler, and while I could probably get this to work it seems excessively low-level; I'd need to use screen co-ordinates to determine which item in the list was being hovered over and look for the widget's timeout to check that the tooltip hadn't disappeared again by the time that the QShortcut was fired. I'd be disappointed if there weren't a simpler way.
Is there an obvious way forward that I've failed to see?
There are probably several possible solutions, but I am afraid none of them is simple. What I would do is to use the implementation detail that the tooltip actual widget is called QTipLabel. See https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qtooltip.cpp.html#QTipLabel and it inherits from QLabel so you can easily get the text from it.
I am afraid the following solution is just a savage hack. I have not tested it, but it should work.
I would override the data model for your view, specifically override method data() which would call the data() method of the original model class but cache the last value which was returned when this method is called with role == Qt::ToolTipRole.
Then you need to catch the shortcut you are interested in. After it is caught, you get all qApp->topLevelWidgets() https://doc.qt.io/qt-5/qapplication.html#topLevelWidgets` and go through them and check if any of them has class name equal to QTipLabel (use QMetaObject::className()) and is visible, i.e. isVisible() == true.
If you get this visible QTipLabel widget (you hold it via QWidget*), qobject_cast it to QLabel* (you cannot cast it to QTipLabel beause you do not have access to the definition of QTipLabel class because it is in private Qt source file) and get the text with QLabel::text(). If the text is the same as the text which you stored in step 1, then yes, this is the text you are looking for and you can copy it to clipboard or do whatever yo want with it.
Nasty, isn't it? But it is the simplest what I can think of.
PS: I believe that step 1 can be implemented also by catching QEvent::QToolTip for your view and then do some magic to get the text, but I think that overriding data() for model can be a bit easier.
PPS: One obvious drawback is that Qt can rename QTipLabel class in the future. But I would not be worry about it. That won't happen becaus ethey do not change QtWidgets module any more. And if it happens, then you just rename the class in your code. No problem.
PPPS: Another potential corner-case is that some other widget (whose tooltip you do NOT want to capture with that shortcut) actually has the same tooltip text as any of the items in your list view (which you DO want to capture). Then if you display tooltip for your list item, then you move your mouse over to that other widget and hover so that its tooltip gets shown (but you do NOT want to capture it) and then you press that shortcut... But I guess that in reality this will not be your case. I doubt there will be this unlikely clash of tooltips.
With thanks to #V.K., this is what worked:
auto candidates = qApp->topLevelWidgets();
QString selectionText;
for (const auto & candidate : candidates)
{
if (strcmp(candidate->metaObject()->className(), "QTipLabel") == 0)
{
QLabel * label = qobject_cast<QLabel *>(candidate);
if (label->isVisible())
{
selectionText = label -> text();
break;
}
}
}
if (!selectionText.isEmpty())
QGuiApplication::clipboard() -> setText(selectionText);

How create a separate copy of a QQuickItem & render it on a different window

I have a QQuickItem fetched from C++ side like this.
QQuickItem * my_item = qmlEngine->rootObjects()[0]->findChild<QQuickItem*>("ItemObjectName");
my_item is valid & has all the properties set on to it.
Scenario
I have 2 windows which need this QQuickItem to be drawn on alterantively. I want to render this my_item to a different window. This works perfectly fine as long as I set the Parent of the my_item to the other window
// the_other_window is a QQuickWindow
QQuickWindow * the_other_window;
// here I set parent
my_item->setParentItem(the_other_window->contentItem());
This requires me to do setParentItem again back to my_item's original window otherwise it goes invisible on the original window. This is working but gives me unnecessary dependency. Instead I am trying to create a copy of the QQuickItem & do a setParentItem on that. By copying like this:
QQuickItem * item_copy = new QQuickItem(my_item);
Problem:
But this doesn't seem to create a copy of the QQuickItem & hence I don't see a copy of my_item on the_other_window.
Question:
All I want to know is, how can I create a valid copy a QQuickItem into another pointer say QQuickItem * item_copy & render it on a different window without affecting the visibility/state of the original QQuickItem?
The interface of QQuickItem doesn't provide clonability. If it did, then all its subclasses would have to reimplement a virtual clone() function.
Indeed, the QQuickItem derives from QObject which explicitly disables copy-like operations (copy constructor and assignment operator), so they're disabled in any QQuickItem-derived class as well. Even if you have a specific subclass of QQuickItem, which you think you know how to copy, you can't implement "real" copying for it.
The closest thing to do in the latter case is to instantiate a new, blank item of your type and manually copy all values of relevant properties from the old to the new instance. You can encapsulate code that in a copy function.

Can I pass "this" to a Q_Slot?

I have an application with many windows (QWidgets).
I didn't save a list of open windows, though, so everytime I want to close a window, I have to retrieve it.
Particularly, each of these windows is called here SubWindow.
Every SubWindow class contains a layout with a MultiEditor *sEditors, which has a menu with an action that closes the current window.
Every SubWindow is created within the MainWindow.
I have two plans.
1) destroying the SubWindow from within itself, by adding in the SubWindow constructor
connect(sEditors, SIGNAL(closeWindow()),
this, closeWindow()));
or
2) destroying the SubWindow from within the MainWindow class, by adding in the SubWindow constructor
connect(sEditors, SIGNAL(closeWindow()),
main, SLOT(closeWindow(this)));
About 1), I don't understand how I can close and destroy a QWidget from within itself (delete this; didn't seem to work, but I can try again).
About 2) my SLOT(closeWindow(this)) doesn't seem to be triggered, so I am wondering if I can pass "this" as an argument.
Ad 1) You can use QObject::deleteLater(). This will destroy the object in the next event loop cycle, and is specifically create for situations like this
Ad 2) You cannot pass actual arguments as parameters in signal-slot connections.
You can however find out who has emitted the signal by using the sender() function in the slot. In your case, that would be the sEditors object.
Other options:
3) You can use a QSignalMapper to map signals from your editors to the Subwindows.
4) (Using Qt5 / C++11) You can use a lambda connection in your Subwindows:
connect(sEditors, SIGNAL(closeWindow()), [this] () {this->closeWindow();});
Can I pass this to a Qt slot?
A slot is a non-static method, so it already has access to this. The this you refer to is the third argument to QObject::connect. In Qt 4 syntax, you're free to omit the third argument - it defaults to this. In Qt 5 syntax, you must be explicit about it, though.
I don't understand how I can close and destroy a QWidget from within itself
To delete any QObject from within itself, use QObject::deleteLater(). Recall that a QWidget is-a QObject in terms of LSP.
my SLOT(closeWindow(this)) doesn't seem to be triggered
There's no such slot (give us a link to its documentation: you can't), and your slot signature is also invalid because the only things within the parentheses in the slot signature can be types, and this is not a type: SLOT(slotName(TYPE_LIST_HERE)), e.g. SLOT(mySlot(int,QString)).
To close a widget, use its close() slot:
connect(sEditors, SIGNAL(closeWindow()), this, SLOT(close());
Yet, by using Qt 4 connect syntax, you're leaving coding mistakes to be detected at runtime - and then if you don't pay attention to the debug output at runtime, you'll miss it. It's thus much better to use the new (Qt 5) connect syntax, and let the compiler detect errors for you:
connect(sEditors, &MultiEditor::closeWindow, this, &QWidget::close);
Alas, there's no need to tightly couple the object that sends closeWindow to SubWindow - at least not within the SubWindow::SubWindow(). Instead, you can connect at the place where you create the editor.
To delete a widget when it gets closed, simply set the Qt::WA_DeleteOnClose attribute on it and let Qt do it for you. No need to explicitly call deleteLater etc.
You might factor all of it into a factory method:
template <class T> T* SubWindow::makeEditor() {
auto sub = new T{this};
sub->setAttribute(Qt::WA_DeleteOnClose);
connect(sEditor, &MultiEditor::closeWindow, sub, &QWidget::close);
return sub;
}
MainWindow::MainWindow(/*...*/) : /*...*/ {
makeEditor<EditorType1>();
makeEditor<EditorType2>();
/*...*/
}

Get pointer to registered field in the wizard

Can I get access to the widget of registred field in the QWizardPage?
QLineEdit* fld = new QLineEdit;
// register
registerField( "test", fld );
....
// Somewhere in wizard
QLineEdit* field = ?
You can assign a unique name to the widget, then find it in the wizard's children. Recall that widgets are QObjects. This technique applies to all QObjects and is not specific to the wizard, or even to widgets.
char const fieldName[] = "MyCoolField";
fld->setObjectName(fieldName);
...
QLineEdit * field = wizardPage->findChild<QLineEdit*>(fieldName);
if (field) {
...
}
It looks like QWizardPage isn't really designed to do this — which is why the field function just returns the widget's contents, not a pointer to it.
Looking at the source code for QWizardPage::registerField, getting to the fields is going to be nasty: they're stored in the QWizardPrivate class, which is used to isolate the private implementation of the QWizard class from its public interface (see Qt's D-Pointer write-up for more information about this approach).
I don't recommend trying to figure out how to actually expose the internals of QWizardPrivate to your own code. That's because the whole reason QWizardPrivate exists is to allow Qt to completely change the private data and implementation in a minor release, without affecting compiled code — it's a moving target.
The simplest solution is to store your own pointer to the QLineEdit externally, if at all possible. For example, if you're subclassing QWizardPage, try creating a "shadow" field map of your own.

Why aren't QObjects deleted twice, if they are class-members and childs at the same time?

If a QObject is a member of a class (not created with the new-operator) and at the same time a child of the class-instance, it should be deleted twice, because all childs of the class-instance are deleted and in the desctructor of the class all class-members are deleted. Hence, the program should crash. But this doesn't happen, so Qt somehow must detect this double-relationship. But does this really happen? And if yes, how does it work?
Here is an example:
class MyWindow : public QMainWindow {
Q_OBJECT
public:
MyWindow();
QLabel label;
};
MyWindow::MyWindow() : label(this) {}
When an instance of MyWindow is destroyed, label should be deleted twice because it is a member AND a child of MyWindow. But this doesn't happen and it seems safe to do this. But how does it work?
This works in this case, because of the order of deletion:
~MyWindow() is called first, which destroys the label. The label's ~QObject() destructor removes the object from the parent's list of children.
After ~MyWindow(), at some point the MyWindow's ~QObject() destructor is called, which delete's the window's children. However, as the label pointer was already removed from the list of children, there is no attempt to delete the label again.
According to the Qt documentation: You can also delete child objects yourself, and they will remove themselves from their parents. I take that to mean that the child is responsible for the detachment as a part of its destructor. From this page.
Also, it may be useful to note that the order of destruction in C++ specifies that member destructors are called prior to base class destructors, see here. So label will remove itself prior to the MyWindow needing to remove it.

Resources