I have a Qt3D application with a Scene3D QML object taht is used to render my 3D scene only, on top of that i have regular 2D qml item/objects to render my ui.
I want to modify the cursor's shape whenever the user is hovering on the 3d scene only, and to reset the cursor when he is entering any 2d widget.
Is there an elegant way of doing this ? I've tried to add a MouseArea on the Scene3D but this doesn't work. Or to add one after all the other so that it is the last to catch mouse events, but if I understand correctly that would mean that I would have to go back and add MouseArea to every QML component that I have.
Thank you for your help.
I do not know what is your problem (as you posted no code) but following simple MouseArea (in Rectangle where the scene view is) seems to work for me:
MouseArea {
hoverEnabled: true
anchors.fill: parent
cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor
}
That resets back to ArrowCursor just fine.
Related
I'm trying to achieve a similar effect as iTunes' miniPlayer when resizing happens on macOS. That is, detecting when a resizing of the the window has been completed, THEN changing the height to a certain value. Here is a visual example :
The problem is that no signal in a QML window exists to notify me when the window manager is done resizing (that is, the user released the handle). Hence, if I don't have a signal and apply my height change as soon as width or height is changed during resizing, the window will flicker (double resize happens) as long as the user didn't release the handle.
Thanks for any input or help!
You could implement your own resize handle pretty easily, using a MouseArea and handling the final resize calculation using onReleased (here forcing the height to be 75% of the width on release):
Window {
id: window
flags: Qt.FramelessWindowHint
visible: true
height: 300
width: 400
MouseArea {
id: resize
anchors {
right: parent.right
bottom: parent.bottom
}
width: 15
height: 15
cursorShape: Qt.SizeFDiagCursor
property point clickPos: "1,1"
onPressed: {
resize.clickPos = Qt.point(mouse.x,mouse.y)
}
onPositionChanged: {
var delta = Qt.point(mouse.x-resize.clickPos.x, mouse.y-resize.clickPos.y)
window.width += delta.x;
window.height += delta.y;
}
onReleased: {
window.height = .75 * window.width
}
Rectangle {
id: resizeHint
color: "red"
anchors.fill: resize
}
}
}
QML provided some NOTIFY signals when property values are supposed to be updated. So you can use Window.width's and Window.height's:
Window {
id: window
onWidthChanged: {
// Will be executed after window.width value changes.
}
onHeightChanged: {
// Will be executed after window.height value changes.
}
// Other window-related stuff
}
Here is an idea (that I did not try yet, but still):
Implement an event handler reacting to changes of the global cursor position. The cursor position inside the window is not applicable as the cursor is outside the window when grabbing a resize handle. Not sure if and how such an event handler is possible, but at least accessing the screen-global cursor position is possible in Qt.
In the event handler, check if the vertical cursor position change compared to the last call of the handler is the same as the window height change.
If yes, assume that the user still holds the window resizing handle, so don't adapt window height.
If no, assume that the user released the window resizing handle and started to move the cursor around freely. Adapt the window height now.
You'll probably have to overcome several issues, such as (1) allowing for a certain divergence between the window height and cursor y position changes, as window height changes might be less frequent and trail the cursor movement somewhat, (2) engaging the cursor position event handler only during a window resize to limit system load. But if it works, it's a native solution, not implementing own resizing handles.
It seems you want QML to send two different signal types, one to mark the start and finish of resizing, and one to mark size changes during resizing. The event sequence would then be something like this:
window.resizeStarted() // hypothetical new event
window.widthChanged()
window.heightChanged()
window.widthChanged()
window.heightChanged()
...
window.resizeEnded() // hypothetical new event
That is apparently impossible in Qt out of the box, but you should be able to implement it yourself with this approach, originally meant to not repaint the window at all while resizing:
Filter the relevant events out until the mouse button has been released. Specifically, "eat" the resize event while the mouse button is held down, and then synthesize the final resize event once the mouse is released. You can do it all in an event filter attached to the window / widget object that displays your QML interface. (source)
The process would be very similar to the one in the quote:
Extend the QML Window type with custom signals:
//MyWindow.qml
Window {
signal resizeStarted()
signal resizeEnded()
}
Create an event filter on the QML window that "eats" all window resize events. When it encounters the first one, it sends resizeStarted(). Then it forwards the window resize events. When it encounters a mouse release event while resizing, it sends resizeEnded() after the last widthChanged() / heightChanged() event.
Implement a corresponding signal handler onResizeEnded in QML to react, here to adapt the height of your application window to a certain fixed value.
Seems quite a promising route to me, but to note, I haven't tried it in code so far.
I am currently working on a virtual reality project that builds on OpenGL. Because I also needed some form of user interface, I thought it would be a good idea to integrate QtQuick windows into the scene. Drawing the window to a texture works without problems (I used this example) but I struggle to send mouse events so that my controllers can interact with it.
Here is a quick example video on YouTube. In this example, the animation of the embedded GIF should stop whenever I hover over it. This works in a normal QML application but not when I manually send a MouseMove event.
The mouse position within the window is known (the red line in the video indicates an intersection) and I am currently sending the event through
QQuickWindow::sendEvent(QQuickItem* item, QEvent*)
where item is the root Rectangle in the qml source:
import QtQuick 2.3
import QtQuick.Controls 2.0
Rectangle {
color: mouseArea.containsMouse ? "red" : "white"
width: 600
height: 400
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
AnimatedImage {
anchors.fill: parent
paused: mouseArea.containsMouse
source: "test.gif"
}
}
}
The event, which I send every time an intersection between the ray and the window is found, is done with:
QMouseEvent* mouseMoveEvent = new QMouseEvent(
QEvent::MouseMove, // wrong event?
cursorPosition, cursorPosition, // Note: cursorPosition := the calculated cursor coordinates within the window
Qt::MouseButton::NoButton,
Qt::MouseButtons(), // is this right?
Qt::KeyboardModifier::NoModifier);
window->sendEvent(rootItem, mouseMoveEvent); // Note: window is my QQuickWindow, rootItem is the root Rectangle
I am not an expert in QML (rarely used it) and would appreciate it if someone has suggestions on how to solve this. My guess is, that I am neither using the right event nor correctly sending them.
I found a solution myself. Instead of using QQuickWindow::sendEvent(..) I had to simply use QApplication()::instance()->sendEvent(..).
I have a QDockWidget:
I would like to alert the user to certain events by setting the background color of the title bar.
I have achieved this by setting the style sheet for my DockWidget:
void DockWidget::setCriticalAlert()
{
setStyleSheet("QDockWidget { background-color:red; }");
}
The result is this:
The problem is that the background-color doesn't get applied when the QDockWidget is docked:
How can I get the background color to be applied when the QDockWidget is docked?
This is a bug in Qt.
Issue 10537
Quoting from the linked issue:
The problem is that in QDockWidget::paintEvent, there is a
isFloating() condition before drawing PE_FrameDockWidget. We cannot
jsut remove this condition as it would break the other style (that
does not whish to draw frame when the dockwidget is docked) We cannot
either use PE_Widget to draw the frame as then it goes over the
dockwidget's title The solution is maybe to introduce a new
PE_FrameDockWidgetDocked primitive element. Or some
SH_DockWidget_DrawDockedFrame stylehint to draw the frame in every
cases.
a valid workaround seems to be to set the stylesheet of the parent, and use the class-and-id selector. Forgive the python formatted code but the concept is the same - in this case, 'dock' is a QDockWidget which has been given an object name using setObjectName(), and its parent, the QMainWindow, is 'self':
self.setStyleSheet("QDockWidget#"+str(dock.objectName())+"::title {background-color:red}")
In PyQt5.5, this works at runtime, i.e., can be changed on the fly.
I find a solution like this:
Firstly put a frame behind all the widgets of dockwidget's center widget, as the background.
Then set stylesheet for the frame.
By this way, we could change the background color of dockwidget.
Or you can extend the dockwidget and overwrite the function
void QDockWidget::setWidget(QWidget *widget)
using private/qdockwidget_h. and add a frame as this widget's father.
Currently I am developing in Qt / Qml, using QtQuick.
I came to a point, where I need to find out, if an item of my scene is topmost. That is, I need to find if the item has the highest z-coordinate.
I tried to build an algorithm, using childAt and mapToScene. It turned out, that this is horribly slow. Then I found out about itemAt(). Unfortunately, it is only available in GraphicScene, i.e. in the widget world.
Now my question: Is there an equivalent to itemAt() in the QmlSceneGraph? How can I find out, if an item is the topmost one? I do not even need the exact z-order, just to make sure, the item is displayed at top.
Thanks for your help.
Greetings from Germany
You can use Item::childAt(real x,real y) method
ApplicationWindow {
id: root
visible: true
MouseArea {
anchors.fill: parent
onClicked: console.log(root.contentItem.childAt(mouse.x,mouse.y));
}
}
I have a custom widget which inherits from QWidget and contains some labels in its layout. I would like to change the background color of the widget and the labels in the widget (this is, everything!) every time I put the mouse over it.
When using *:hover { background: red; } in my custom widget, I only get the contents red when moving the mouse over the labels, but not outside them, between labels, etc. I don't understand this behavior taking into account that I put the StyleSheet in the parent widget.
Any ideas? Many thanks,
You can set the parent's stylesheet which will cascade to children like this:
parent->setStyleSheet("* {background: red}");
For hovering only:
parent->setStyleSheet("*:hover {background: red}");
Check out https://qt-project.org/doc/qt-5.1/qtwidgets/stylesheet-syntax.html
Finally I solved the problem creating a QFrame inside the main QWidget and setting the StyleSheet of that QFrame.