I am trying to write a simple program (nothing special) which has a QListView and some Buttons.
My question is:
How do I specifically tell the QListView to accept Drag and Drop from the filesystem?
I currently have
setAcceptDrops(true)
Which is OK, but the drag and drop works on the whole (Main)Window. I just want it to work when the file is dragged into the QListView.
Why doesn't this work?:
ui->listView->setAcceptDrops(true);
The whole code:
#include "player.h"
#include "ui_player.h"
#include <QListView>
Player::Player(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Player)
{
ui->setupUi(this);
setAcceptDrops(true);
//This doesnt work:
//ui->listView->setAcceptDrops(true);
}
Player::~Player()
{
delete ui;
}
void Player::dropEvent(QDropEvent *ev)
{
QList<QUrl> urls = ev -> mimeData() -> urls();
foreach(QUrl url, urls)
{
qDebug() << url.toString();
}
ev->acceptProposedAction();
}
void Player::dragEnterEvent(QDragEnterEvent *ev)
{
ev->acceptProposedAction();
}
You should override these event functions for QListView, not QMainWindow. When you do ui->listView->setAcceptDrops(true);, QListView is now the widget that reacts to the drop events, by calling its virtual dropEvent and dragEnterEvent functions.
Make your own class that inherits QListView and define dropEvent and dragEnterEvent in there:
class MyListView
{
public:
MyListView(QWidget *parent); // implement
protected:
void dropEvent(QDropEvent *ev) override; // implement
void dragEnterEvent(QDragEnterEvent *ev) override; // implement
};
You might also want to override dragMoveEvent as the reference says.
Related
C++ Qt newbe here. I work with a QDial object that is intended to be controlled with a mouse wheel, it works fine as such, emitting valueChanged() signals when necessary.
I would like to put a semi-transparent QToolButton on top of it, allowing users to click on the button (and set QDial value to a pre-defined number) while maintaining the ability to use the mouse wheel to control QDial as usual.
I experimented a bit with the TransparentForMouseEvents attribute:
ui->toolButton_Example->setAttribute(Qt::WA_TransparentForMouseEvents);
The problem is - the above code switches off all events, including the ability to emit the clicked() signal.
Is there a way to make a QToolButton transparent selectively for MouseWheelEvents while preserving the ability to respond to MouseClick events? Or would this require rewriting the event filter from scratch?
EDIT: Just to clarify - This question is about making QToolButton transparent to MouseWheel EVENTS while still allowing it to respond to MouseClick EVENTS. It is not about making the button transparent in the graphical sense.
SOLUTION
OK, problem solved the traditional way by subclassing QDial and overriding MousePressEvent and MouseReleaseEvent:
#include <QDial>
#include <QMouseEvent>
class QSuperDial : public QDial {
public:
QSuperDial (QWidget *parent = nullptr) : QDial(parent) {
}
virtual void mousePressEvent (QMouseEvent *event) override {
emit sliderPressed();
}
virtual void mouseMoveEvent (QMouseEvent * event) override {
}
virtual void mouseReleaseEvent (QMouseEvent *event) override {
}
};
Promoting QDial to QSuperDial results in a QDial object that 'behaves' like a button when pressed, emitting sliderPressed signal, while still being responsive to MouseWheelEvent (like a normal QDial).
I think this is the simplest and the most 'Qt-like' solution, but please do correct me if I'm mistaken.
You can use QObject::installEventFilter to have the parent object filter the events before they reach the tool button. Then, override the parent's QObject::eventFilter to handle/ignore the event.
I create an example below:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QToolButton>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event) override;
private:
QToolButton tool_button_ignored_;
QToolButton tool_button_handled_;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QDebug>
#include <QEvent>
#include <QHBoxLayout>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
tool_button_ignored_.setObjectName("tool_button_ignored_");
tool_button_ignored_.setText("Ignored button");
tool_button_ignored_.installEventFilter(this);
tool_button_handled_.setObjectName("tool_button_handled_");
tool_button_handled_.setText("Handled button");
tool_button_handled_.installEventFilter(this);
QWidget *central_widget = new QWidget{this};
QHBoxLayout *layout = new QHBoxLayout{central_widget};
layout->addWidget(&tool_button_ignored_);
layout->addWidget(&tool_button_handled_);
this->setCentralWidget(central_widget);
}
MainWindow::~MainWindow()
{
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (watched != &tool_button_ignored_ || event->type() != QEvent::Wheel)
{
qDebug() << event->type() << watched->objectName() << "handled";
return QMainWindow::eventFilter(watched, event);
}
else
{
qDebug() << event->type() << watched->objectName() << "ignored";
return true; // stop being handled further
}
}
I've the following situation:
Basically I've a QTreeWidget with some items, and a canvas that's a subclass of a QGraphicsView. What I want to accomplish is to drag a QTreeWidgetItem in the QGraphicsView subclass. When the mouse is released in the QGraphicsView subclass I want to create a new shape (let's say a circle) with some data of the dragged item (let's say the name itself written inside the circle).
I was able to enable the drag from the QTreeWidget by using the setDragEnabled(true) method, and I'm able to receive the event in the QGraphicsView, because when I enter its area it calls the dragEnterEvent.
In my subclass I've a QGraphicsScene, like this:
#ifndef SEQUENCECANVAS_HPP_
#define SEQUENCECANVAS_HPP_
#include <QWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
class SequenceCanvas : public QGraphicsView {
Q_OBJECT
public:
SequenceCanvas(QWidget* parent = nullptr);
virtual ~SequenceCanvas() = default;
protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
private:
void createWidgets();
void createLayout();
private:
QGraphicsScene m_scene;
};
#endif // !SEQUENCECANVAS_HPP_
#include "SequenceCanvas.hpp"
#include <QString>
#include <QHBoxLayout>
#include <QDragEnterEvent>
#include <QMimeData>
SequenceCanvas::SequenceCanvas(QWidget* parent) :
QGraphicsView(parent) {
createWidgets();
createLayout();
setScene(&m_scene);
setAcceptDrops(true);
}
void SequenceCanvas::dragEnterEvent(QDragEnterEvent* event) {
event->acceptProposedAction(); // no filter at the moment I just want to test
}
void SequenceCanvas::dropEvent(QDropEvent* event) {
int i = 0; // just for putting a breakpoint now. Its breakpoint is never raised
}
void SequenceCanvas::createWidgets() {
}
void SequenceCanvas::createLayout() {
}
Even if the dragEnterEvent is called when my mouse enter my widget during the drag, the dropEvent method is not called, so when I release the mouse inside my SequenceCanvas class nothing happens.
Basically I've two questions:
How can I raise the drop event inside my SequenceCanvas class that's a subclass of QGraphicsView?
How can I pass custom data (let's say a vector of strings) with the drop event, so I can pass all needed information for managing the event?
EDIT:
I missed to reimplement the void dragMoveEvent(QDragMoveEvent* event) method. I've added it and now I receive drop events. I need to know how to pass custom data with a drag-drop event now... Thanks to zeFrenchy.
My class body now is:
SequenceCanvas::SequenceCanvas(QWidget* parent) :
QGraphicsView(parent) {
createWidgets();
createLayout();
setScene(&m_scene);
setAcceptDrops(true);
bool ok = acceptDrops();
}
///////////////////////////////////////////////////////////////////////////////
// VIRTUAL PROTECTED SECTION //
///////////////////////////////////////////////////////////////////////////////
void SequenceCanvas::dragEnterEvent(QDragEnterEvent* event) {
event->acceptProposedAction();
}
void SequenceCanvas::dragMoveEvent(QDragMoveEvent* event) {
event->acceptProposedAction();
}
void SequenceCanvas::dropEvent(QDropEvent* event) {
int i = 0;
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE SECTION //
///////////////////////////////////////////////////////////////////////////////
void SequenceCanvas::createWidgets() {
}
void SequenceCanvas::createLayout() {
}
The documentation says the following about DragMoveEvent:
A widget will receive drag move events repeatedly while the drag is within its boundaries, if it accepts drop events and enter events. The widget should examine the event to see what kind of data it provides, and call the accept() function to accept the drop if appropriate.
So you should be provide an override for dragMoveEvent(QDragMoveEvent* event) in which you simply accept the proposed action.
Using Qt to create an application that accepts a file drop. I have an area on my UI that I want to drop the file into, using a Qlabel. I have the function of dragging and dropping the file into the UI working, however I can drop it anywhere on the window, and not just into the Qlabel area.
I thought using
ui->label_drag->setAcceptDrops(true);
would work, however this just removed the functionality all together. What is the best way of handling this? if possible at all.
Thanks
The best way to do this is to override the QLabel class. In the dragEnterEvent be sure to call acceptProposedAction to process the move and leave events. If you don't do that, only the dragEnter event will fire.
Sample code follows. To use this in your project, add the source to your project and then right click on the label on the form and promote the item to QLabelDragDrop.
#ifndef QLABELDRAGDROP_H
#define QLABELDRAGDROP_H
#include <QLabel>
class QLabelDragDrop : public QLabel
{
Q_OBJECT
public:
explicit QLabelDragDrop(QWidget *parent = nullptr);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
signals:
public slots:
};
#endif // QLABELDRAGDROP_H
#include "qlabeldragdrop.h"
#include <QDebug>
#include <QDragEnterEvent>
#include <QDropEvent>
QLabelDragDrop::QLabelDragDrop(QWidget *parent) : QLabel(parent)
{
setAcceptDrops(true);
setMouseTracking(true);
}
void QLabelDragDrop::dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << "dragEnterEvent";
event->acceptProposedAction();
}
void QLabelDragDrop::dragLeaveEvent(QDragLeaveEvent *event)
{
qDebug() << "dragLeaveEvent";
releaseMouse();
}
void QLabelDragDrop::dragMoveEvent(QDragMoveEvent *event)
{
qDebug() << "dragMoveEvent";
}
void QLabelDragDrop::dropEvent(QDropEvent *event)
{
qDebug() << "dropEvent";
}
I've been searching online for days and I cant find anything to help out with my specific problem. I'm trying to set up this dialog to accept files to be dropped into the QTreeWidget, named filesTreeWidget, but everything I've been searching online doesn't seem to make a difference. I'm pretty new to QT and C++ as well, so I'm sure that doesn't help.
Thanks for any help
Header
class FileIQ : public QDialog
{
Q_OBJECT
protected:
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
}
Cpp
FileIQ::FileIQ(QWidget *parent, DR::EnginePtr engine)
: QDialog(parent)
, ui(new Ui::FileIQ)
, engine_(engine)
{
ui->filesTreeWidget->setAcceptDrops(true);
ui->filesTreeWidget->setDropIndicatorShown(true);
setAcceptDrops(true);
}
void FileIQ::dropEvent(QDropEvent *event)
{
foreach(const QUrl &url, event->mimeData()->urls()) {
QString filename = url.toLocalFile();
qDebug() << "Dropped file:" << filename;
QTreeWidgetItem *item = new QTreeWidgetItem(ui->filesTreeWidget);
item->setText(0, filename);
}
}
void FileIQ::dragEnterEvent(QDragEnterEvent *event)
{
event->accept();
}
void FileIQ::dragMoveEvent(QDragMoveEvent * event)
{
event->accept();
}
void FileIQ::dragLeaveEvent(QDragLeaveEvent * event)
{
event->accept();
}
First, the right thing is to implement drag and drop within QTreeWidget, not inside QDialog. To do this we must create a class that inherits from QTreeWidget and we must implement the following protected methods:
bool QTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index,
const QMimeData *data, Qt::DropAction action)
Handles the data supplied by a drag and drop operation that ended with
the given action in the index in the given parent item.
The default implementation returns true if the drop was successfully
handled by decoding the mime data and inserting it into the model;
otherwise it returns false.
QStringList QTreeWidget::mimeTypes() const
Returns a list of MIME types that can be used to describe a list of
treewidget items.
Qt::DropActions QTreeWidget::supportedDropActions() const
Returns the drop actions supported by this view.
From the above we implemented this class:
#ifndef TREEWIDGET_H
#define TREEWIDGET_H
#include <QDropEvent>
#include <QTreeWidget>
#include <QMimeData>
#include <QFileInfo>
class FilesTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
FilesTreeWidget(QWidget *parent= Q_NULLPTR):
QTreeWidget(parent)
{
setAcceptDrops(true);
setDropIndicatorShown(true);
setColumnCount(2);
}
protected:
bool dropMimeData(QTreeWidgetItem *parent, int /*index*/, const QMimeData *data, Qt::DropAction /*action*/)
{
for(const QUrl url: data->urls()) {
const QFileInfo info( url.toLocalFile());
if(info.isFile()){
QTreeWidgetItem *item;
if (parent){
item = new QTreeWidgetItem(parent);
parent->setExpanded(true);
}
else
item = new QTreeWidgetItem(this);
item->setText(0, info.fileName());
item->setText(1, info.filePath());
}
}
return true;
}
QStringList mimeTypes () const
{
return QStringList()<<"text/uri-list";
}
Qt::DropActions supportedDropActions () const
{
return Qt::CopyAction;
}
};
#endif // TREEWIDGET_H
The complete example can be found in the following link. If you already have a QTreeWidget assigned by Qt Designer the simplest solution is to promote the Qt Designer QTreeWidget to use the new class.
Output:
I'm trying to write an editor for an rpg (Role Playing Game) (npc / quests / items etc.). I need to create an icon with a "white background" that represents the npc's image. It should be clickable (when it's clicked, current selected npc's icon ID will be set according to the selection).
I've managed to build a pop-up dialog to show all the icons, but couldn't manage to find a way to create clickable icons. Which class should I implement in order to get it working?
Clickable icons can be achieved using either QPushButton or QToolButton:
QPushButton* button = new QPushButton;
button->setIcon(QIcon("/path/to/my/icon"));
Clickable QLabel : https://wiki.qt.io/Clickable_QLabel
Use with a QPixmap : http://doc.qt.io/qt-4.8/qlabel.html#pixmap-prop
Header
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
Source
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
I've done something similar but didn't want something that looked like a button, nor did I want to get into style overrides or special painting. Instead, I created a ClickableLabel class that derives from QLabel.
The pertinent part of the code is:
class ClickableLabel : public QLabel
{
protected:
virtual void mouseReleaseEvent (QMouseEvent *evt)
{
emit clicked (evt->button ());
}
signals:
void clicked (int button);
...rest of class definition...
}
You can adjust the signal parameters as desired.