whats up with QLayout->setParent - qt

When I try to change the location of a layout with setParent the result is non-functional or odd.
the following works:
ui.txDiag_1->setParent(ui.tab_2);
movingHlayout = new QVBoxLayout(ui.tab_2);
movingHlayout->setSpacing(3);
movingHlayout->setMargin(3);
movingHlayout->setObjectName(QString::fromUtf8("movingHlayout"));
movingHlayout->addWidget(ui.txDiag_1);
but this doesnt(movingHlayout has been constructed before hand):
ui.txDiag_1->setParent(ui.tab_2);
movingHlayout->setParent(ui.tab_2);
movingHlayout->setSpacing(3);
movingHlayout->setMargin(3);
movingHlayout->setObjectName(QString::fromUtf8("movingHlayout"));
movingHlayout->addWidget(ui.txDiag_1);

You possibly have to call setLayout() on the widget you are setting the layout up in.
http://doc.qt.io/qt-5/qwidget.html#setLayout
As you can see from the docs, the ownership of the layout will automatically be set to the target widget.

To complete the other answer, here is the reason why setParent does not work as you expect:
setParent is not in QLayout implementation, but in QObject only. So using it will only change the pointer ownership and deletion, not the widget layout mechanism.

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

Qt - when has the UI finished initialization? So scroll bar positions etc can be set?

I have a simple use case in a Qt app:
When the UI has been constructed and a QGraphicsView (or any other QWidget) has been populated, scroll the graphics view to a given location, based on the graphics views width and height (or any other attribute set by the layout engine).
However I can't find a reliable or known place to do this. In the constructors the layout has not yet been applied, so the width isn't the final width that the user sees once the UI is "up". Most suggestions seem to consist of hacks such as:
QTimer::singleShot(0, this, SLOT(initWidget()));
In my case this doesn't seem to work unless I set the delay to around 10msec which seems even more risky and hacky. Surely there must be something like win32's WM_INITDIALOG or the likes?
You can override showEvent() or resizeEvent() of the relevant class.
After a bit of experimenting, since you say you're using showMaximized(), you actually may want to override both, and then get the size from first resizeEvent() after first showEvent() (or 2nd resizeEvent() overall). Output from my test app which used showMaximized():
Starting /home/hyde/test/build-eventTest-Desktop-Debug/eventTest...
resizeEvent size QSize(200, 100) oldSize QSize(-1, -1)
showEvent geometry QRect(0,0 200x100)
resizeEvent size QSize(1440, 851) oldSize QSize(640, 480)
/home/hyde/test/build-eventTest-Desktop-Debug/eventTest exited with code 0
Then do what you need to do there. If you want to do it just once, then add a boolean member variable like
bool mFullyInited;
and initialize it to false, and then in the overriden event handler method, test it and set it to true when you've done the initialization.

What is an appropriate place to initialize QWidgets?

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.

Can QWidget::find find widgets from a different process?

The documentation for QWidget::winId states (among other things) "If a widget is non-native (alien) and winId is invoked on it, that widget will be provided a native handle."
I'm not sure what 'alien' means in that context, but I'm choosing to ignore it for now. :)
So assuming that my widget now has a valid native handle associated with it, can I then pass that native handle to another process and into QWidget::find and get a valid QWidget object back within that second process?
I probably don't need to do too much else to the widget in the second process other than show/hide it and attach it to a parent widget. (It is guaranteed to not be attached to any parent widgets in the first process and never visible in the context of the first process).
If all the above works:
How much control will the second process have over that widget?
Will the first process receive user input events as if it were attached
to the first process's UI, and will the first process be able to update the widget as normal?
James
Let's take a look at Qt sources.
QWidget *QWidget::find(WId id)
{
return QWidgetPrivate::mapper ? QWidgetPrivate::mapper->value(id, 0) : 0;
}
find() can find a widget only if mapper contains it. The mapper is a static QHash<WId, QWidget *> variable. Items are inserted in this hash only in the QWidgetPrivate::setWinId method.
So, if a widget with a WId was created in another process, you can't find it using QWidget::find. This function doesn't use any native OS functions to find widgets.
Also see general description of alien widgets in Qt documentation:
Introduced in Qt 4.4, alien widgets are widgets unknown to the
windowing system. They do not have a native window handle associated
with them. This feature significantly speeds up widget painting,
resizing, and removes flicker.

Qt QGraphicsScene::drawItems subsitute?

For QGraphicsScene::drawItems the reference says:
Reimplement this function to provide custom painting of all items for the scene; gaining complete control over how each item is drawn.
But this function is marked as obsolete.
Is there any new equivalent method?
QGraphicsView::paintEvent() now calls
d->scene->d_func()->drawItems()
which means the method is part of class QGraphicsScenePrivate which you cannot override afaik.
If you need to change the way your items are drawn, first try to think of another way (i.e. a solution which does not require stepping into the drawItems() method). If you can't find any solution like that, your only chance is reactivating the pre-4.6-behaviour by setting
QGraphicsView::setOptimizationFlag( QGraphicsView::IndirectPainting )

Resources