Qt: Reordering ListView Items using drag and drop - qt

I have implemented the following methods for drag and drop
void mousePressEvent(QMouseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
void startDrag(Qt::DropActions supportedActions);
I have a QListView and have used setIndexWidget() to show a custom widget at each modelindex. I am trying to arrange the items in the listview using drag and drop, However the items in the listview are not arranged properly, I have written the following code:-
MyListView::MyListView(QWidget *parent) :
QListView(parent)
{
setSelectionMode(QAbstractItemView::SingleSelection);
setDragEnabled(true);
setAcceptDrops(true);
setDragDropMode(QAbstractItemView::InternalMove);
setDefaultDropAction(Qt::MoveAction);
setDropIndicatorShown(true);
setDragDropOverwriteMode(true);
setMovement(QListView::Snap);
setFlow(QListView::TopToBottom);
// setStyleSheet("QListView::item {"
// "height: 50px;"
// "border: 1px solid black;}");
}
void MyListView::setModel(QAbstractItemModel *model)
{
QListView::setModel(model);
for (int i=0; i<model->rowCount(); i++)
{
QModelIndex index = model->index(i,0);
ListWidgetItem *widget = new ListWidgetItem;
widget->setIndex(i);;
setIndexWidget(index,widget);
}
}
void MyListView::mousePressEvent(QMouseEvent *event)
{
QModelIndex i = indexAt(event->pos());
if (event->button() == Qt::LeftButton)
{
ListWidgetItem *item = dynamic_cast<ListWidgetItem *>(childAt(event->pos()));
if (!item)
return;
y = event->pos().y()-item->pos().y();
QModelIndex indx = indexAt(event->pos());
currentPixmap = QPixmap::grabWidget(item);
dragPoint = event->pos();
if (event->pos().x() < 25 && indx.row() >= 0)
startDrag(Qt::MoveAction);
}
}
void MyListView::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("application/x-QListView-DragAndDrop"))
{
event->accept();
}
else
event->ignore();
}
void MyListView::dragMoveEvent(QDragMoveEvent *event)
{
ListWidgetItem *item = dynamic_cast<ListWidgetItem *>(childAt(event->pos()));
if(!item)
return;
itemList.append(item);
foreach (ListWidgetItem *widget,itemList)
{
if (widget == item)
widget->setStyleSheet("#ListWidget { border-top: 2px solid red; }");
else
widget->setStyleSheet("#ListWidget { border-top: 0px solid black; }");
}
if (event->mimeData()->hasFormat("application/x-QListView-DragAndDrop"))
{
event->setDropAction(Qt::MoveAction);
event->accept();
} else
event->ignore();
}
void MyListView::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat("application/x-QListView-DragAndDrop")) {
QByteArray itemData = event->mimeData()->data("application/x-QListView-DragAndDrop");
QDataStream dataStream(&itemData,QIODevice::ReadOnly);
QPoint itemPoint;
dataStream >> itemPoint;
ListWidgetItem *item = dynamic_cast<ListWidgetItem *> (childAt(itemPoint));
if(!item)
return;
QModelIndex index = indexAt(event->pos());
int row = index.row();
model()->insertRows(row,1);
QModelIndex idx =model()->index(row,0);
setIndexWidget(idx,item);
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
foreach (ListWidgetItem *widget,itemList)
{
widget->setStyleSheet("#ListWidget { border-top: 0px solid black; }");
}
itemList.clear();
// model()->removeRow(n+1);
event->accept();
}
} else{
event->ignore();
}
}
void MyListView::startDrag(Qt::DropActions supportedActions)
{
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
ListWidgetItem *item = dynamic_cast<ListWidgetItem *>(childAt(dragPoint));
dataStream << dragPoint;
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-QListView-DragAndDrop", itemData);
drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setHotSpot(mapToParent(QPoint(item->x(),y)));
drag->setPixmap(currentPixmap);
QModelIndex indx = indexAt(dragPoint);
if (drag->exec(Qt::MoveAction | Qt::CopyAction) == Qt::MoveAction)
{
model()->removeRow(indx.row());
}
}
I have searched but didnt get any proper solution.
Thanks

Have you looked at QAbstractItemView's DragDropMode ? http://qt-project.org/doc/qt-4.8/qabstractitemview.html#dragDropMode-prop
It sounds as though this ius what you are trying to achieve.

Related

Getting subclassed qgraphicsitem coords when casting by type

I have a subclass of a qgraphicsitem to create a group of items that goes in another subclass that goes to a subclassed scene.
When casting by subclass I can get the items in the good scene coords but item.pos() = (0,0) or the wrong position using item->setPos();
1) Here I get the item coords but the wrong position when adding.
2) Here I get the item coords = (0,0) but the items are in the good position.
void addExtChordseistris()
{
QPoint p = QCursor::pos();
for(QGraphicsView *view: views()){
QWidget *viewport = view->viewport();
QRect vr = viewport->rect();
QPoint vp = viewport->mapFromGlobal(p);
if(vr.contains(vp)){
QPointF sp = view->mapToScene(vp);
chord *itemdos = new chord();
addItem(itemdos);
itemdos->addchorddos(sp);
itemdos->setPos(sp); // -> using this or not
}
}
}
This is what gives me pos.() = (0,0).
void debugSceneItemscuatro()
{
QList<QGraphicsItem *> allitems = items();
foreach(auto item, allitems) {
if(item->type() == chord::Type){
qDebug() << item->pos();
}
}
}
This is how the item were added:
#include "chord.h"
#include "note.h"
chord::chord()
{
Pressed = false;
setFlag(ItemIsMovable);
// QList<QGraphicsItem> coso;
}
QRectF chord::boundingRect() const
{
// outer most edges
return QRectF(0,0,100,100);
}
void chord::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rect = boundingRect();
if(Pressed)
{
QPen pen(Qt::red, 3);
painter->setPen(pen);
// painter->drawEllipse(rect);
}
else
{
QPen pen(Qt::black, 3);
painter->setPen(pen);
// painter->drawRect(rect);
}
}
//[1] Add chord
void chord::addchord(QPointF sp)
{
// scene()->addLine(sp.x(), sp.y(), sp.x()+10, sp.y()+10);
auto *line = scene()->addLine(sp.x(), sp.y(), sp.x() + 10, sp.y() + 10);
line->setParentItem(this);
QList<int> midics = {10, 30, 40};
for(int i = 0; i < midics.length(); i++)
{
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem("n", this);
item->setFont(QFont("omheads", 20));
item->setPos(sp.x(), sp.y()+midics[i]);
// scene()->addItem(item);
coso.append(item);
}
}
//[1] Add chord
void chord::addchorddos(QPointF sp)
{
// scene()->addLine(sp.x(), sp.y(), sp.x()+10, sp.y()+10);
auto *line = scene()->addLine(sp.x(), sp.y(), sp.x() + 10, sp.y() + 10);
line->setParentItem(this);
QList<int> midics = {0, 10, 30, 40};
for(int i = 0; i < midics.length(); i++)
{
note *item = new note();
item->setParentItem(this);
QPointF ssp = {sp.x(), sp.y()+midics[i]};
item->addnote(ssp);
// item->setPos(sp.x(), sp.y()+midics[i]);
// scene()->addItem(item);
// coso.append(item);
}
}
//void chord::getobjects(QGraphicsItem item)
//{
// return coso;
//}
QList<QGraphicsItem*> chord::retrievedata() const
{
return coso;
}
void chord::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Pressed = true;
update();
QGraphicsItem::mousePressEvent(event);
}
void chord::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Pressed = false;
update();
QGraphicsItem::mouseReleaseEvent(event);
}
And the other subclass,..
void note::addnote(QPointF ssp)
{
QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem("n", this);
item->setFont(QFont("omheads", 20));
item->setPos(ssp.x(), ssp.y());
}
Thanks,...maybe I'm doing a wrong approach, thanks for any Idea, solution...! :-)

Drag and Drop QFrame

I've been looking at drag and drop examples and am still puzzled with them. In particular I don't know what to do in mousePressEvent( ) and dropEvent().
I'm following and modifying example which drags and drops QLabel.
Here is what I think mousePressEvent() should look like for QFrame. I tried it and I can see the frame move but I lose it when I drop it.
void SummaryBar::mousePressEvent(QMouseEvent *event)
{
QFrame *child = static_cast<QFrame*>(childAt(event->pos()));
if (!child)
return;
QPixmap pixmap = QPixmap::grabWidget(child);
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << pixmap << QPoint(event->pos() - child->pos());
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-dnditemdata", itemData);
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(pixmap);
drag->setHotSpot(event->pos() - child->pos());
if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) {
child->close();
} else {
child->show();
}
}
Here is dropEvent(). It obviously does not work... not sure why.
void SummaryBar::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
QPixmap pixmap;
QPoint offset;
dataStream >> pixmap >> offset;
QFrame *newFrame = new QFrame(this);
//newFrame->setPixmap(pixmap);
newFrame->move(event->pos() - offset);
newFrame->show();
newFrame->setAttribute(Qt::WA_DeleteOnClose);
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
}
void SummaryBar::dragEnterEvent(QDragEnterEvent *event)
{
if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
if (event->source() == this) {
event->setDropAction(Qt::MoveAction);
event->accept();
} else {
event->acceptProposedAction();
}
} else {
event->ignore();
}
}
I would greatly appreciate any help!!

How can I prevent transform cursor to SplitHCursor when it's under border between QHeaderView sections

There are N columns with manual resizing width from left. Other columns widths are resizing only when columns with manual resizing are resizing. I need to prevent cursor icon changing when cursor is under borders of sections without manual resizing.
What did I try to do. But this is work not very good.
table_header_border.zip
#include "mainwindow.h"
#include "ui_mainwindow.h"
const int N = 2;
//==============================================================================
int nWidth(const QTableWidget *table)
{
int ret = 0;
if (table->verticalHeader()->isVisible())
{
ret += table->verticalHeader()->width();
}
for (int i = 0; i < N; i++)
{
ret += table->columnWidth(i);
}
return ret;
}
bool isInNColumns(const QTableWidget *table)
{
QPoint cursorPos = table->mapFromGlobal(QCursor::pos());
return cursorPos.x() < nWidth(table) + 5;
}
//==============================================================================
class MyHorizontalHeader : public QHeaderView
{
public:
MyHorizontalHeader(QWidget *parent=0) : QHeaderView(Qt::Horizontal, parent)
{
setMouseTracking(true);
}
private slots:
void mouseMoveEvent(QMouseEvent *event)
{
QHeaderView::mouseMoveEvent(event);
if (cursor().shape() == Qt::SplitHCursor)
{
QTableWidget *table = dynamic_cast<QTableWidget *>(parent());
if (table != NULL && !isInNColumns(table))
{
qApp->setOverrideCursor(QCursor(Qt::ArrowCursor));
return;
}
qApp->setOverrideCursor(QCursor(Qt::SplitHCursor));
}
}
};
//==============================================================================
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->tableWidget->setHorizontalHeader(new MyHorizontalHeader(this));
}
MainWindow::~MainWindow()
{
delete ui;
}
You should use QEvent::Enter and QEvent::Leave for better result.
Use next event filter:
In header:
protected:
bool eventFilter(QObject *obj, QEvent *event);
In constructor:
qApp->installEventFilter(this);
EventFilter:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui->tableWidget && event->type() == QEvent::Enter)
{
qApp->setOverrideCursor(QCursor(Qt::ArrowCursor));
//or
qApp->setOverrideCursor(ui->tableWidget->cursor());
qDebug() << "added";
}
if (obj == ui->tableWidget && event->type() == QEvent::Leave)
{
qApp->restoreOverrideCursor();
}
return QObject::eventFilter(obj, event);
}
horizontalHeader()->setSectionResizeMode(i, QHeaderView::Fixed);

Widgets drawing in Qt

I have been looking all over the Internet and I don't find any solution to my problem so I have a bar like this:
(Scroll bar with pictures for handlers)
But when I scroll the pictures through the other what happen is:
Is there anyway to stop over painting the sad face over the happy one?
This is the paintEvent method
void QSlider::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
if (ori_==Qt::Horizontal)
{
if(index>=0)
{
this->setFixedSize(42,57);
printf("\n");
QString dir="C:\\Qt\\Qt5.2.1\\Tools\\QtCreator\\bin\\pluginss\\";
QString emo=QString::fromStdString(emotions.at(index));
dir.append(emo);
dir.append(".png");
QPixmap pixmap(dir);
painter.drawPixmap(QPoint(0,0), pixmap.scaled(40,40, Qt::KeepAspectRatio));
QRect rec(0,22,40,40);
painter.drawText(rec,Qt::AlignCenter,emo);
}
else
{
this->setFixedSize(10,20);
QRect rec(0,7,8,8);
painter.drawRect(rec);
QPolygon pp;
pp << QPoint(0,7) << QPoint(4,0) << QPoint(8,7);
painter.drawPolygon(pp, Qt::OddEvenFill);
}
}
}
This is my mouseMoveEvent:
void QSliderWidget::mouseMoveEvent(QMouseEvent* e)
{
if (activeSlider_>=0)
{
QRect crec = contentsRect();
qreal pos;
crec.adjust(rampeditor_->bspace_,0,-rampeditor_->bspace_,0);
pos = 1.0*(e->pos().x()-rampeditor_->bspace_)/(crec.width());
if (pos<0.0 || pos>1.0)
{
delete(rampeditor_->sliders_[activeSlider_]);
rampeditor_->sliders_.removeAt(activeSlider_);
activeSlider_ = -1;
rampeditor_->updateRamp();
}
else
{
rampeditor_->sliders_[activeSlider_]->move(0,e->pos().y());
seconds.replace(activeSlider_-1,e->pos().x());
qSort(rampeditor_->sliders_.begin(), rampeditor_->sliders_.end(), QColorRampEditor::SliderSort);
index=activeSlider_-1;
rampeditor_->mergeSort(0,seconds.size());
}
}
}

Qt 4.7 - Drawing a 2 point line with dynamic feedback using paintEvent

so I am trying to draw a line between two points. Left mouse click starts the line then I would like the line to by dynamically drawn as the mouse moves (almost like a preview of the line). Left mouse click again and the line will be permanently drawn. I know there are a lot of other posts about QPaintEvents and I have combined some of the techniques used, but for some reason nothing is being drawn to the canvas. Below is the code:
void Main::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton) {
QPointF pos = event->pos();
if( mStartPoint.isNull() ) {
if(josh.contains(pos))
mStartPoint = pos;
} else {
canvas.addLine(mStartPoint.x(),mStartPoint.y(),pos.x(),pos.y());
mStartPoint = QPointF();
}
}
}
bool Main::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (!mStartPoint.isNull()) {
m_targetImage = QImage(canvas.width(),canvas.height(),QImage::Format_ARGB32);
QPainter p;
p.begin(&m_targetImage);
p.drawLine(mStartPoint, mouseEvent->pos());
p.end();
}
statusBar()->showMessage(QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y()));
}
return false;
}
void Main::paintEvent(QPaintEvent *pe)
{
QPainter painter(this);
QPen pen(Qt::red);
pen.setWidth(10);
painter.setPen(pen);
painter.drawImage(0, 0, m_targetImage);
}
Any help is appreciated! Thanks! Josh
I think this is what you want. Change parameters according to your requirements.
//In your constructor
m_nInitialX = 0;
m_nInitialY = 0;
m_nFinalX = 0;
m_nFinalY = 0;
m_nPTargetPixmap = 0;
m_nPTargetPixmap = new QPixmap(400,400);
m_nbMousePressed = false;
void Main::mousePressEvent(QMouseEvent* event)
{
m_nbMousePressed = true;
m_nInitialX = event->pos().x();
m_nInitialY = event->pos().y();
}
void Main::mouseReleaseEvent(QMouseEvent *event)
{
m_nbMousePressed = false;
}
void Main::paintEvent(QPaintEvent *e)
{
if(m_nbMousePressed)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::green);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
}
QPainter painter(this);
painter.drawPixmap(0, 0, *m_nPTargetPixmap);
}
void Main::mouseMoveEvent(QMouseEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QPainter PixmapPainter(m_nPTargetPixmap);
QPen pen(Qt::black);
PixmapPainter.setPen(pen);
PixmapPainter.drawLine(m_nInitialX, m_nInitialY, m_nFinalX, m_nFinalY);
update(); // update your view
m_nFinalX = event->pos().x();
m_nFinalY = event->pos().y();
}
update(); // update your view
}
This piece of code will draw a 2 point line that you want.
Here's my example of how to paint lines in your way directly on the widget.
Declaration:
private:
void drawLines(QPainter *p);
QPoint startPos;
QPoint endPos;
bool inDrawing;
QVector<QLine> lines;
Setting initial values in constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
startPos = QPoint();
endPos = QPoint();
inDrawing = false;
setMouseTracking(true);
}
Starting or ending the line drawing:
void Widget::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton)
{
if (!inDrawing)
{
startPos = event->pos();
}
else
{
endPos = event->pos();
QLine line = QLine(startPos, event->pos());
lines.append(line);
}
inDrawing = !inDrawing;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (inDrawing)
{
endPos = event->pos();
update();
}
}
Drawing current and saved lines:
void Widget::drawLines(QPainter *p)
{
if (!startPos.isNull() && !endPos.isNull())
{
p->drawLine(startPos, endPos);
}
p->drawLines(lines);
}
Drawing:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter p(this);
QPen pen;
pen.setColor(Qt::red);
pen.setWidth(4);
p.setPen(pen);
drawLines(&p);
}

Resources