Render a QQuickItem on a second window without changing its parent hierarchy - qt

I have to render a QQuickItem owned by a particular window, into another. Basically MyQQuickItem owned by window1 to be rendered on window2. This switch has to happen in my app repeatedly due to a certain functionality.
I do the following to achieve the goal & the code basically works fine.
Code:
MyQQuickItem * myQuickItem = qmlEngine->rootObjects()[0]->findChild<QQuickItem*>("myquickitemobject");
myQuickItem->setParentItem(window1->contentItem());
// do the required on window2
// then set window1 as parent back again
myQuickItem->setParentItem(window2->contentItem());
Problem:
Above technique functionally works fine. But this requires me to flip flop a few times juggling between setting parent item from window1 to window2 & back again.
Question:
Is there some other way to share MyQQuickItem between the 2 windows? Or is it possible display MyQQuickItem on the both the windows alternatively without having to change the parent hierarchy?

You might use grabToImage() and display the grabbed image on your second window.
This might not be ideal, performance wise. You can find some questions on how to do this on this site. Especially interesting might be this.
I don't know your case, but it might be better, to have two instances of the same component displaying the same data model - possible with input for one disabled.

Related

Using QT 4.7 - dialog is busy loading a BIG thing; how to disable all controls?

[EDIT] - wasn't getting to the disable code the way I thought I was; works fine with any of the solutions below.
I have a modeless QT dialog, in which all kinds of user settings can be manipulated. In addition, it can save and load large data sets, which can take several seconds. Lots going on in the main window underneath (realtime app) and that's fine, and interaction there is okay, but in the dialog itself, I need to block user interaction.
Essentially, during the load, I don't want the user to be able to change / affect any of the controls in the modeless dialog (and there are a lot of them.)
Rather than disable each one individually (or even that way if that's the only way), is there a straightforward means I can use to disable input to the dialog entirely until the load is complete?
There's a progress bar in it that shows what's going on, too, which is constantly updated by the load process, so that needs to keep on working.
I tried this...
On dialog open:
QDialog *window = this;
Then around load:
window->setDisabled(true);
....
window->setDisabled(false);
...which compiles fine, and runs without complaint, but the window did not disable.
I also tried:
window->setEnabled(false);
....
window->setEnabled(true);
...that doesn't seem to do anything either.
I tried this too:
QList<QWidget*> list = window->findChildren<QWidget *>();
foreach(QWidget *qw, list)
{
qw->setEnabled(false);
}
....
QList<QWidget*> list = window->findChildren<QWidget *>();
foreach(QWidget *qw, list)
{
qw->setEnabled(true);
}
..also does nothing.
Based on Qt documentation (http://doc.qt.io/archives/qt-4.8/qwidget.html#enabled-prop):
Disabling a widget implicitly disables all its children. Enabling
respectively enables all child widgets unless they have been
explicitly disabled.
So your snippets are essentially doing same thing in different ways so that doesn't seem to be a problem and modality of dialog shouldn't have any effect on this as well. How are you loading / saving the data? If you are doing it in e.g. click slot then you are basically blocking the UI event loop which means that UI doesn't have cycles to react to your changes. If you want to isolate UI from background action so it will properly react even that you are doing heavy lifting in the background and you should consider using QRunnable or QTread to offload the work away from UI thread.

How to replace all cursor appearances within a QML program?

I'm working on a game that uses QML for its UI.
I would like to replace all cursors appearances with cursor-images that are more fitting to the game style (e.g. pointing skeleton hand instead of the normal Qt::ArrowCursor).
Calling QGuiApplication::setOverrideCursor() seams not to be a practical solution as I can not "overwrite" each MouseArea to call may replaceCursor() magic-global-function. For example the change column with cursor within a TableView is currently impossible for me to manipulate.
To me the most practical solution would be to replace the appearance of all cursors but leaf Qt with the tasks to correctly choose the cursor style.
Thanks for any help!
You can still use QGuiApplication::setOverrideCursor() to decorate your mouse areas. It works like a stack, you can set and then restore cursors, so you begin with setting an initial cursor from main.cpp, and then you use an "overloaded" MouseArea which sets its cursor using setOverrideCursor() as well, instead of using the QML functionality.
For example:
onContainsMouseChanged: {
if (containsMouse) Sys.setOverrideCursor(yourCursortype)
else Sys.restoreOverrideCursor()
}
Of course, that means you will have to create an auxiliary object that will call those functions from C++, and expose it to QML so it can be called from there.

Qt Parent child relationship for independent qmainwindows

Using Pyside, but a general Qt question:
I am building a Qt app with a controlling QMainWindow. From this window the user can open other QMainWindows (or QDialogs) and from some of those she can open more. The user is intended to think of the first QMainWindow as "the app" and the others as lots of different views on more or less the same data.
So I'd like all the windows to be independently stackable so the user can set up the screen to their own requirements. In particular I want the user to be able to bring the first QMainWindow on top if wanted. But I don't really want each window to have its own task bar entry (though I can live with that). Also I would like them to minimise and restore together, and I would like them all to close when the first main window closes.
If I parent them all on the first mainwindow it works nicely except they stay on top of it which is not what I want.
Instead I have it kind of working by making them all independent with parent = None. Then I register them all with the main window and close them all when it closes. But this makes them a bit too independent - they minimise separately and have their own task bar entry.
Am I missing some obvious fix to this? Is there any easy way (a flag?) to stop the children staying on top of the parent?
Or is there some UI guideline that I am breaking by desiring this?
Or is there a cleaner design somehow? I thought of adding a dummy parent that they could all descend from but maybe that's messy. Would that parent need a visual presence? I wouldn't want that.
Suggestions?
You can have as many QMainWindows as you want, or parentless QWidgets. I think the best way to handle your situation is to create your own pseudo parent-child relationship like this:
In your QMainWindow subclass, store a QList of all the QWidgets you want it to manage. Then, again in your QMainWindow subclass, reimplement methods such as QWidget::closeEvent(), QWidget::hideEvent() (for when the window is minimized), and QWidget::showEvent() (for when it is restored) so that it also closes, hides, or shows all of the widgets in its QList. Make sure to also delete them in the QMainWindow subclass's destructor. Now, whenever you create a sub-window, pass the main window a pointer to it not as a normal QWidget child, but just so that it can be added to the main window's QList of QWidgets to manage. E.g.:
MainWindowSubclass::addPseudoChild(QWidget *pseudoChild)
{
myListOfPseudoChildren.append(pseudoChild);
}
Another alternative that hasn't been mentioned yet is populating a QMdiArea with QMdiSubWindows. It doesn't do exactly what you asked for, but it's a pretty clean design nonetheless.
So I thought I would add what I eventually settled upon. This was particularly inspired by the comments of #leemes (Thanks - good stuff) and a little experimentation of my own.
I used the code attached here DetachTabExample
to develop a "Detachable Tab" widget and tab bar. This allows tabs to be dragged outside the main window when they become independent windows. Then if closed they return to the tab bar.
Then I placed all my content in the QMainWindow but in separate tabs. The users can drag the ones they want out on to the other monitor. Seems to be working fine. There are still some extra windows that I have floating but it has cut down the clutter and clarified the structure.

How to update a QLayout and get the new dimensions before returning?

This is driving me nuts. I have a custom menu class that, when set visible, shows a list of items located in a particular folder. When a hardware button is pressed, my application gets the latest list of items, populates the menu with them, and returns.
The menu displaying these items uses a QListWidget filled with custom widgets. Each of the widgets contains one or more QLabels in a horizontal layout, and is created at the time the menu is shown. In order to adjust the text displayed based on the menu width available, I need to get the size of the QLabel AFTER it has been resized according to the layout, but before the menu becomes visible to the user. The problem is, my layout does not get updated until all of the functions constructing my list return.
I have tried QApplication::ProcessEvents() and the layout update functions, but none of them have updated the values of my QLabels before returning. I can set a QTimer when the button is initially pressed, and have it show the menu, update the items, and stop itself, but that seems like a terrible solution.
Any help would really be appreciated! I've spent most of a day on this.
Marlon
I had this exact problem and could not find an answer anywhere on the Internet. Calling Layout.update(), Layout.activate(), or widget.adjustSize() (all suggested in various places) all did not work.
I had a widget with a vertical layout that I wanted to add a QLabel to and then immediately use the size of the QLabel.
The only thing that worked reliably was
layout->addWidget(myLabel);
myLabel->show();
size = myLabel->size();
It would seem that layouts will just not recalculate until you either return from a function and allow the Qt event loop to progress or manually call show() yourself.
How to update a QLayout and get the new dimensions before returning?
Don't. You're not meant to do that. It'll drive you "nuts" because you're doing it backwards. Layout updates are handled asynchronously from the event loop. Instead of getting layout dimensions right away, set yourself up to be part of the system. Some options are:
Implement a custom widget that will interact properly with the layout, growing to fill the available width of the layout. Perhaps all you need is a size policy and a way to elide text?
Make a custom layout that takes the special properties of your use case into account.
You want to call QWidget::adjustSize() on your parent widget. This will force the layout recalculations.
Have you tried using layout()->update(); ?
I've tried many but nothing works for me on Qt 5.15.
Only invented little patch - create timer and get size after 20 msec:
QTimer::singleShot(20, this, [this]
{
const auto height = myLayout->contentsRect().height();
// ...
});

Attach Qt windows?

Is there any way to attach two Qt windows together? For example, if window A is the main window and window B is another widget I want to be able to show window B to the side of A and have both windows move together if the windows are dragged.
Not that I am aware of, but you can try following the trail of QMoveEvent. When a given widget is moved void QWidget::moveEvent ( QMoveEvent * event ) is called, and the QMoveEvent contains both old and new pos. Using this information, you can inject a move event in the other widget as well, and make it follow.
Of course, I am speaking of two independent widgets, each one in its own window. If they are contained, you don't need anything but a Layout management (see QLayout and related classes).
I don't work with Qt since a long time, so there could be a better method, but if I had to do it right now, this is the strategy I would use.
Also, I have the feeling that the QMoveEvent will be invoked only at start and end, unless you enable mouse tracking. If the former is the case, you will obtain that the other widget will "teleport" at the end of the move, instead of following smoothly.
You might be after something like this:
http://doc.qt.io/archives/4.6/qt4-mainwindow.html
Window A would be a QMainWindow and window B would be a QDockWidget.

Resources