Qt set custom drag cursor? - qt

How can I set the cursor used in Qt while performing a drag operation? I am using the QDrag class. The function setCursor takes a pixmap and has no way to specify the hotspot, nor do the docs specify that it could override the "no action" cursor.
I'm happy if I can just do an explicit cursor in mouseMoveEvent but I'm not sure how during a drag operation.

Checking the source code it appears Qt is lame in this regards and has no mechanism to do this. For the X11 code the function QDragManager::updateCursor variables which contain the cursors it uses. Those are created using the QCursor constructor with constant hot-spot values (0,0). The ForbiddenCursor is completely hard-coded, thus preventing any alternation.
To set the cursor it calls QApplication::changeOverrideCursor. As a static function there is no way to intercept that call.
Even if the pixmaps are set (via setCursor) the intial drag cursor is still the default. This just appears to be a defect in QT. This is at qdnd_x11.cpp:1948, the pointer cursor is forcibly set at the start of a drag
Thus there is no actual way to use custom cursors for the standard drag-n-drop.

Related

How to intercept / modify excape key functionality in Qt

I am trying to fix a bug in a Qt app which I did not write. The window changes the background color of the entire window to red and puts up some buttons, dialog boxes, etc. When the escape key is pushed, the boxes and buttons go away, leaving an empty red screen. The Cancel button does the right thing in returning to the previous window. I think I need to somehow be notified of when the escape key is pushed, and then call the same function as the cancel pushbutton does. Hopefully, I can limit the scope of this special action to when the problem window is up. I am an experienced programmer but a complete Qt newbie. This app is purely C++. To my knowledge, it does not appear to use any QML. I am still searching through the Qt online documentation, but any suggestions / examples are appreciated.
This depends a lot on your specific Qt version and setup of your application. That said, I'll take a shot at helping. In the class where you're trying to intercept the escape key press, assuming it's inheriting QObject, simply override the virtual function eventFilter. Inside your overridden instance of eventFilter, check that the QEvent type is a QEvent::KeyPress, and then check whether the key of that KeyPress is the escape key, and handle as needed.
Be sure that you pass the event out of your function, else you'll see your overridden function eat all events. If you explicitly want the event to be "eaten", simply do not return it from your function. For specifics and examples, check out documentation of QObject::eventFilter().

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.

How to make a QSlider read-only?

Using Qt 5.2.1
Is it possible to set a QSlider (doesn't matter if it's horizontal or vertical) to read-only that is user cannot change the value of the slider but only use it as an indicator of some sort? I was unable to find anything in the Qt documentation or the Qt Designer.
Example for application: displaying a binary state of some sort in the GUI (in my case is the emergency stop on or off).
AFAIK such feature is not available in the QSlider implementation.
However, you can create your own class deriving from QSlider and implement the desired behavior by overwriting mousePressEvent, mouseReleaseEvent, mouseMoveEvent, keyPressEvent and keyReleaseEvent and only call the respective parent implementation if the readOnly property is set to false.
Luckily, such an implementation is already available in kalarm, so have a look at it: http://api.kde.org/4.6-api/kdepim-apidocs/kalarm/lib/html/slider_8cpp_source.html
Maybe a QProgressBar would be more suitable since users know it as "read only" and "shows how much has been done".
Following kuba ubar's second approach -
Suppose the object name of your slider is horizontalSlider. Then the code should be
// getting the palette of the slider
QPalette _sliderPalette = ui->horizontalSlider->palette();
// changing the colorGroup of that palette
_sliderPalette.setCurrentColorGroup(QPalette::Active);
// setting the changed palette to the slider
ui->horizontalSlider->setPalette(_sliderPalette);
One simple solution would be to install an event filter on the slider that consumes all mouse, focus and keyboard events. You'd also need to make the slider have a Qt::NoFocus policy. Such an event filter would be universal and could be used with any control widget.
An alternative would be to disable the widget, and style it so that the disabled and enabled palette are the same. This might not work with some of the platform styles, though, and would need experimental verification before you commit to it.

How to enable both internal reordering and external dropping in a Qt widget?

I have created a widget which inherits QListWidget.
My goal is for it to accept files dropped into it from an external file manager, and for the user to be able to reorder the elements in the widget. I can achieve both, but not at the same time.
If I just set
myWidget->setDragDropMode(QListView::InternalMove);
myWidget->setDragEnabled(true);
I can reorder the items within the widget, but I can't drop external items into it.
If I reimplement the dragMoveEvent, dragEnterEvent and dropEvent events, all of them just having acceptProposedAction(); and some debug messages inside them, I can drop external files into my widget, but I can no longer rearrange the items.
Is there a way to have the above two at the same time, or do I have to manage the items myself in the reimplemented functions? If so, how can I know if a dropped item is internal or external, and how can I know from which position it was taken and into which position in the list it was dropped into?
If I parse the mimeData which I got from the event, I can see whether it as a file or a text, and I get "qabstractitemmodeldatalist" if it was an internal item, but it still doesn't give me its position.
I can check event->pos() to know in pixels where the drop has been made, and event->source() to learn about what was dropped there, but is this really best practice, to start calculating pixel values and adding objects "manually"?
The solution was very simple: I just had to call the functions of the parent class at the end of each function I've overridden.
void myWidget::dropEvent(QDropEvent *event)
{
do_stuff_with_received_data(event);
QListWidget::dropEvent(event);
}

QAbstractItemDelegate painting while dragging problem

I'm overloading the paint() function in QAbstractItemDelegate (my own Item delegate class).
When dragging, it paints the contents of the entire cell, which I don't want. I'm assuming that the paint() function is called with something specific while dragging, but I don't seem to find it.
The closest I've been able to find is a QState variable in the owning view class (access function QTableView::state() is protected.) By creating a function on my QTableView-derived class called 'isDragging()' which calls that function and returns whether dragging or no, I can determine within my delegate class whether I'm dragging or not, and can modify the paint() function.
This almost works.
The problem is that it shows the modified paint image in the original cell, which I don't want - I want to leave the image in the original cell untouched.
Have to scour the examples, I guess, and see if there's something that does this...
I have crawled through the Qt source and I can see where it sets the drag pixmap by calling the QItemDelegate::paint() function, but the only thing it changes is it forces QStyle::State_Selected in the item option style. That's not enough, since the item is selected, already.
Any way to know how to draw a cell's contents explicitly when dragging?
Ok, the ultimate answer on this was to, yes, set the flag on 'startDrag', but rather than leaving it around and unsetting it on mouse release button event, simply call the base method and then unset.
The reason is that the image for the cursor is only requested (and painted) once - not continuously during the drag, as I had first thought. Leaving the flag set meant the cursor image would get drawn at inappropriate times.
So, the implementation looks like:
MyClass::dragStart(Qt::DropActions supportedActions)
{
__dragStart = true;
TableView::dragStart(supportedActions);
// request for drag cursor image happens here
__dragStart = false;
}
Why don't you do that yourself? Set a flag when dragging starts and remember the active ModelIndex, do some special painting when the flag is set, and clear the flag when dragging is finished. You can do this by overriding QAbstractItemView::startDrag.

Resources