请教大佬, 如何在自定义QListWidget上面拖拽Button按钮?



  • from PyQt5.QtCore import Qt, QSize, QRect, QPoint
    from PyQt5.QtGui import QColor, QPixmap, QDrag, QPainter, QCursor
    from PyQt5.QtWidgets import QListWidget, QListWidgetItem, QLabel, QRubberBand, QToolButton
    
    
    class DragDropListWidget(QListWidget):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.resize(340, 340)
            # 隐藏横向/纵向滚动条
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            # 不能编辑
            self.setEditTriggers(self.NoEditTriggers)
            # 开启拖功能
            self.setDragEnabled(True)
            # 只能在内部拖放
            self.setDragDropMode(self.InternalMove)
            # 忽略放
            # self.setDefaultDropAction(Qt.MoveAction)
            # ****重要的一句(作用是可以单选,多选。Ctrl、Shift多选,可从空白位置框选)****
            # ****不能用ExtendedSelection,因为它可以在选中item后继续框选会和拖拽冲突****
            # self.setSelectionMode(self.ContiguousSelection)
            self.setSelectionMode(self.ExtendedSelection)
    
            # 设置从左到右、自动换行、依次排列
            self.setFlow(self.LeftToRight)
            self.setWrapping(True)
            self.setResizeMode(self.Adjust)
            # item的间隔
            self.setSpacing(2)
            # 橡皮筋(用于框选效果)
            self._rubberPos = None
            self._rubberBand = QRubberBand(QRubberBand.Rectangle, self)
    
            self.initItems()
    
        # 实现拖拽的时候预览效果图
        # 这里演示拼接所有的item截图(也可以自己写算法实现堆叠效果)
        def startDrag(self, supportedActions):
            items = self.selectedItems()
            drag = QDrag(self)
            mimeData = self.mimeData(items)
            # 由于QMimeData只能设置image、urls、str、bytes等等不方便
            # 这里添加一个额外的属性直接把item放进去,后面可以根据item取出数据
            mimeData.setProperty('myItems', items)
            drag.setMimeData(mimeData)
            pixmap = QPixmap(self.viewport().visibleRegion().boundingRect().size())
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            for item in items:
                rect = self.visualRect(self.indexFromItem(item))
                painter.drawPixmap(rect, self.viewport().grab(rect))
            painter.end()
            drag.setPixmap(pixmap)
            drag.setHotSpot(self.viewport().mapFromGlobal(QCursor.pos()))
            drag.exec_(supportedActions)
    
        def mousePressEvent(self, event):
            # 列表框点击事件,用于设置框选工具的开始位置
            super().mousePressEvent(event)
            if event.buttons() != Qt.LeftButton or self.itemAt(event.pos()):
                return
            self._rubberPos = event.pos()
            self._rubberBand.setGeometry(QRect(self._rubberPos, QSize()))
            self._rubberBand.show()
    
        def mouseReleaseEvent(self, event):
            # 列表框点击释放事件,用于隐藏框选工具
            super().mouseReleaseEvent(event)
            self._rubberPos = None
            self._rubberBand.hide()
    
        def mouseMoveEvent(self, event):
            # 列表框鼠标移动事件,用于设置框选工具的矩形范围
            super().mouseMoveEvent(event)
            if self._rubberPos:
                pos = event.pos()
                lx, ly = self._rubberPos.x(), self._rubberPos.y()
                rx, ry = pos.x(), pos.y()
                size = QSize(abs(rx - lx), abs(ry - ly))
                self._rubberBand.setGeometry(
                    QRect(QPoint(min(lx, rx), min(ly, ry)), size))
    
        def makeItem(self, size, cname):
            item = QListWidgetItem(self)
            item.setData(Qt.UserRole + 1, cname)  # 把颜色放进自定义的data里面
            item.setSizeHint(size)
            label = QLabel(self)  # 自定义控件
            label.setMargin(2)  # 往内缩进2
            label.resize(size)
            pixmap = QPixmap(size.scaled(80, 80, Qt.IgnoreAspectRatio))  # 调整尺寸
            pixmap.fill(QColor(cname))
            label.setPixmap(pixmap)
            self.setItemWidget(item, label)
    
        def initItems(self):
            size = QSize(80, 80)
    
            toolButton = QToolButton(self)
            toolButton.setObjectName("toolButton")
            toolButton.setText("drag\nbutton")
            item = QListWidgetItem(self)
            item.setSizeHint(size)
            self.setItemWidget(item, toolButton)
    
            for cname in QColor.colorNames():
                self.makeItem(size, cname)
    
    
    if __name__ == '__main__':
        import sys
        from PyQt5.QtWidgets import QApplication
    
        app = QApplication(sys.argv)
        app.setStyleSheet("""QListWidget {
            outline: 0px;
            background-color: transparent;
        }
        QListWidget::item:selected {
            border-radius: 2px;
            border: 1px solid rgb(0, 170, 255);
        }
        QListWidget::item:selected:!active {
            border-radius: 2px;
            border: 1px solid transparent;
        }
        QListWidget::item:selected:active {
            border-radius: 2px;
            border: 1px solid rgb(0, 170, 255);
        }
        QListWidget::item:hover {
            border-radius: 2px;
            border: 1px solid rgb(0, 170, 255);
        }"""
                          )
        wa = DragDropListWidget()
        wa.show()
        sys.exit(app.exec_())
    

    替代文字



  • 这个可以自由拖动, 但是不能自动对齐, 不是我需要的效果


  • 网站研运

    @zhoujin7 这个是PySide的代码。的确有深度……
    还要配置环境才可以顺利地跑通你的代码呢。


Log in to reply
 

走马观花

最近的回复

  • 113.jpg
    1、什么是lambda表达式,什么是闭包?
    lambda表达式即lambda函数,也就是匿名函数。

    lambda表达式在C++中包含了
    []表示捕获
    ()是函数的参数,需要指定类型
    ->type是返回的类型,可以省略,如果编译器无法推出类型的话可以强制编写
    {}是函数体。

    lambda可以被声明为mutable的,作用是将捕获的内容进行改变。
    闭包是函数的定义以及定义函数时提供的环境,总称为闭包。lambda函数也是一种闭包。
    lambda本身是匿名函数,而捕获语句则是提供了定义函数时提供的环境。

    2、什么是右值引用?
    右值引用相对与左值引用而言的。左值即=运算符左边的变量,右值是=运算符右边的常量或变量。由此可以看出,
    右值引用指的是对常量或变量的引用。它的用途包含了移动语义和完美转发。
    移动语义就是弥补了C++历史在处理变量传递时丢失的一种语义。它和值传递、引用传递一样,是变量传递的方式之一。
    如果没有移动语义,为了将一个类的实例传递给另外一个实例,就需要额外地进行构造、赋值、销毁的操作。
    对于一些比较复杂的变量,的确是非常耗时并且消耗大的操作。(浪费指令时间、浪费内存)

    对于这样的函数返回:
    vector<string> str_split(const string& s) {
    vector<string> v;
    // ...
    return v; // v是左值,但优先移动,不支持移动时仍可复制。
    }

    标准要求先调用移动构造函数,如果不符合那么再调用拷贝构造函数。所以可以轻松地写出这种写法而不必担心效率问题。
    同时,现代编译器都会对返回值进行优化,成为RVO以及NRVO。所以不用太担心会多调用构造析构函数。

    对于完美转发,C++对于引用的转发有规则。传统的C++是无法对引用进行再引用的。但是现代的C++放宽了它的使用范围。
    只有右引用右值的时候,才会产生右引用。这也称为引用折叠。

    3、auto关键字的作用是什么?
    auto关键字为的是能够让编译器自动推导类型。自C++98之后,编译器对类型的推导变得越来越智能了。
    而我们在编写复杂代码的时候,冗长的类型不仅容易出错,有时也不容易人工推导出类型。
    因此auto可以简化我们的任务量,让类型的推导交给编译器完成。
    除了auto外,我们还可以使用decltype()来让编译器推导类型。

    read more
  • 我感觉比起《Physically Based Rendering Technique》,还是《Ray Tracing in a Weekend》更容易上手,因为慢慢地能够做出一个渲染效果,这个是有成就感的。🎓

    read more
  • G

    北京名律免费法律咨询,解决您的法律困扰,如果您眼下没有遇到法律上的问题,也可以留一位大律师的联系方式以备不时之需!ccfd91efc3a6b2ed0e79054d2248eed.jpg bf6015b383483f1fe83cdcfd130fc6b.jpg 2f347879129add1096bf3424edbe517.jpg

    read more

关注我们

微博
QQ群