环境配置 :MinGW + QT 5.12 |
效果图:
这种自定义拖拽样式的灵感来自于Chrome浏览器的书签栏。文章中所使用的自定义QListWidget来自于:自定义QListWidget实现item被hover时改变图标样式(模仿网易云音乐选项列表)(方法二)中的TestListWidget类的拓展(在之前的基础上加入了拖拽的代码)。本文中拖拽的特点是:拖拽不影响选中 + doAutoScroll。
实现方法:
- 拖拽功能实现:继承QListWidget(重写drag事件)
- item在release时被选中:继承QListWidget(重写mousePressEvent和mouseReleaseEvent)
- item在被hover时改变图标样式:继承QListWigetItem + 继承QStyledItemDelegate
- 绘制dropIndicator:继承QListWidget(使用update()进行控制) + 继承QStyledItemDelegate (使用画笔进行绘制)
- 拖拽至边缘时doAutoScroll:继承QListWidget(使用定时器触发doAutoScroll()函数)
拖拽时缩略图thumbnail类:
推荐其它关于拖拽的文章:
- 拖拽之路(原生之初一):自定义QListWidget实现美观的拖拽样式
- 拖拽之路(原生之初二):自定义QListView实现美观的拖拽样式
- 拖拽之路(一):自定义QListWidget实现美观的拖拽样式(拖拽即选中)
- 拖拽之路(二):自定义QListWidget实现美观的拖拽样式(拖拽不影响选中)
- 拖拽之路(三):自定义QListView实现美观的拖拽样式(拖拽即选中)
- 拖拽之路(四):自定义QListView实现美观的拖拽样式(拖拽不影响选中)
- 拖拽之路(五):自定义QListWidget实现美观的拖拽样式(拖拽不影响选中 + doAutoScroll)
这里需要说明一下:QListWidget是鼠标press时item就会被选中,自定义的TestListWidget类重写了mousePressEvent和mouseReleaseEvent使得item在鼠标release时才会被选中。至于为什么这样做,是因为Chrome浏览器的书签栏以及网易云音乐的选项列表都是在鼠标release时才会触发选中…
正是由于item在鼠标release时才会被选中,所以才会出现 拖拽即选中 和 拖拽不影响选中 两种情况,而QListWidget是鼠标press时item就会被选中,所以只能是 拖拽即选中(拖拽的条件是press,导致item在触发拖拽时就被选中)。
(0)TestListWidgetItem类继承自QListWidgetItem
- TestListWidgetItem.h文件:
class TestListWidgetItem : public QListWidgetItem
{
//Q_OBJECT //由于QListWidgetItem没有QObject属性,所以Q_OBJECT需要注释掉
public:
explicit TestListWidgetItem(QListWidget *view = nullptr);
void setUpIcon(const QIcon &icon, const QIcon &icon_hover);
QIcon Img;
QIcon Img_hover;
};
- TestListWidgetItem.c文件:
TestListWidgetItem::TestListWidgetItem(QListWidget *view) :
QListWidgetItem(view)
{
}
void TestListWidgetItem::setUpIcon(const QIcon &icon, const QIcon &icon_hover)
{
Img = icon;
Img_hover = icon_hover;
setIcon(Img);
}
(0.5)MyTimer类继承自QTimer
class MyTimer : public QTimer
{
Q_OBJECT
public:
explicit MyTimer(QObject *parent = nullptr) :
QTimer(parent)
{
setTimerType(Qt::PreciseTimer);
}
public slots:
void getInterval(int value) {setInterval(value);}
void beginTimer() {start(10);}
void stopTimer() {stop();}
};
(1)TestListWidget类继承自QListWidget
- TestListWidget.h文件:
class TestListWidget : public QListWidget
{
Q_OBJECT
public:
explicit TestListWidget(QWidget *parent = nullptr);
bool isDraging() const {return IsDraging;}
int offset() const {return 19;}
int highlightedRow() const {return theHighlightedRow;}
int dragRow() const {return theDragRow;}
int selectedRow() const {return theSelectedRow;}
static QString myMimeType() { return QStringLiteral("MyListWidget/text_icon"); }
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
bool event(QEvent *event) override;
private:
QPoint startPos;
bool IsDraging = false;
QRect oldHighlightedRect;
QRect theHighlightedRect;
int theHighlightedRow = -1;
int theDragRow = -1;
int theSelectedRow = -1;
int theInsertRow = -1;
const int scrollMargin = 25;
int scrollBarValue = 0;
int scrollBarRange = 0;
int timeCount = 0;
const int timeLimit = 20;
const QRect targetRect(const QPoint &position) const;
private slots:
void doAutoScroll();
public slots:
void getScrollBarValue(int value) {scrollBarValue = value;}
void getscrollBarRange(int min, int max) {Q_UNUSED(min); scrollBarRange = max;}
signals:
void exceptedValue(int value);
void Interval(int interval);
void beginDrag();
void endDrag();
};
- TestListWidget.c文件:
TestListWidget::TestListWidget(QWidget *parent) :
QListWidget(parent)
{
//setMouseTracking(true); //注释与否没有影响
//setDragEnabled(true); //注释与否没有影响
setAcceptDrops(true);
//setDropIndicatorShown(false); //注释与否没有影响
//setDefaultDropAction(Qt::MoveAction); //注释与否没有影响
//[1]使用线程是为了配合定时器,使定时器更加精确
MyTimer *scrollBarTimer = new MyTimer();
//scrollBarTimer->setInterval(10);
QThread *timerThread = new QThread(this);
scrollBarTimer->moveToThread(timerThread);
connect(this, &TestListWidget::beginDrag, scrollBarTimer, &MyTimer::beginTimer);
connect(this, &TestListWidget::endDrag, scrollBarTimer, &MyTimer::stopTimer);
connect(this, &TestListWidget::Interval, scrollBarTimer, &MyTimer::getInterval);
connect(scrollBarTimer, &MyTimer::timeout, this, &TestListWidget::doAutoScroll);
connect(timerThread, &QThread::finished, scrollBarTimer, &MyTimer::deleteLater);
timerThread->start();
//[1]
}
//拖拽起点
void TestListWidget::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton){
startPos = event->pos();
}
}
void TestListWidget::mouseReleaseEvent(QMouseEvent *event)
{
if((event->pos() - startPos).manhattanLength() > 5) return;
TestListWidgetItem *item = static_cast<TestListWidgetItem *>(itemAt(event->pos()));
setCurrentItem(item);
}
void TestListWidget::mouseMoveEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton){
//[1]超过规定距离才会触发拖拽,防止手滑...
if((event->pos() - startPos).manhattanLength() < QApplication::startDragDistance()) return;
//[1]
TestListWidgetItem *theDragItem = static_cast<TestListWidgetItem *>(itemAt(startPos));
//setCurrentItem(theDragItem); //拖拽即选中
theDragRow = row(theDragItem);
theSelectedRow = row(currentItem());
//[2]这部分是把拖拽的数据放在QMimeData容器中(参考Qt例程puzzle,使用QByteArray和QDataStream感觉很方便)
QString text = theDragItem->text();
QIcon icon = theDragItem->Img;
QIcon icon_hover = theDragItem->Img_hover;
QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << text << icon << icon_hover;
QMimeData *mimeData = new QMimeData;
mimeData->setData(myMimeType(), itemData);
//[2]
//[3]设置拖拽时的缩略图,thumbnail类(找机会我会写一篇单独的文章介绍)是继承自QWidget的类椭圆形半透明窗口,使用grab()将QWidget变成QPixmap。
thumbnail *DragImage = new thumbnail(this);
DragImage->setupthumbnail(icon_hover, text);
//DragImage->setIconSize(18); //default:20
QPixmap pixmap = DragImage->grab();
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
//设置缩略图
drag->setPixmap(pixmap);
//设置鼠标在缩略图上的位置
drag->setHotSpot(QPoint(pixmap.width()/2, pixmap.height()/2));
//[3]
//拖拽开始!
if(drag->exec(Qt::MoveAction) == Qt::MoveAction){
if(theSelectedRow != theDragRow) delete takeItem(row(theDragItem));
}
}
}
void TestListWidget::dragEnterEvent(QDragEnterEvent *event)
{
TestListWidget *source = qobject_cast<TestListWidget *>(event->source());
if (source && source == this) {
//IsDraging(标志位)判断是否正在拖拽
IsDraging = true;
emit beginDrag(); //定时器开始(检测鼠标位置到listWidget上下边缘的距离)
event->setDropAction(Qt::MoveAction);
event->accept();
}
}
//当拖拽离开QListWidget时,需要update以保证DropIndicator消失
void TestListWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
emit endDrag(); //定时器停止
theHighlightedRow = -2;
if(!theHighlightedRect.contains(QRect(0, 40 * theDragRow, width(), 40))){
update(theHighlightedRect);
}
//IsDraging(标志位)判断是否正在拖拽
IsDraging = false;
theInsertRow = -1;
event->accept();
}
//拖拽移动时刷新以更新DropIndicator
void TestListWidget::dragMoveEvent(QDragMoveEvent *event)
{
TestListWidget *source = qobject_cast<TestListWidget *>(event->source());
if (source && source == this) {
//[1]控制QscrollBar滚动速度。鼠标离边缘越近,速度越快
if(timeCount >= timeLimit){
int posYToParent = event->pos().y(); //timer interval control
if(posYToParent >= height() - scrollMargin){
emit Interval((height() - posYToParent + 5) / 3);
}else if(posYToParent <= scrollMargin){
emit Interval((posYToParent + 5) / 3);
}
}
//[1]
oldHighlightedRect = theHighlightedRect;
theHighlightedRect = targetRect(event->pos());
//offset() = 19(这个数值是我调用父类的dropEvent(event)一次一次试出来的,我觉得公式应该是19 = 40 / 2 - 1, 其中40是item行高)
if(event-o>pos().y() >= offset()){
theHighlightedRow = row(itemAt(event->pos() - QPoint(0, offset())));
if(oldHighlightedRect != theHighlightedRect){
update(oldHighlightedRect); //刷新旧区域使DropIndicator消失
update(theHighlightedRect); //刷新新区域使DropIndicator显示
}else
update(theHighlightedRect);
theInsertRow = row(itemAt(event->pos() - QPoint(0, offset()))) + 1;
}else{
theHighlightedRow = -1;
update(QRect(0, 0, width(), 80)); //仅刷新第一行
theInsertRow = 0;
}
event->setDropAction(Qt::MoveAction);
event->accept();
}
}
void TestListWidget::dropEvent(QDropEvent *event)
{
TestListWidget *source = qobject_cast<TestListWidget *>(event->source());
if (source && source == this){
IsDraging = false; //拖拽完成
emit endDrag(); //定时器停止
theHighlightedRow = -2;
update(theHighlightedRect); //拖拽完成,刷新以使DropIndicator消失
//这种情况下,拖拽后的顺序和之前的顺序相同,所以无需触发拖拽
if(theInsertRow == theDragRow || theInsertRow == theDragRow + 1) return;
//如果拖拽行即选中行,则可以直接调用父类dropEvent(event)
if(theSelectedRow == theDragRow){
QListWidget::dropEvent(event);
return;
}
//如果拖拽行非选中行,则需要手动插入
QString text;
QIcon icon, icon_hover;
QByteArray itemData = event->mimeData()->data(myMimeType());
QDataStream dataStream(&itemData, QIODevice::ReadOnly);
dataStream >> text >> icon >> icon_hover;
TestListWidgetItem *theInsertItem = new TestListWidgetItem(/*this*/); //注意这里不能指定parent
theInsertItem->setUpIcon(icon, icon_hover);
theInsertItem->setText(text);
insertItem(theInsertRow, theInsertItem);
event->setDropAction(Qt::MoveAction);
event->accept();
}
}
//响应子线程中QTimer的timeout()信号
void TestListWidget::doAutoScroll()
{
QPoint pos = mapFromGlobal(QCursor::pos()); //获取鼠标位置
int posYToParent = pos.y();
int step = 1;
//[1]当timeCount > timeLimit时才会触发autoScroll
if(posYToParent >= height() - scrollMargin && scrollBarValue < scrollBarRange){
if(timeCount >= timeLimit){
emit exceptedValue(scrollBarValue + step); //控制QScrollBar
if(pos.y() + scrollBarValue >= offset()){
theHighlightedRow = row(itemAt(pos - QPoint(0, offset())));
oldHighlightedRect = theHighlightedRect;
theHighlightedRect = targetRect(pos);
if(oldHighlightedRect != theHighlightedRect){
update(oldHighlightedRect);
update(theHighlightedRect);
}
theInsertRow = row(itemAt(pos - QPoint(0, offset()))) + 1;
}
}else{
timeCount++;
}
}else if(posYToParent <= scrollMargin && scrollBarValue > 0){
if(timeCount >= timeLimit){
emit exceptedValue(scrollBarValue - step); //控制QScrollBar
if(pos.y() + scrollBarValue >= offset()){
theHighlightedRow = row(itemAt(pos - QPoint(0, offset())));
oldHighlightedRect = theHighlightedRect;
theHighlightedRect = targetRect(pos);
if(oldHighlightedRect != theHighlightedRect){
update(oldHighlightedRect);
update(theHighlightedRect);
}
theInsertRow = row(itemAt(pos - QPoint(0, offset()))) + 1;
}else if(pos.y() + scrollBarValue >= 0){
theHighlightedRow = -1;
update(QRect(0, 0, width(), 80));
theInsertRow = 0;
}else{
theHighlightedRow = -2;
update(QRect(0, 0, width(), 40));
theInsertRow = -1;
}
}else{
timeCount++;
}
}else{
timeCount = 0;
}
}
const QRect TestListWidget::targetRect(const QPoint &position) const
{
//40是item的行高
if(position.y() >= offset())
return QRect(0, (position.y() - offset()) / 40 * 40, width(), 2 * 40);
else
return QRect(0, 0, width(), 40);
}
(2)TestItemDelegate类继承自QStyledItemDelegate,主要是为了绘制dropIndicator。图示为dropIndicator组成:
- TestItemDelegate.h文件:
#define POLYGON 4 //等腰三角形直角边长
#define WIDTH 1 //分隔符粗细的一半
class TestItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
TestItemDelegate(QObject *parent = nullptr);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
- TestItemDelegate.c文件:
void TestItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
TestListWidget *dragWidget = qobject_cast<TestListWidget *>(option.styleObject);
bool isDraging = dragWidget->isDraging();
QRect rect = option.rect;
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);
if(option.state & (QStyle::State_MouseOver | QStyle::State_Selected)){
TestListWidgetItem *item = static_cast<TestListWidgetItem *>(dragWidget->item(index.row()));
item->setIcon(item->Img_hover);
if(option.state & QStyle::State_MouseOver){
}
if(option.state & QStyle::State_Selected){
painter->setBrush(QColor(180, 0, 0));
painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), 4, rect.height());
painter->setBrush(QColor(230, 231, 234));
painter->drawRect(rect.topLeft().x() + 4, rect.topLeft().y(), rect.width() - 4, rect.height());
}
}else{
TestListWidgetItem *item = static_cast<TestListWidgetItem *>(dragWidget->item(index.row()));
item->setIcon(item->Img);
}
//begin drag
if(isDraging){
int theDragRow = dragWidget->dragRow();
int theSelectedRow = dragWidget->selectedRow();
int UpRow = dragWidget->highlightedRow();
int DownRow = UpRow + 1;
int rowCount = dragWidget->count() - 1;
//只绘制空隙,而不绘制DropIndicator(这种情况是当拖拽行非选中行时,需要在选中行上面或者下面绘制空隙用来显示DropIndicator)
if(index.row() == theSelectedRow && theDragRow != theSelectedRow){
if(index.row() == UpRow && index.row() != theDragRow - 1){
int offset = 3; //这个值可以自己定,设为3感觉更美观
QPolygon trianglePolygon_bottomLeft;
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (offset + WIDTH) + 1);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (offset + WIDTH + POLYGON) + 1);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x() + POLYGON, rect.bottomLeft().y() - (offset + WIDTH) + 1);
QPolygon trianglePolygon_bottomRight;
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (offset + WIDTH) + 1);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (offset + WIDTH + POLYGON) + 1);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() - POLYGON + 1, rect.bottomRight().y() - (offset + WIDTH) + 1);
painter->setBrush(QColor(245, 245, 247));
painter->drawPolygon(trianglePolygon_bottomLeft);
painter->drawPolygon(trianglePolygon_bottomRight);
painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - (offset + WIDTH) + 1, rect.width(), offset + WIDTH);
painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - (offset + WIDTH) + 1, rect.width(), offset + WIDTH);
}else if(index.row() == DownRow && index.row() != theDragRow + 1){
int offset = 3;
QPolygon trianglePolygon_topLeft;
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + offset + WIDTH);
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + offset + WIDTH + POLYGON);
trianglePolygon_topLeft << QPoint(rect.topLeft().x() + POLYGON, rect.topLeft().y() + offset + WIDTH);
QPolygon trianglePolygon_topRight;
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + offset + WIDTH);
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + offset + WIDTH + POLYGON);
trianglePolygon_topRight << QPoint(rect.topRight().x() - POLYGON + 1, rect.topRight().y() + offset + WIDTH);
painter->setBrush(QColor(245, 245, 247));
painter->drawPolygon(trianglePolygon_topLeft);
painter->drawPolygon(trianglePolygon_topRight);
painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), rect.width(), offset + WIDTH);
}
}
//绘制DropIndicator
if(index.row() == UpRow && index.row() != theDragRow - 1 && index.row() != theDragRow){
painter->setBrush(QColor(66, 133, 244));
if(UpRow == rowCount){
//到达尾部,三角形向上移动一个WIDTH的距离,以使分隔符宽度*2
QPolygon trianglePolygon_bottomLeft;
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (POLYGON + WIDTH) + 1 - WIDTH);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1 - WIDTH);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x() + POLYGON, rect.bottomLeft().y() - WIDTH + 1 - WIDTH);
QPolygon trianglePolygon_bottomRight;
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (POLYGON + WIDTH) + 1 - WIDTH);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - WIDTH + 1 - WIDTH);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() - POLYGON + 1, rect.bottomRight().y() - WIDTH + 1 - WIDTH);
painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - 2 * WIDTH + 1, rect.width(), 2 * WIDTH); //rect
painter->drawPolygon(trianglePolygon_bottomLeft);
painter->drawPolygon(trianglePolygon_bottomRight);
}
else {
//正常情况,组成上半部分(+1是根据实际情况修正)
QPolygon trianglePolygon_bottomLeft;
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - (POLYGON + WIDTH) + 1);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1);
trianglePolygon_bottomLeft << QPoint(rect.bottomLeft().x() + POLYGON, rect.bottomLeft().y() - WIDTH + 1);
QPolygon trianglePolygon_bottomRight;
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - (POLYGON + WIDTH) + 1);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() + 1, rect.bottomRight().y() - WIDTH + 1);
trianglePolygon_bottomRight << QPoint(rect.bottomRight().x() - POLYGON + 1, rect.bottomRight().y() - WIDTH + 1);
painter->drawRect(rect.bottomLeft().x(), rect.bottomLeft().y() - WIDTH + 1, rect.width(), WIDTH); //rect
painter->drawPolygon(trianglePolygon_bottomLeft);
painter->drawPolygon(trianglePolygon_bottomRight);
}
}
else if(index.row() == DownRow && index.row() != theDragRow + 1 && index.row() != theDragRow){
painter->setBrush(QColor(66, 133, 244));
if(DownRow == 0){
//到达头部,三角形向下移动一个WIDTH的距离,以使分隔符宽度*2
QPolygon trianglePolygon_topLeft;
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + (POLYGON + WIDTH) + WIDTH);
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + WIDTH + WIDTH);
trianglePolygon_topLeft << QPoint(rect.topLeft().x() + POLYGON, rect.topLeft().y() + WIDTH + WIDTH);
QPolygon trianglePolygon_topRight;
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + (POLYGON + WIDTH) + WIDTH);
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + WIDTH + WIDTH);
trianglePolygon_topRight << QPoint(rect.topRight().x() - POLYGON + 1, rect.topRight().y() + WIDTH + WIDTH);
painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), rect.width(), 2 * WIDTH); //rect
painter->drawPolygon(trianglePolygon_topLeft);
painter->drawPolygon(trianglePolygon_topRight);
}
else{
//正常情况,组成下半部分(+1是根据实际情况修正)
QPolygon trianglePolygon_topLeft;
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + (POLYGON + WIDTH));
trianglePolygon_topLeft << QPoint(rect.topLeft().x(), rect.topLeft().y() + WIDTH);
trianglePolygon_topLeft << QPoint(rect.topLeft().x() + POLYGON, rect.topLeft().y() + WIDTH);
QPolygon trianglePolygon_topRight;
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + (POLYGON + WIDTH));
trianglePolygon_topRight << QPoint(rect.topRight().x() + 1, rect.topRight().y() + WIDTH);
trianglePolygon_topRight << QPoint(rect.topRight().x() - POLYGON + 1, rect.topRight().y() + WIDTH);
painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), rect.width(), WIDTH); //rect
painter->drawPolygon(trianglePolygon_topLeft);
painter->drawPolygon(trianglePolygon_topRight);
}
}
//用于高亮拖拽行
if(index.row() == theDragRow && theDragRow != theSelectedRow){
painter->setBrush(QColor(180, 0, 0));
painter->drawRect(rect.topLeft().x(), rect.topLeft().y(), 4, rect.height());
painter->setBrush(QColor(230, 231, 234));
painter->drawRect(rect.topLeft().x() + 4, rect.topLeft().y(), rect.width() - 4, rect.height());
//opt.state is used to hightlight the font
QStyleOptionViewItem opt(option);
opt.state |= QStyle::State_Selected;
QStyledItemDelegate::paint(painter, opt, index);
return;
}
QStyledItemDelegate::paint(painter, option, index);
return;
}
//end drag
QStyledItemDelegate::paint(painter, option, index);
}
(3)使用TestListWidgetItem和TestItemDelegate
- 主窗口.h文件:
class test : public QWidget
{
Q_OBJECT
public:
explicit test(QWidget *parent = nullptr);
private:
void initUi();
};
- 主窗口.c文件:
test::test(QWidget *parent) : QWidget(parent)
{
initUi();
}
void test::initUi()
{
setFixedSize(250, 600);
TestListWidget *listwidget = new TestListWidget(this);
listwidget->setIconSize(QSize(25, 25));
listwidget->setFocusPolicy(Qt::NoFocus); //这样可禁用tab键和上下方向键并且除去复选框
listwidget->setFixedHeight(320);
listwidget->setFont(QFont("宋体", 10, QFont::DemiBold));
listwidget->setStyleSheet(
//"*{outline:0px;}" //除去复选框
"QListWidget{background:rgb(245, 245, 247); border:0px; margin:0px 0px 0px 0px;}"
"QListWidget::Item{height:40px; border:0px; padding-left:14px; color:rgba(200, 40, 40, 255);}"
"QListWidget::Item:hover{color:rgba(40, 40, 200, 255); padding-left:14px;}"
"QListWidget::Item:selected{color:rgba(40, 40, 200, 255); padding-left:15px;}"
);
TestItemDelegate *delegate = new TestItemDelegate();
listwidget->setItemDelegate(delegate);
QScrollBar *scrollBar = new QScrollBar();
scrollBar->setStyleSheet(
"QScrollBar{background:transparent; width:10px; margin:0px 0px 0px 0px;}"
"QScrollBar::handle{background:rgba(223, 223, 225, 200); border:0px; border-radius:5px; margin:0px 0px 0px 0px;}"
"QScrollBar::handle:hover{background:lightgray;}"
"QScrollBar::handle:pressed{background:rgba(200, 200, 200, 255);}"
"QScrollBar::sub-page{background:transparent;}"
"QScrollBar::add-page{background:transparent;}"
"QScrollBar::up-arrow{background:transparent;}"
"QScrollBar::down-arrow{background:transparent;}"
"QScrollBar::sub-line{background:transparent;}"
"QScrollBar::add-line{background:transparent;}"
);
connect(scrollBar, &QScrollBar::valueChanged, listwidget, &TestListWidget::getScrollBarValue);
connect(scrollBar, &QScrollBar::rangeChanged, listwidget, &TestListWidget::getscrollBarRange);
connect(listwidget, &TestListWidget::exceptedValue, scrollBar, &QScrollBar::setValue);
listwidget->setVerticalScrollBar(scrollBar);
listwidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
QString item1 = "深海鱼的眼泪";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item1));
QString item2 = "彼岸花的悲殇";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item2));
QString item3 = "向日葵的绚烂";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item3));
QString item4 = "维也纳的艺术";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item4));
QString item5 = "谁是谁的谁";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item5));
QString item6 = "梦里悲几许";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item6));
QString item7 = "回忆是道殇";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item7));
QString item8 = "繁华沦为泪";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item8));
QString item9 = "倾似谁的城";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item9));
QString item10 = "你若在心上";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item10));
QString item11 = "等你几年又何妨";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item11));
QString item12 = "典型的音乐范r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item12));
QString item13 = "专业的艺术范r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item13));
QString item14 = "美腻的国际范r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item14));
QString item15 = "天真的小腐女r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item15));
QString item16 = "职业的卖萌娃r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item16));
QString item17 = "邪恶的两面范r";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item17));
QString item18 = "暗涌旳黑夜";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item18));
QString item19 = "我背着琴去远方";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item19));
QString item20 = "眼泪落在琴弦上";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item20));
QString item21 = "画一方禁地";
listwidget->addItem(new TestListWidgetItem(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"), item21));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->addWidget(listwidget);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
}
如果想要接触更多关于拖拽的代码,在Qt例程中搜索“drag”。推荐看一下例程puzzle的两种实现方法(一种是继承QListWidget,另一种是QListView + 继承QAbstractListModel)。
环境配置 :MinGW + QT 5.12 |