Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Add reorder functionality to CustomListModel class using QStyledItemDelegate

Add reorder functionality to CustomListModel class using QStyledItemDelegate

Scheduled Pinned Locked Moved Unsolved Qt for Python
22 Posts 4 Posters 2.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    Geuse
    wrote on 23 Feb 2023, 17:13 last edited by Geuse
    #1

    I found this example: https://dftalk.jp/?p=16745

    I'm using Maya to develop my UI:s.
    It looks very much what I'm after, but I can't for the life of me figure out how to implement the reordering of the items.
    I've modified the code somewhat, but instead of putting the element in between two other in the list, it creates a new one at the bottom and only displays it with the name of the item.
    I can read the code and see why this is happening. The problem is I can't figure out how to code it like I want it to behave.
    I'm not getting the correct row value as I execute print(row) in the dropMimeData method.
    I can see in the mimeData method that I am only adding the data name field, no other data. So how can I get the rest of them?
    And I'm wondering if the approach is to copy the data and create a new item at the new position and then delete the old item, or is there a way to actually move the original item to its new location?
    I'm not sure if I need to modify the CustomListDelegate class's dropEvent or not.

    Any help, or pointers are greatly appreciated.

    import maya.OpenMayaUI as omui
    
    import os
    import sys
    
    from PySide2 import QtWidgets, QtGui, QtCore
    from shiboken2 import wrapInstance
    
    # スクリプトのフォルダ
    #CURRENT_PATH = os.path.dirname(__file__)
    CURRENT_PATH = os.path.normpath(r'D:\scripts\delegate_example')
    # オリジナルのRole
    DESCRIPTION_ROLE = QtCore.Qt.UserRole
    STATUS_ROLE = QtCore.Qt.UserRole + 1
    THUMB_ROLE = QtCore.Qt.UserRole + 2
    
    # Modelに入るデータ
    SAMPLE_DATA = [
        {"name":"Ben", "description":"Support", "status":"OK", "color":[217,204,0], "thumbnail":"ben.png"},
        {"name":"Chris", "description":"Modeling", "status":"NOT OK", "color":[127,197,195], "thumbnail":"chris.png"},
        {"name":"Daniel", "description":"Manager", "status":"OK", "color":[217,204,166], "thumbnail":"daniel.png"},
        {"name":"Natalie", "description":"Sales", "status":"NOT OK", "color":[237,111,112], "thumbnail":"natalie.png"},
        {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}
    ]
    
    
    class CustomListModel(QtCore.QAbstractListModel):
    
        def __init__(self, parent=None, data=None):
    
            super(CustomListModel, self).__init__(parent)
            self.__items = data
    
        def rowCount(self, parent=QtCore.QModelIndex()):
            return len(self.__items)
    
        def data(self, index, role=QtCore.Qt.DisplayRole):
    
            if not index.isValid():
                return None
    
            if not 0 <= index.row() < len(self.__items):
                return None
    
            if role == QtCore.Qt.DisplayRole:
                return self.__items[index.row()]["name"]
    
            elif role == DESCRIPTION_ROLE:
                return self.__items[index.row()]["description"]
    
            elif role == STATUS_ROLE:
                return self.__items[index.row()]["status"]
    
            elif role == THUMB_ROLE:
                return self.__items[index.row()]["thumbnail"]
    
            elif role == QtCore.Qt.BackgroundRole:
                color = self.__items[index.row()]["color"]
                return QtGui.QColor(color[0], color[1], color[2])
    
            else:
                return None
    
    
    
    
        def flags(self, index):
            defaultFlags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
            if index.isValid():
                return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
            else:
                return QtCore.Qt.ItemIsDropEnabled | defaultFlags
    
        def mimeTypes(self):
            return ['text/plain']
    
        def mimeData(self, indexes):
            mimeData = QtCore.QMimeData()
            encodedData = QtCore.QByteArray()
            stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.WriteOnly)
            for index in indexes:
                if index.isValid():
                    itemData = self.__items[index.row()]
                    stream.writeQString(itemData['name'])
            mimeData.setData('text/plain', encodedData)
            return mimeData
    
        def canDropMimeData(self, data, action, row, column, parent):
            if not data.hasText():
                return False
            if action == QtCore.Qt.IgnoreAction:
                return True
            if column > 0:
                return False
            if row != -1:
                return True
            if parent.isValid():
                return False
            return True
    
        def dropMimeData(self, data, action, row, column, parent):
            
            print(self.rowCount())
            print(row)
            
            if not self.canDropMimeData(data, action, row, column, parent):
                return False
            if action == QtCore.Qt.IgnoreAction:
                return True
            if row == -1:
                row = self.rowCount()
            if column > 0:
                return False
    
    
            
            encodedData = data.data('text/plain')
            stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.ReadOnly)
            newItems = []
            rows = 0
           
            
            while not stream.atEnd():
                text = stream.readQString()
                newItems.append(text)
                rows += 1
    
            self.beginInsertRows(QtCore.QModelIndex(), row, row + rows - 1)
            for text in newItems:
                print(text)
                self.__items.insert(row, {"name": text, "description": "", "status": "", "thumbnail": "", "color": [255, 255, 255]})
                row += 1
            self.endInsertRows()
    
            return True
    
    
    
    
    
    class CustomListDelegate(QtWidgets.QStyledItemDelegate):
    
        # Listの各アイテムの大きさ
        ITEM_SIZE_HINT = [300, 70]
    
        # 各アイテム内の表示に使う定数
        MARGIN = 20
        THUMB_AREA_WIDTH = 70
        THUMB_WIDTH = 60
        STATUS_AREA_WIDTH = 24
    
        # Painterが使うブラシやペンの定義
        BG_DEFAULT = QtGui.QBrush(QtGui.QColor(247,248,242))
        BG_SELECTED = QtGui.QBrush(QtGui.QColor(255,255,204))
        BORDER_DEFAULT = QtGui.QPen(QtGui.QColor(255,255,255), 0.5, QtCore.Qt.SolidLine)
        TEXT_BLACK_PEN = QtGui.QPen(QtGui.QColor(106,107,109), 0.5, QtCore.Qt.SolidLine)
        SHADOW_PEN = QtGui.QPen(QtGui.QColor(220,220,220, 100), 1, QtCore.Qt.SolidLine)
        FONT_H1 = QtGui.QFont("Corbel", 12, QtGui.QFont.Normal, True)
        FONT_H2 = QtGui.QFont("Corbel", 11, QtGui.QFont.Normal, True)
    
    
        def __init__(self, parent=None):
    
            super(CustomListDelegate, self).__init__(parent)
            self._pressed_index = None
            self._target_index = None
            self._target_rect = None
    
    
        def mousePressEvent(self, event, model, view):
            if event.button() == QtCore.Qt.LeftButton:
                self._pressed_index = view.indexAt(event.pos())
                if self._pressed_index.isValid():
                    pixmap = QtGui.QPixmap.grabWidget(view, self._pressed_index.rect())
                    drag = QtGui.QDrag(view)
                    mime_data = QtCore.QMimeData()
                    mime_data.setData("application/x-qabstractitemmodeldatalist", QByteArray())
                    drag.setMimeData(mime_data)
                    drag.setPixmap(pixmap)
                    drag.setHotSpot(event.pos() - self._pressed_index.rect().topLeft())
                    drag.exec_()
    
        def mouseMoveEvent(self, event, model, view):
            if self._pressed_index.isValid():
                if (event.pos() - self._pressed_index.rect().topLeft()).manhattanLength() > QtWidgets.QApplication.startDragDistance():
                    pixmap = QtGui.QPixmap.grabWidget(view, self._pressed_index.rect())
                    drag = QtGui.QDrag(view)
                    mime_data = QtCore.QMimeData()
                    mime_data.setData("application/x-qabstractitemmodeldatalist", QByteArray())
                    drag.setMimeData(mime_data)
                    drag.setPixmap(pixmap)
                    drag.setHotSpot(event.pos() - self._pressed_index.rect().topLeft())
                    drag.exec_()
    
    
        def dragEnterEvent(self, event, model, view):
            if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
                event.accept()
                self._target_index = view.indexAt(event.pos())
                self._target_rect = view.visualRect(self._target_index)
                return
            
            event.ignore()
        
        def dragMoveEvent(self, event, model, view):
            if self._target_index.isValid():
                self._target_rect.setHeight(view.rowHeight(self._target_index))
                if self._target_rect.contains(event.pos()):
                    event.setDropAction(QtCore.Qt.MoveAction)
                    event.accept()
                    return
                
            event.ignore()
    
        def dropEvent(self, event, model, view):
    
            if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist") and self._target_index.isValid():
                target_row = self._target_index.row()
                source_row = self._pressed_index.row()
                if target_row > source_row:
                    target_row -= 1
    
                item_data = model._CustomListModel__items.pop(source_row)
                model.beginInsertRows(QtCore.QModelIndex(), target_row, target_row)
                model._CustomListModel__items.insert(target_row, item_data)
                model.endInsertRows()
                self._pressed_index = None
                self._target_index = None
                event.accept()
                return
            
            event.ignore()
    
    
    
    
    
    
    
    
    
        # 背景描画メソッド paintメソッド内で呼ばれている
        def drawBackground(self, painter, rect, selected, color):
    
            if selected:
                baseColor = self.BG_SELECTED
    
            else:
                baseColor = self.BG_DEFAULT
    
            painter.setPen(self.BORDER_DEFAULT)
            painter.setBrush(baseColor)
            painter.drawRoundedRect(rect, 1, 1)
    
            painter.setPen(self.SHADOW_PEN)
            painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y()+2, rect.bottomRight().x(), rect.bottomRight().y()+2)
    
            r = QtCore.QRect(rect.left(),
                             rect.top(),
                             self.THUMB_AREA_WIDTH,
                             rect.height())
    
            painter.setPen(QtCore.Qt.NoPen)
            painter.setBrush(QtGui.QBrush(color))
            painter.drawRoundedRect(r, 1, 1)
    
    
        # サムネイル描画メソッド paintメソッド内で呼ばれている
        def drawThumbnail(self, painter, rect, thumbnail):
    
            r = QtCore.QRect(rect.left() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                             rect.top() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                             self.THUMB_WIDTH,
                             self.THUMB_WIDTH)
            thumbImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", thumbnail)).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH)
            painter.drawPixmap(r, thumbImage)
    
    
        # 名前テキスト描画メソッド paintメソッド内で呼ばれている
        def drawName(self, painter, rect, name):
    
            painter.setFont(self.FONT_H1)
            painter.setPen(self.TEXT_BLACK_PEN)
            r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                             rect.top(),
                             rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                             rect.height() / 2)
            painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, "Name : " + name)
            painter.setPen(self.TEXT_BLACK_PEN)
            painter.drawLine(r.bottomLeft(), r.bottomRight())
    
    
        # ディスクリプションテキスト描画メソッド paintメソッド内で呼ばれている
        def drawDescription(self, painter, rect, description):
    
            painter.setFont(self.FONT_H2)
            painter.setPen(self.TEXT_BLACK_PEN)
            r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                             rect.top() + rect.height() / 2,
                             rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                             rect.height() / 2)
            painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, description)
    
    
        # ステータス画像描画メソッド paintメソッド内で呼ばれている
        def drawStatus(self, painter, rect, status):
            painter.setFont(self.FONT_H2)
            painter.setPen(self.TEXT_BLACK_PEN)
            r = QtCore.QRect(rect.right() - self.STATUS_AREA_WIDTH - self.MARGIN,
                             rect.center().y() - self.STATUS_AREA_WIDTH / 2,
                             self.STATUS_AREA_WIDTH,
                             self.STATUS_AREA_WIDTH)
            statusIcon = "notok.png"
    
            if status == "OK":
                statusIcon = "ok.png"
    
            statusImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", statusIcon))
            painter.drawPixmap(r, statusImage)
    
    
        # 描画メインメソッド
        def paint(self, painter, option, index):
    
            selected = False
    
            if option.state & QtWidgets.QStyle.State_Selected:
                selected = True
    
            name = index.data(QtCore.Qt.DisplayRole)
            description = index.data(DESCRIPTION_ROLE)
            status = index.data(STATUS_ROLE)
            color = index.data(QtCore.Qt.BackgroundRole)
            thumbnail = index.data(THUMB_ROLE)
    
            self.drawBackground(painter, option.rect, selected, color)
            self.drawThumbnail(painter, option.rect, thumbnail)
            self.drawName(painter, option.rect, name)
            self.drawDescription(painter, option.rect, description)
            self.drawStatus(painter, option.rect, status)
    
    
        # 各アイテムの大きさ
        def sizeHint(self, option, index):
    
            return QtCore.QSize(self.ITEM_SIZE_HINT[0], self.ITEM_SIZE_HINT[1])
    
    
    
    
            
            
            
    #----------------------------------------------------------------------------
    ## メインUI
    
    def maya_main_window():
        main_window_ptr = omui.MQtUtil.mainWindow()
        return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
        
    
    class GUI(QtWidgets.QMainWindow):
    
        def __init__(self, parent=maya_main_window()):
            super(GUI, self).__init__(parent)
    
            self.setWindowTitle('Main Window')
            self.resize(350, 600)
            self.setMaximumSize(525, 700)
            self.initUI()
    
    
        def initUI(self):
    
            # リストの設定
            myListView = QtWidgets.QListView(self)
            myListView.setSpacing(10)
            myListView.setAutoFillBackground(False)
            url = os.path.join(CURRENT_PATH, "images", "bg.png").replace("\\", "/")
            myListView.setStyleSheet("background: url("+url+") center center;")
            myListView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
                    
            # modelを作る
            myListModel = CustomListModel(data = SAMPLE_DATA)
    
            # delegateを作る
            myListDelegate = CustomListDelegate()
    
    
            # listview
            myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
            myListView.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
            myListView.setDefaultDropAction(QtCore.Qt.MoveAction)
            myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
            myListView.setDragEnabled(True)
            myListView.setAcceptDrops(True)
            myListView.setDropIndicatorShown(True)
            
    
            # modelとdelegateをリストにセット
            myListView.setItemDelegate(myListDelegate)
            myListView.setModel(myListModel)
    
            self.setCentralWidget(myListView)
    
    
    
    
    #
    if __name__ == "__main__":
        try:
            test_dialog.close()
            test_dialog.deleteLater()
        except:
            pass
    
        test_dialog = GUI()
        test_dialog.show()
    
    G 1 Reply Last reply 24 Feb 2023, 19:52
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on 23 Feb 2023, 20:39 last edited by
      #2

      Hi,

      Why are you implementing drag and drop in the delegate ? That's not its role and QListView already supports it.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      G 1 Reply Last reply 23 Feb 2023, 21:20
      1
      • SGaistS SGaist
        23 Feb 2023, 20:39

        Hi,

        Why are you implementing drag and drop in the delegate ? That's not its role and QListView already supports it.

        G Offline
        G Offline
        Geuse
        wrote on 23 Feb 2023, 21:20 last edited by Geuse
        #3

        @SGaist Thanks so much.
        And to answer your question "why?" Because I don't know any better... =/
        So I should remove all event methods from the CustomListDelegate class?

        Tried that and the UI works the same... :-) So it should just handle the paint methods then, correct?
        So I suppose my only problem is to modify the dropMimeData then in CustomListModel.

        But how do I do that. As I wrote, I believe I need a way to get the row of where I'm dragging from to where I'm going.
        And is there a way I can move an item or should I create a new one and copy the contents of the old one?

        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on 23 Feb 2023, 22:00 last edited by
          #4

          Yes, remove all that stuff from the delegate.

          There's a dedicated section about drag and drop in the model view programming chapter in Qt's documentation.

          The chapter explains all the concepts regarding Qt's model view implementation.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          G 1 Reply Last reply 23 Feb 2023, 22:24
          1
          • SGaistS SGaist
            23 Feb 2023, 22:00

            Yes, remove all that stuff from the delegate.

            There's a dedicated section about drag and drop in the model view programming chapter in Qt's documentation.

            The chapter explains all the concepts regarding Qt's model view implementation.

            G Offline
            G Offline
            Geuse
            wrote on 23 Feb 2023, 22:24 last edited by
            #5

            @SGaist Thanks,
            I will look into that.

            1 Reply Last reply
            0
            • G Geuse
              23 Feb 2023, 17:13

              I found this example: https://dftalk.jp/?p=16745

              I'm using Maya to develop my UI:s.
              It looks very much what I'm after, but I can't for the life of me figure out how to implement the reordering of the items.
              I've modified the code somewhat, but instead of putting the element in between two other in the list, it creates a new one at the bottom and only displays it with the name of the item.
              I can read the code and see why this is happening. The problem is I can't figure out how to code it like I want it to behave.
              I'm not getting the correct row value as I execute print(row) in the dropMimeData method.
              I can see in the mimeData method that I am only adding the data name field, no other data. So how can I get the rest of them?
              And I'm wondering if the approach is to copy the data and create a new item at the new position and then delete the old item, or is there a way to actually move the original item to its new location?
              I'm not sure if I need to modify the CustomListDelegate class's dropEvent or not.

              Any help, or pointers are greatly appreciated.

              import maya.OpenMayaUI as omui
              
              import os
              import sys
              
              from PySide2 import QtWidgets, QtGui, QtCore
              from shiboken2 import wrapInstance
              
              # スクリプトのフォルダ
              #CURRENT_PATH = os.path.dirname(__file__)
              CURRENT_PATH = os.path.normpath(r'D:\scripts\delegate_example')
              # オリジナルのRole
              DESCRIPTION_ROLE = QtCore.Qt.UserRole
              STATUS_ROLE = QtCore.Qt.UserRole + 1
              THUMB_ROLE = QtCore.Qt.UserRole + 2
              
              # Modelに入るデータ
              SAMPLE_DATA = [
                  {"name":"Ben", "description":"Support", "status":"OK", "color":[217,204,0], "thumbnail":"ben.png"},
                  {"name":"Chris", "description":"Modeling", "status":"NOT OK", "color":[127,197,195], "thumbnail":"chris.png"},
                  {"name":"Daniel", "description":"Manager", "status":"OK", "color":[217,204,166], "thumbnail":"daniel.png"},
                  {"name":"Natalie", "description":"Sales", "status":"NOT OK", "color":[237,111,112], "thumbnail":"natalie.png"},
                  {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}
              ]
              
              
              class CustomListModel(QtCore.QAbstractListModel):
              
                  def __init__(self, parent=None, data=None):
              
                      super(CustomListModel, self).__init__(parent)
                      self.__items = data
              
                  def rowCount(self, parent=QtCore.QModelIndex()):
                      return len(self.__items)
              
                  def data(self, index, role=QtCore.Qt.DisplayRole):
              
                      if not index.isValid():
                          return None
              
                      if not 0 <= index.row() < len(self.__items):
                          return None
              
                      if role == QtCore.Qt.DisplayRole:
                          return self.__items[index.row()]["name"]
              
                      elif role == DESCRIPTION_ROLE:
                          return self.__items[index.row()]["description"]
              
                      elif role == STATUS_ROLE:
                          return self.__items[index.row()]["status"]
              
                      elif role == THUMB_ROLE:
                          return self.__items[index.row()]["thumbnail"]
              
                      elif role == QtCore.Qt.BackgroundRole:
                          color = self.__items[index.row()]["color"]
                          return QtGui.QColor(color[0], color[1], color[2])
              
                      else:
                          return None
              
              
              
              
                  def flags(self, index):
                      defaultFlags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
                      if index.isValid():
                          return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
                      else:
                          return QtCore.Qt.ItemIsDropEnabled | defaultFlags
              
                  def mimeTypes(self):
                      return ['text/plain']
              
                  def mimeData(self, indexes):
                      mimeData = QtCore.QMimeData()
                      encodedData = QtCore.QByteArray()
                      stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.WriteOnly)
                      for index in indexes:
                          if index.isValid():
                              itemData = self.__items[index.row()]
                              stream.writeQString(itemData['name'])
                      mimeData.setData('text/plain', encodedData)
                      return mimeData
              
                  def canDropMimeData(self, data, action, row, column, parent):
                      if not data.hasText():
                          return False
                      if action == QtCore.Qt.IgnoreAction:
                          return True
                      if column > 0:
                          return False
                      if row != -1:
                          return True
                      if parent.isValid():
                          return False
                      return True
              
                  def dropMimeData(self, data, action, row, column, parent):
                      
                      print(self.rowCount())
                      print(row)
                      
                      if not self.canDropMimeData(data, action, row, column, parent):
                          return False
                      if action == QtCore.Qt.IgnoreAction:
                          return True
                      if row == -1:
                          row = self.rowCount()
                      if column > 0:
                          return False
              
              
                      
                      encodedData = data.data('text/plain')
                      stream = QtCore.QDataStream(encodedData, QtCore.QIODevice.ReadOnly)
                      newItems = []
                      rows = 0
                     
                      
                      while not stream.atEnd():
                          text = stream.readQString()
                          newItems.append(text)
                          rows += 1
              
                      self.beginInsertRows(QtCore.QModelIndex(), row, row + rows - 1)
                      for text in newItems:
                          print(text)
                          self.__items.insert(row, {"name": text, "description": "", "status": "", "thumbnail": "", "color": [255, 255, 255]})
                          row += 1
                      self.endInsertRows()
              
                      return True
              
              
              
              
              
              class CustomListDelegate(QtWidgets.QStyledItemDelegate):
              
                  # Listの各アイテムの大きさ
                  ITEM_SIZE_HINT = [300, 70]
              
                  # 各アイテム内の表示に使う定数
                  MARGIN = 20
                  THUMB_AREA_WIDTH = 70
                  THUMB_WIDTH = 60
                  STATUS_AREA_WIDTH = 24
              
                  # Painterが使うブラシやペンの定義
                  BG_DEFAULT = QtGui.QBrush(QtGui.QColor(247,248,242))
                  BG_SELECTED = QtGui.QBrush(QtGui.QColor(255,255,204))
                  BORDER_DEFAULT = QtGui.QPen(QtGui.QColor(255,255,255), 0.5, QtCore.Qt.SolidLine)
                  TEXT_BLACK_PEN = QtGui.QPen(QtGui.QColor(106,107,109), 0.5, QtCore.Qt.SolidLine)
                  SHADOW_PEN = QtGui.QPen(QtGui.QColor(220,220,220, 100), 1, QtCore.Qt.SolidLine)
                  FONT_H1 = QtGui.QFont("Corbel", 12, QtGui.QFont.Normal, True)
                  FONT_H2 = QtGui.QFont("Corbel", 11, QtGui.QFont.Normal, True)
              
              
                  def __init__(self, parent=None):
              
                      super(CustomListDelegate, self).__init__(parent)
                      self._pressed_index = None
                      self._target_index = None
                      self._target_rect = None
              
              
                  def mousePressEvent(self, event, model, view):
                      if event.button() == QtCore.Qt.LeftButton:
                          self._pressed_index = view.indexAt(event.pos())
                          if self._pressed_index.isValid():
                              pixmap = QtGui.QPixmap.grabWidget(view, self._pressed_index.rect())
                              drag = QtGui.QDrag(view)
                              mime_data = QtCore.QMimeData()
                              mime_data.setData("application/x-qabstractitemmodeldatalist", QByteArray())
                              drag.setMimeData(mime_data)
                              drag.setPixmap(pixmap)
                              drag.setHotSpot(event.pos() - self._pressed_index.rect().topLeft())
                              drag.exec_()
              
                  def mouseMoveEvent(self, event, model, view):
                      if self._pressed_index.isValid():
                          if (event.pos() - self._pressed_index.rect().topLeft()).manhattanLength() > QtWidgets.QApplication.startDragDistance():
                              pixmap = QtGui.QPixmap.grabWidget(view, self._pressed_index.rect())
                              drag = QtGui.QDrag(view)
                              mime_data = QtCore.QMimeData()
                              mime_data.setData("application/x-qabstractitemmodeldatalist", QByteArray())
                              drag.setMimeData(mime_data)
                              drag.setPixmap(pixmap)
                              drag.setHotSpot(event.pos() - self._pressed_index.rect().topLeft())
                              drag.exec_()
              
              
                  def dragEnterEvent(self, event, model, view):
                      if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist"):
                          event.accept()
                          self._target_index = view.indexAt(event.pos())
                          self._target_rect = view.visualRect(self._target_index)
                          return
                      
                      event.ignore()
                  
                  def dragMoveEvent(self, event, model, view):
                      if self._target_index.isValid():
                          self._target_rect.setHeight(view.rowHeight(self._target_index))
                          if self._target_rect.contains(event.pos()):
                              event.setDropAction(QtCore.Qt.MoveAction)
                              event.accept()
                              return
                          
                      event.ignore()
              
                  def dropEvent(self, event, model, view):
              
                      if event.mimeData().hasFormat("application/x-qabstractitemmodeldatalist") and self._target_index.isValid():
                          target_row = self._target_index.row()
                          source_row = self._pressed_index.row()
                          if target_row > source_row:
                              target_row -= 1
              
                          item_data = model._CustomListModel__items.pop(source_row)
                          model.beginInsertRows(QtCore.QModelIndex(), target_row, target_row)
                          model._CustomListModel__items.insert(target_row, item_data)
                          model.endInsertRows()
                          self._pressed_index = None
                          self._target_index = None
                          event.accept()
                          return
                      
                      event.ignore()
              
              
              
              
              
              
              
              
              
                  # 背景描画メソッド paintメソッド内で呼ばれている
                  def drawBackground(self, painter, rect, selected, color):
              
                      if selected:
                          baseColor = self.BG_SELECTED
              
                      else:
                          baseColor = self.BG_DEFAULT
              
                      painter.setPen(self.BORDER_DEFAULT)
                      painter.setBrush(baseColor)
                      painter.drawRoundedRect(rect, 1, 1)
              
                      painter.setPen(self.SHADOW_PEN)
                      painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y()+2, rect.bottomRight().x(), rect.bottomRight().y()+2)
              
                      r = QtCore.QRect(rect.left(),
                                       rect.top(),
                                       self.THUMB_AREA_WIDTH,
                                       rect.height())
              
                      painter.setPen(QtCore.Qt.NoPen)
                      painter.setBrush(QtGui.QBrush(color))
                      painter.drawRoundedRect(r, 1, 1)
              
              
                  # サムネイル描画メソッド paintメソッド内で呼ばれている
                  def drawThumbnail(self, painter, rect, thumbnail):
              
                      r = QtCore.QRect(rect.left() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                       rect.top() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                       self.THUMB_WIDTH,
                                       self.THUMB_WIDTH)
                      thumbImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", thumbnail)).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH)
                      painter.drawPixmap(r, thumbImage)
              
              
                  # 名前テキスト描画メソッド paintメソッド内で呼ばれている
                  def drawName(self, painter, rect, name):
              
                      painter.setFont(self.FONT_H1)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                       rect.top(),
                                       rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                       rect.height() / 2)
                      painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, "Name : " + name)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      painter.drawLine(r.bottomLeft(), r.bottomRight())
              
              
                  # ディスクリプションテキスト描画メソッド paintメソッド内で呼ばれている
                  def drawDescription(self, painter, rect, description):
              
                      painter.setFont(self.FONT_H2)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                       rect.top() + rect.height() / 2,
                                       rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                       rect.height() / 2)
                      painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, description)
              
              
                  # ステータス画像描画メソッド paintメソッド内で呼ばれている
                  def drawStatus(self, painter, rect, status):
                      painter.setFont(self.FONT_H2)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.right() - self.STATUS_AREA_WIDTH - self.MARGIN,
                                       rect.center().y() - self.STATUS_AREA_WIDTH / 2,
                                       self.STATUS_AREA_WIDTH,
                                       self.STATUS_AREA_WIDTH)
                      statusIcon = "notok.png"
              
                      if status == "OK":
                          statusIcon = "ok.png"
              
                      statusImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", statusIcon))
                      painter.drawPixmap(r, statusImage)
              
              
                  # 描画メインメソッド
                  def paint(self, painter, option, index):
              
                      selected = False
              
                      if option.state & QtWidgets.QStyle.State_Selected:
                          selected = True
              
                      name = index.data(QtCore.Qt.DisplayRole)
                      description = index.data(DESCRIPTION_ROLE)
                      status = index.data(STATUS_ROLE)
                      color = index.data(QtCore.Qt.BackgroundRole)
                      thumbnail = index.data(THUMB_ROLE)
              
                      self.drawBackground(painter, option.rect, selected, color)
                      self.drawThumbnail(painter, option.rect, thumbnail)
                      self.drawName(painter, option.rect, name)
                      self.drawDescription(painter, option.rect, description)
                      self.drawStatus(painter, option.rect, status)
              
              
                  # 各アイテムの大きさ
                  def sizeHint(self, option, index):
              
                      return QtCore.QSize(self.ITEM_SIZE_HINT[0], self.ITEM_SIZE_HINT[1])
              
              
              
              
                      
                      
                      
              #----------------------------------------------------------------------------
              ## メインUI
              
              def maya_main_window():
                  main_window_ptr = omui.MQtUtil.mainWindow()
                  return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
                  
              
              class GUI(QtWidgets.QMainWindow):
              
                  def __init__(self, parent=maya_main_window()):
                      super(GUI, self).__init__(parent)
              
                      self.setWindowTitle('Main Window')
                      self.resize(350, 600)
                      self.setMaximumSize(525, 700)
                      self.initUI()
              
              
                  def initUI(self):
              
                      # リストの設定
                      myListView = QtWidgets.QListView(self)
                      myListView.setSpacing(10)
                      myListView.setAutoFillBackground(False)
                      url = os.path.join(CURRENT_PATH, "images", "bg.png").replace("\\", "/")
                      myListView.setStyleSheet("background: url("+url+") center center;")
                      myListView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
                              
                      # modelを作る
                      myListModel = CustomListModel(data = SAMPLE_DATA)
              
                      # delegateを作る
                      myListDelegate = CustomListDelegate()
              
              
                      # listview
                      myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                      myListView.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
                      myListView.setDefaultDropAction(QtCore.Qt.MoveAction)
                      myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                      myListView.setDragEnabled(True)
                      myListView.setAcceptDrops(True)
                      myListView.setDropIndicatorShown(True)
                      
              
                      # modelとdelegateをリストにセット
                      myListView.setItemDelegate(myListDelegate)
                      myListView.setModel(myListModel)
              
                      self.setCentralWidget(myListView)
              
              
              
              
              #
              if __name__ == "__main__":
                  try:
                      test_dialog.close()
                      test_dialog.deleteLater()
                  except:
                      pass
              
                  test_dialog = GUI()
                  test_dialog.show()
              
              G Offline
              G Offline
              Geuse
              wrote on 24 Feb 2023, 19:52 last edited by
              #6

              I've read and tried to understand how to do, but I still fail. I've found three threads on stack overflow that touches on this, but I still can't sort it out.

              https://stackoverflow.com/questions/45448863/cant-seem-to-enable-drag-drop-in-qlistview

              https://stackoverflow.com/questions/56819085/qt-how-to-implement-simple-internal-dragdrop-for-reordering-items-in-qlistview

              https://stackoverflow.com/questions/73544611/pyside-qlistview-drag-and-drop

              From what I gather I need to implement four methods: data, setData, insertRows and removeRows.
              removeRows works well, but not insertRows. I'm trying to debug it and print out the values, but obviously I'm not getting it right. My thinking is that the values in the argument list should return the row of where to insert a new element. So where I drop it should tell me what row that is. I'm not sure exactly what I need to do here. it's all so confusing. =/

              import maya.OpenMayaUI as omui
              
              import os
              import sys
              
              from PySide2 import QtWidgets, QtGui, QtCore
              from shiboken2 import wrapInstance
              
              # スクリプトのフォルダ
              #CURRENT_PATH = os.path.dirname(__file__)
              CURRENT_PATH = os.path.normpath(r'D:\01_personal_files\20_MAYA_CUSTOM_CONFIG\10_maya_custom_config_2022\14_MODULES_DAG\16_dag_wip')
              # オリジナルのRole
              DESCRIPTION_ROLE = QtCore.Qt.UserRole
              STATUS_ROLE = QtCore.Qt.UserRole + 1
              THUMB_ROLE = QtCore.Qt.UserRole + 2
              
              # Modelに入るデータ
              SAMPLE_DATA = [
                  {"name":"Ben", "description":"Support", "status":"OK", "color":[217,204,0], "thumbnail":"ben.png"},
                  {"name":"Chris", "description":"Modeling", "status":"NOT OK", "color":[127,197,195], "thumbnail":"chris.png"},
                  {"name":"Daniel", "description":"Manager", "status":"OK", "color":[217,204,166], "thumbnail":"daniel.png"},
                  {"name":"Natalie", "description":"Sales", "status":"NOT OK", "color":[237,111,112], "thumbnail":"natalie.png"},
                  {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}
              ]
              
              
              class CustomListModel(QtCore.QAbstractListModel):
              
                  def __init__(self, parent=None, data=None):
              
                      super(CustomListModel, self).__init__(parent)
                      self.__items = data
              
                  
                  def rowCount(self, parent=QtCore.QModelIndex()):
                      return len(self.__items)
              
                  def data(self, index, role=QtCore.Qt.DisplayRole):
              
                      if not index.isValid():
                          return None
              
                      if not 0 <= index.row() < len(self.__items):
                          return None
              
                      if role == QtCore.Qt.DisplayRole:
                          return self.__items[index.row()]["name"]
              
                      elif role == DESCRIPTION_ROLE:
                          return self.__items[index.row()]["description"]
              
                      elif role == STATUS_ROLE:
                          return self.__items[index.row()]["status"]
              
                      elif role == THUMB_ROLE:
                          return self.__items[index.row()]["thumbnail"]
              
                      elif role == QtCore.Qt.BackgroundRole:
                          color = self.__items[index.row()]["color"]
                          return QtGui.QColor(color[0], color[1], color[2])
              
                      else:
                          return None
              
              
                  def removeRows(self, row, count, parent=QtCore.QModelIndex()):
                      self.beginRemoveRows(parent, row, row + count - 1)
                      del self.__items[row:row + count]
                      self.endRemoveRows()
                      print(f'remove {row}')
                      return True
              
                  def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                      self.beginInsertRows(parent, row, row + count - 1)
                      self.endInsertRows()
                      #print(f'number of items: {len(self.__items)}')
                      print(f'move {row} to {row + count - 1}')
                      return True
              
                  def supportedDropActions(self):
                      return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction;
              
              
                  def flags(self, index):
                      defaultFlags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
                      if index.isValid():
                          return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
                      else:
                          return QtCore.Qt.ItemIsDropEnabled | defaultFlags
              
              
              
              
              
              
              
              
              
              class CustomListDelegate(QtWidgets.QStyledItemDelegate):
              
                  # Listの各アイテムの大きさ
                  ITEM_SIZE_HINT = [300, 70]
              
                  # 各アイテム内の表示に使う定数
                  MARGIN = 20
                  THUMB_AREA_WIDTH = 70
                  THUMB_WIDTH = 60
                  STATUS_AREA_WIDTH = 24
              
                  # Painterが使うブラシやペンの定義
                  BG_DEFAULT = QtGui.QBrush(QtGui.QColor(247,248,242))
                  BG_SELECTED = QtGui.QBrush(QtGui.QColor(255,255,204))
                  BORDER_DEFAULT = QtGui.QPen(QtGui.QColor(255,255,255), 0.5, QtCore.Qt.SolidLine)
                  TEXT_BLACK_PEN = QtGui.QPen(QtGui.QColor(106,107,109), 0.5, QtCore.Qt.SolidLine)
                  SHADOW_PEN = QtGui.QPen(QtGui.QColor(220,220,220, 100), 1, QtCore.Qt.SolidLine)
                  FONT_H1 = QtGui.QFont("Corbel", 12, QtGui.QFont.Normal, True)
                  FONT_H2 = QtGui.QFont("Corbel", 11, QtGui.QFont.Normal, True)
              
              
                  def __init__(self, parent=None):
              
                      super(CustomListDelegate, self).__init__(parent)
              
              
              
                  # 背景描画メソッド paintメソッド内で呼ばれている
                  def drawBackground(self, painter, rect, selected, color):
              
                      if selected:
                          baseColor = self.BG_SELECTED
              
                      else:
                          baseColor = self.BG_DEFAULT
              
                      painter.setPen(self.BORDER_DEFAULT)
                      painter.setBrush(baseColor)
                      painter.drawRoundedRect(rect, 1, 1)
              
                      painter.setPen(self.SHADOW_PEN)
                      painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y()+2, rect.bottomRight().x(), rect.bottomRight().y()+2)
              
                      r = QtCore.QRect(rect.left(),
                                       rect.top(),
                                       self.THUMB_AREA_WIDTH,
                                       rect.height())
              
                      painter.setPen(QtCore.Qt.NoPen)
                      painter.setBrush(QtGui.QBrush(color))
                      painter.drawRoundedRect(r, 1, 1)
              
              
                  # サムネイル描画メソッド paintメソッド内で呼ばれている
                  def drawThumbnail(self, painter, rect, thumbnail):
              
                      r = QtCore.QRect(rect.left() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                       rect.top() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                       self.THUMB_WIDTH,
                                       self.THUMB_WIDTH)
                      thumbImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", thumbnail)).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH)
                      painter.drawPixmap(r, thumbImage)
              
              
                  # 名前テキスト描画メソッド paintメソッド内で呼ばれている
                  def drawName(self, painter, rect, name):
              
                      painter.setFont(self.FONT_H1)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                       rect.top(),
                                       rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                       rect.height() / 2)
                      painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, "Name : " + name)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      painter.drawLine(r.bottomLeft(), r.bottomRight())
              
              
                  # ディスクリプションテキスト描画メソッド paintメソッド内で呼ばれている
                  def drawDescription(self, painter, rect, description):
              
                      painter.setFont(self.FONT_H2)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                       rect.top() + rect.height() / 2,
                                       rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                       rect.height() / 2)
                      painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, description)
              
              
                  # ステータス画像描画メソッド paintメソッド内で呼ばれている
                  def drawStatus(self, painter, rect, status):
                      painter.setFont(self.FONT_H2)
                      painter.setPen(self.TEXT_BLACK_PEN)
                      r = QtCore.QRect(rect.right() - self.STATUS_AREA_WIDTH - self.MARGIN,
                                       rect.center().y() - self.STATUS_AREA_WIDTH / 2,
                                       self.STATUS_AREA_WIDTH,
                                       self.STATUS_AREA_WIDTH)
                      statusIcon = "notok.png"
              
                      if status == "OK":
                          statusIcon = "ok.png"
              
                      statusImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", statusIcon))
                      painter.drawPixmap(r, statusImage)
              
              
                  # 描画メインメソッド
                  def paint(self, painter, option, index):
              
                      selected = False
              
                      if option.state & QtWidgets.QStyle.State_Selected:
                          selected = True
              
                      name = index.data(QtCore.Qt.DisplayRole)
                      description = index.data(DESCRIPTION_ROLE)
                      status = index.data(STATUS_ROLE)
                      color = index.data(QtCore.Qt.BackgroundRole)
                      thumbnail = index.data(THUMB_ROLE)
              
                      self.drawBackground(painter, option.rect, selected, color)
                      self.drawThumbnail(painter, option.rect, thumbnail)
                      self.drawName(painter, option.rect, name)
                      self.drawDescription(painter, option.rect, description)
                      self.drawStatus(painter, option.rect, status)
              
              
                  # 各アイテムの大きさ
                  def sizeHint(self, option, index):
              
                      return QtCore.QSize(self.ITEM_SIZE_HINT[0], self.ITEM_SIZE_HINT[1])
              
              
              
              
                      
                      
                      
              #----------------------------------------------------------------------------
              ## メインUI
              
              def maya_main_window():
                  main_window_ptr = omui.MQtUtil.mainWindow()
                  return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
                  
              
              class GUI(QtWidgets.QMainWindow):
              
                  def __init__(self, parent=maya_main_window()):
                      super(GUI, self).__init__(parent)
              
                      self.setWindowTitle('Main Window')
                      self.resize(350, 600)
                      self.setMaximumSize(525, 700)
                      self.initUI()
              
              
                  def initUI(self):
              
                      # リストの設定
                      myListView = QtWidgets.QListView(self)
                      myListView.setSpacing(10)
                      myListView.setAutoFillBackground(False)
                      url = os.path.join(CURRENT_PATH, "images", "bg.png").replace("\\", "/")
                      myListView.setStyleSheet("background: url("+url+") center center;")
                      myListView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
                              
                      # modelを作る
                      myListModel = CustomListModel(data = SAMPLE_DATA)
              
                      # delegateを作る
                      myListDelegate = CustomListDelegate()
              
              
                      # listview
                      myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                      myListView.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
                      myListView.setDefaultDropAction(QtCore.Qt.MoveAction)
                      myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                      myListView.setDragEnabled(True)
                      myListView.setAcceptDrops(True)
                      myListView.setDropIndicatorShown(True)
                      
              
                      # modelとdelegateをリストにセット
                      myListView.setItemDelegate(myListDelegate)
                      myListView.setModel(myListModel)
              
                      self.setCentralWidget(myListView)
              
              
              
              
              #
              if __name__ == "__main__":
                  try:
                      test_dialog.close()
                      test_dialog.deleteLater()
                  except:
                      pass
              
                  test_dialog = GUI()
                  test_dialog.show()
              
              G 1 Reply Last reply 25 Feb 2023, 22:58
              0
              • G Geuse
                24 Feb 2023, 19:52

                I've read and tried to understand how to do, but I still fail. I've found three threads on stack overflow that touches on this, but I still can't sort it out.

                https://stackoverflow.com/questions/45448863/cant-seem-to-enable-drag-drop-in-qlistview

                https://stackoverflow.com/questions/56819085/qt-how-to-implement-simple-internal-dragdrop-for-reordering-items-in-qlistview

                https://stackoverflow.com/questions/73544611/pyside-qlistview-drag-and-drop

                From what I gather I need to implement four methods: data, setData, insertRows and removeRows.
                removeRows works well, but not insertRows. I'm trying to debug it and print out the values, but obviously I'm not getting it right. My thinking is that the values in the argument list should return the row of where to insert a new element. So where I drop it should tell me what row that is. I'm not sure exactly what I need to do here. it's all so confusing. =/

                import maya.OpenMayaUI as omui
                
                import os
                import sys
                
                from PySide2 import QtWidgets, QtGui, QtCore
                from shiboken2 import wrapInstance
                
                # スクリプトのフォルダ
                #CURRENT_PATH = os.path.dirname(__file__)
                CURRENT_PATH = os.path.normpath(r'D:\01_personal_files\20_MAYA_CUSTOM_CONFIG\10_maya_custom_config_2022\14_MODULES_DAG\16_dag_wip')
                # オリジナルのRole
                DESCRIPTION_ROLE = QtCore.Qt.UserRole
                STATUS_ROLE = QtCore.Qt.UserRole + 1
                THUMB_ROLE = QtCore.Qt.UserRole + 2
                
                # Modelに入るデータ
                SAMPLE_DATA = [
                    {"name":"Ben", "description":"Support", "status":"OK", "color":[217,204,0], "thumbnail":"ben.png"},
                    {"name":"Chris", "description":"Modeling", "status":"NOT OK", "color":[127,197,195], "thumbnail":"chris.png"},
                    {"name":"Daniel", "description":"Manager", "status":"OK", "color":[217,204,166], "thumbnail":"daniel.png"},
                    {"name":"Natalie", "description":"Sales", "status":"NOT OK", "color":[237,111,112], "thumbnail":"natalie.png"},
                    {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}
                ]
                
                
                class CustomListModel(QtCore.QAbstractListModel):
                
                    def __init__(self, parent=None, data=None):
                
                        super(CustomListModel, self).__init__(parent)
                        self.__items = data
                
                    
                    def rowCount(self, parent=QtCore.QModelIndex()):
                        return len(self.__items)
                
                    def data(self, index, role=QtCore.Qt.DisplayRole):
                
                        if not index.isValid():
                            return None
                
                        if not 0 <= index.row() < len(self.__items):
                            return None
                
                        if role == QtCore.Qt.DisplayRole:
                            return self.__items[index.row()]["name"]
                
                        elif role == DESCRIPTION_ROLE:
                            return self.__items[index.row()]["description"]
                
                        elif role == STATUS_ROLE:
                            return self.__items[index.row()]["status"]
                
                        elif role == THUMB_ROLE:
                            return self.__items[index.row()]["thumbnail"]
                
                        elif role == QtCore.Qt.BackgroundRole:
                            color = self.__items[index.row()]["color"]
                            return QtGui.QColor(color[0], color[1], color[2])
                
                        else:
                            return None
                
                
                    def removeRows(self, row, count, parent=QtCore.QModelIndex()):
                        self.beginRemoveRows(parent, row, row + count - 1)
                        del self.__items[row:row + count]
                        self.endRemoveRows()
                        print(f'remove {row}')
                        return True
                
                    def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                        self.beginInsertRows(parent, row, row + count - 1)
                        self.endInsertRows()
                        #print(f'number of items: {len(self.__items)}')
                        print(f'move {row} to {row + count - 1}')
                        return True
                
                    def supportedDropActions(self):
                        return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction;
                
                
                    def flags(self, index):
                        defaultFlags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
                        if index.isValid():
                            return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
                        else:
                            return QtCore.Qt.ItemIsDropEnabled | defaultFlags
                
                
                
                
                
                
                
                
                
                class CustomListDelegate(QtWidgets.QStyledItemDelegate):
                
                    # Listの各アイテムの大きさ
                    ITEM_SIZE_HINT = [300, 70]
                
                    # 各アイテム内の表示に使う定数
                    MARGIN = 20
                    THUMB_AREA_WIDTH = 70
                    THUMB_WIDTH = 60
                    STATUS_AREA_WIDTH = 24
                
                    # Painterが使うブラシやペンの定義
                    BG_DEFAULT = QtGui.QBrush(QtGui.QColor(247,248,242))
                    BG_SELECTED = QtGui.QBrush(QtGui.QColor(255,255,204))
                    BORDER_DEFAULT = QtGui.QPen(QtGui.QColor(255,255,255), 0.5, QtCore.Qt.SolidLine)
                    TEXT_BLACK_PEN = QtGui.QPen(QtGui.QColor(106,107,109), 0.5, QtCore.Qt.SolidLine)
                    SHADOW_PEN = QtGui.QPen(QtGui.QColor(220,220,220, 100), 1, QtCore.Qt.SolidLine)
                    FONT_H1 = QtGui.QFont("Corbel", 12, QtGui.QFont.Normal, True)
                    FONT_H2 = QtGui.QFont("Corbel", 11, QtGui.QFont.Normal, True)
                
                
                    def __init__(self, parent=None):
                
                        super(CustomListDelegate, self).__init__(parent)
                
                
                
                    # 背景描画メソッド paintメソッド内で呼ばれている
                    def drawBackground(self, painter, rect, selected, color):
                
                        if selected:
                            baseColor = self.BG_SELECTED
                
                        else:
                            baseColor = self.BG_DEFAULT
                
                        painter.setPen(self.BORDER_DEFAULT)
                        painter.setBrush(baseColor)
                        painter.drawRoundedRect(rect, 1, 1)
                
                        painter.setPen(self.SHADOW_PEN)
                        painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y()+2, rect.bottomRight().x(), rect.bottomRight().y()+2)
                
                        r = QtCore.QRect(rect.left(),
                                         rect.top(),
                                         self.THUMB_AREA_WIDTH,
                                         rect.height())
                
                        painter.setPen(QtCore.Qt.NoPen)
                        painter.setBrush(QtGui.QBrush(color))
                        painter.drawRoundedRect(r, 1, 1)
                
                
                    # サムネイル描画メソッド paintメソッド内で呼ばれている
                    def drawThumbnail(self, painter, rect, thumbnail):
                
                        r = QtCore.QRect(rect.left() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                         rect.top() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                         self.THUMB_WIDTH,
                                         self.THUMB_WIDTH)
                        thumbImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", thumbnail)).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH)
                        painter.drawPixmap(r, thumbImage)
                
                
                    # 名前テキスト描画メソッド paintメソッド内で呼ばれている
                    def drawName(self, painter, rect, name):
                
                        painter.setFont(self.FONT_H1)
                        painter.setPen(self.TEXT_BLACK_PEN)
                        r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                         rect.top(),
                                         rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                         rect.height() / 2)
                        painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, "Name : " + name)
                        painter.setPen(self.TEXT_BLACK_PEN)
                        painter.drawLine(r.bottomLeft(), r.bottomRight())
                
                
                    # ディスクリプションテキスト描画メソッド paintメソッド内で呼ばれている
                    def drawDescription(self, painter, rect, description):
                
                        painter.setFont(self.FONT_H2)
                        painter.setPen(self.TEXT_BLACK_PEN)
                        r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                         rect.top() + rect.height() / 2,
                                         rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                         rect.height() / 2)
                        painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, description)
                
                
                    # ステータス画像描画メソッド paintメソッド内で呼ばれている
                    def drawStatus(self, painter, rect, status):
                        painter.setFont(self.FONT_H2)
                        painter.setPen(self.TEXT_BLACK_PEN)
                        r = QtCore.QRect(rect.right() - self.STATUS_AREA_WIDTH - self.MARGIN,
                                         rect.center().y() - self.STATUS_AREA_WIDTH / 2,
                                         self.STATUS_AREA_WIDTH,
                                         self.STATUS_AREA_WIDTH)
                        statusIcon = "notok.png"
                
                        if status == "OK":
                            statusIcon = "ok.png"
                
                        statusImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", statusIcon))
                        painter.drawPixmap(r, statusImage)
                
                
                    # 描画メインメソッド
                    def paint(self, painter, option, index):
                
                        selected = False
                
                        if option.state & QtWidgets.QStyle.State_Selected:
                            selected = True
                
                        name = index.data(QtCore.Qt.DisplayRole)
                        description = index.data(DESCRIPTION_ROLE)
                        status = index.data(STATUS_ROLE)
                        color = index.data(QtCore.Qt.BackgroundRole)
                        thumbnail = index.data(THUMB_ROLE)
                
                        self.drawBackground(painter, option.rect, selected, color)
                        self.drawThumbnail(painter, option.rect, thumbnail)
                        self.drawName(painter, option.rect, name)
                        self.drawDescription(painter, option.rect, description)
                        self.drawStatus(painter, option.rect, status)
                
                
                    # 各アイテムの大きさ
                    def sizeHint(self, option, index):
                
                        return QtCore.QSize(self.ITEM_SIZE_HINT[0], self.ITEM_SIZE_HINT[1])
                
                
                
                
                        
                        
                        
                #----------------------------------------------------------------------------
                ## メインUI
                
                def maya_main_window():
                    main_window_ptr = omui.MQtUtil.mainWindow()
                    return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
                    
                
                class GUI(QtWidgets.QMainWindow):
                
                    def __init__(self, parent=maya_main_window()):
                        super(GUI, self).__init__(parent)
                
                        self.setWindowTitle('Main Window')
                        self.resize(350, 600)
                        self.setMaximumSize(525, 700)
                        self.initUI()
                
                
                    def initUI(self):
                
                        # リストの設定
                        myListView = QtWidgets.QListView(self)
                        myListView.setSpacing(10)
                        myListView.setAutoFillBackground(False)
                        url = os.path.join(CURRENT_PATH, "images", "bg.png").replace("\\", "/")
                        myListView.setStyleSheet("background: url("+url+") center center;")
                        myListView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
                                
                        # modelを作る
                        myListModel = CustomListModel(data = SAMPLE_DATA)
                
                        # delegateを作る
                        myListDelegate = CustomListDelegate()
                
                
                        # listview
                        myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                        myListView.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
                        myListView.setDefaultDropAction(QtCore.Qt.MoveAction)
                        myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                        myListView.setDragEnabled(True)
                        myListView.setAcceptDrops(True)
                        myListView.setDropIndicatorShown(True)
                        
                
                        # modelとdelegateをリストにセット
                        myListView.setItemDelegate(myListDelegate)
                        myListView.setModel(myListModel)
                
                        self.setCentralWidget(myListView)
                
                
                
                
                #
                if __name__ == "__main__":
                    try:
                        test_dialog.close()
                        test_dialog.deleteLater()
                    except:
                        pass
                
                    test_dialog = GUI()
                    test_dialog.show()
                
                G Offline
                G Offline
                Geuse
                wrote on 25 Feb 2023, 22:58 last edited by
                #7

                I've been reading the docs and I don't really get what I need to do.
                For instance, since in this example I'm using a custom model with for my data. I'm just not sure of all the things I need to implement myself. dragging works, and obviosly I can delete the dragged object.
                I just can't seem to get to the point where I move it in place.
                Is data encoded and decoded from the model when I drag and drop in the same ListView, say for a moving operation in this case? It would help tremendously if I could just learn what methods I need to implement myself. Because I'm really stuck now...

                Ideally I'd like it to look like this:
                https://reactscript.com/wp-content/uploads/2019/01/Drag-Drop-List-Component-react-movable.gif

                Not sure if it's possible. But even so, just to get drag-and-drop-move to work would be a dream.

                SGaistS 1 Reply Last reply 26 Feb 2023, 20:26
                0
                • G Geuse
                  25 Feb 2023, 22:58

                  I've been reading the docs and I don't really get what I need to do.
                  For instance, since in this example I'm using a custom model with for my data. I'm just not sure of all the things I need to implement myself. dragging works, and obviosly I can delete the dragged object.
                  I just can't seem to get to the point where I move it in place.
                  Is data encoded and decoded from the model when I drag and drop in the same ListView, say for a moving operation in this case? It would help tremendously if I could just learn what methods I need to implement myself. Because I'm really stuck now...

                  Ideally I'd like it to look like this:
                  https://reactscript.com/wp-content/uploads/2019/01/Drag-Drop-List-Component-react-movable.gif

                  Not sure if it's possible. But even so, just to get drag-and-drop-move to work would be a dream.

                  SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 26 Feb 2023, 20:26 last edited by
                  #8

                  @Geuse

                  myListWidget.setDragDropMode(QAbstractItemView.InternalMove);
                  

                  Is what you need.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  G 1 Reply Last reply 26 Feb 2023, 22:17
                  0
                  • SGaistS SGaist
                    26 Feb 2023, 20:26

                    @Geuse

                    myListWidget.setDragDropMode(QAbstractItemView.InternalMove);
                    

                    Is what you need.

                    G Offline
                    G Offline
                    Geuse
                    wrote on 26 Feb 2023, 22:17 last edited by
                    #9

                    @SGaist Thank you.
                    I tried changing to that and I understand that should be what to use, but that alone doesn't make it work.
                    I would need a real example to see how class CustomListModel(QtCore.QAbstractListModel): should look and work.
                    I feel like I'm just getting confused because I don't know or understand exactly what I'm doing wrong or what needs to be done.

                    JonBJ 1 Reply Last reply 26 Feb 2023, 23:06
                    0
                    • G Geuse
                      26 Feb 2023, 22:17

                      @SGaist Thank you.
                      I tried changing to that and I understand that should be what to use, but that alone doesn't make it work.
                      I would need a real example to see how class CustomListModel(QtCore.QAbstractListModel): should look and work.
                      I feel like I'm just getting confused because I don't know or understand exactly what I'm doing wrong or what needs to be done.

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on 26 Feb 2023, 23:06 last edited by
                      #10

                      @Geuse
                      So far as I can see, you already have

                              del self.__items[row:row + count]
                      

                      in removeRows(), you just need to do the insertion in insertRows(). Then drag&drop reordering (QAbstractItemView.InternalMove) is done by Qt calling both of these methods.

                      G 1 Reply Last reply 27 Feb 2023, 00:31
                      0
                      • JonBJ JonB
                        26 Feb 2023, 23:06

                        @Geuse
                        So far as I can see, you already have

                                del self.__items[row:row + count]
                        

                        in removeRows(), you just need to do the insertion in insertRows(). Then drag&drop reordering (QAbstractItemView.InternalMove) is done by Qt calling both of these methods.

                        G Offline
                        G Offline
                        Geuse
                        wrote on 27 Feb 2023, 00:31 last edited by
                        #11

                        @JonB said in Add reorder functionality to CustomListModel class using QStyledItemDelegate:

                        insertRows()

                        Ok, thank you, it's just that my insertion doesn't work. I'm trying to debug it, but from the print out I'm doing, it seems I'm not getting the correct rows that I can use to put the elemtn in. I get the same row I'm dragging from,
                        From the method:

                        def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                               self.beginInsertRows(parent, row, row + count - 1)
                               self.endInsertRows()
                               print(f'move {row} to {row + count - 1}')
                        

                        As an example, I'm dragging from element 0, The printout says "move 0 to 0".

                        _
                        And does it work like this: when you remove an element, you dump the data temporarly and then when you insert, you create a new element and grab that data. This is the purpose of the mime data?

                        I removed the implementations of mimeTypes, mimeData, canDropMimeData, dropMimeData.
                        But I need to implement them myself?

                        JonBJ 1 Reply Last reply 27 Feb 2023, 08:16
                        0
                        • jeremy_kJ Offline
                          jeremy_kJ Offline
                          jeremy_k
                          wrote on 27 Feb 2023, 02:23 last edited by
                          #12

                          If the goal is to have working code rather than learning how to implement a model, I think OP will be better off using QStandardItemModel. In C++, a custom model has the potential advantage of superior performance. With python, I'm skeptical.

                          QListView view;
                          QStandardItemModel model;
                          for(int i = 0; i < 10; i++) {
                              QStandardItem *item = new QStandardItem(QString::number(i));
                              item->setDropEnabled(false);
                              model.appendRow(item);
                          }
                          view.setModel(&model);
                          view.setDragDropMode(QAbstractItemView::DragDropMode::InternalMove);
                          view.show();
                          

                          Asking a question about code? http://eel.is/iso-c++/testcase/

                          1 Reply Last reply
                          0
                          • G Geuse
                            27 Feb 2023, 00:31

                            @JonB said in Add reorder functionality to CustomListModel class using QStyledItemDelegate:

                            insertRows()

                            Ok, thank you, it's just that my insertion doesn't work. I'm trying to debug it, but from the print out I'm doing, it seems I'm not getting the correct rows that I can use to put the elemtn in. I get the same row I'm dragging from,
                            From the method:

                            def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                                   self.beginInsertRows(parent, row, row + count - 1)
                                   self.endInsertRows()
                                   print(f'move {row} to {row + count - 1}')
                            

                            As an example, I'm dragging from element 0, The printout says "move 0 to 0".

                            _
                            And does it work like this: when you remove an element, you dump the data temporarly and then when you insert, you create a new element and grab that data. This is the purpose of the mime data?

                            I removed the implementations of mimeTypes, mimeData, canDropMimeData, dropMimeData.
                            But I need to implement them myself?

                            JonBJ Offline
                            JonBJ Offline
                            JonB
                            wrote on 27 Feb 2023, 08:16 last edited by JonB
                            #13

                            @Geuse
                            But your insertRows() is empty between beginInsertRows() and endInsertRows(). It does not do anything about inserting any rows there, and it should/must do. Just as you implement del self.__items[row:row + count] in between beginRemoveRows() and endRemoveRows(). So you need the "opposite" of that, something like self._items.insert(row, count). You need to insert count new rows staring at row, the rows should be "blank" (as in, a list/array with the right number of columns but blank/default values in each column). I don't think Python actually offers to insert more than one element at a time into a list, so you'll need to do it in a for i in range(count) loop.

                            You should not be thinking about "moving" or "copying" or "mime data". Nor is it your job to "dump/restore" whatever data is already there. The Qt QListView caller looks after that, your job is just to respond to the calls to remove and insert correctly.

                            Having said the above. If it's all getting too much you might follow @jeremy_k's suggestion and use a QStandardItemModel for this, which will already have the correct remove/insertRows() implemented for you.

                            G 1 Reply Last reply 27 Feb 2023, 11:07
                            0
                            • JonBJ JonB
                              27 Feb 2023, 08:16

                              @Geuse
                              But your insertRows() is empty between beginInsertRows() and endInsertRows(). It does not do anything about inserting any rows there, and it should/must do. Just as you implement del self.__items[row:row + count] in between beginRemoveRows() and endRemoveRows(). So you need the "opposite" of that, something like self._items.insert(row, count). You need to insert count new rows staring at row, the rows should be "blank" (as in, a list/array with the right number of columns but blank/default values in each column). I don't think Python actually offers to insert more than one element at a time into a list, so you'll need to do it in a for i in range(count) loop.

                              You should not be thinking about "moving" or "copying" or "mime data". Nor is it your job to "dump/restore" whatever data is already there. The Qt QListView caller looks after that, your job is just to respond to the calls to remove and insert correctly.

                              Having said the above. If it's all getting too much you might follow @jeremy_k's suggestion and use a QStandardItemModel for this, which will already have the correct remove/insertRows() implemented for you.

                              G Offline
                              G Offline
                              Geuse
                              wrote on 27 Feb 2023, 11:07 last edited by
                              #14

                              @jeremy_k Thank you, I didn't know about that. I will look into it.

                              @JonB Yes, I removed my "insertion code" because it wasn't working. So I thought I should do a print out of the values instead and maybe that's where I made my mistake? the values for count and row that I print out seems to be incorrect. I mean, the values are not the one I anticipated so I'm getting confused.

                              I will try your suggestion. And since I'm a beginner I din't know about QStandardItemModel and it is to be used instead of QAbstractListModel?

                              Thank you so much.

                              G 1 Reply Last reply 27 Feb 2023, 15:17
                              0
                              • G Geuse
                                27 Feb 2023, 11:07

                                @jeremy_k Thank you, I didn't know about that. I will look into it.

                                @JonB Yes, I removed my "insertion code" because it wasn't working. So I thought I should do a print out of the values instead and maybe that's where I made my mistake? the values for count and row that I print out seems to be incorrect. I mean, the values are not the one I anticipated so I'm getting confused.

                                I will try your suggestion. And since I'm a beginner I din't know about QStandardItemModel and it is to be used instead of QAbstractListModel?

                                Thank you so much.

                                G Offline
                                G Offline
                                Geuse
                                wrote on 27 Feb 2023, 15:17 last edited by
                                #15

                                Ok, I kinda see how this works. Maybe....?
                                It seems that insertRows is run before the removeRows.
                                The problem I'm facing is that I don't know how to get the index of the item I want to insert from the row or count argument in the method when insertRows is run. It just spits out seemingly random values. I'm sure they aren't random, but not the values I'm anticipating. I can't make sense of it so how can I tell the method to create a new row in between two elements?

                                The removeRows returns the expected row number that I can use to delete that item from the list with.

                                JonBJ 1 Reply Last reply 27 Feb 2023, 15:33
                                0
                                • G Geuse
                                  27 Feb 2023, 15:17

                                  Ok, I kinda see how this works. Maybe....?
                                  It seems that insertRows is run before the removeRows.
                                  The problem I'm facing is that I don't know how to get the index of the item I want to insert from the row or count argument in the method when insertRows is run. It just spits out seemingly random values. I'm sure they aren't random, but not the values I'm anticipating. I can't make sense of it so how can I tell the method to create a new row in between two elements?

                                  The removeRows returns the expected row number that I can use to delete that item from the list with.

                                  JonBJ Offline
                                  JonBJ Offline
                                  JonB
                                  wrote on 27 Feb 2023, 15:33 last edited by
                                  #16

                                  @Geuse
                                  We are going round in circles. You keep saying/asking the same thing and the answer remains the same. Just implement insertRows() given its parameters. Something like:

                                  self.beginInsertRows(parent, row, row + count - 1)
                                  for i in range(count):
                                      self.__items.insert(row + i, [ 0, "", None, ... ])
                                  self.endInsertRows()
                                  

                                  Stop worrying about when it's being called, just make it do what it's asked to do.

                                  G 1 Reply Last reply 27 Feb 2023, 16:45
                                  0
                                  • JonBJ JonB
                                    27 Feb 2023, 15:33

                                    @Geuse
                                    We are going round in circles. You keep saying/asking the same thing and the answer remains the same. Just implement insertRows() given its parameters. Something like:

                                    self.beginInsertRows(parent, row, row + count - 1)
                                    for i in range(count):
                                        self.__items.insert(row + i, [ 0, "", None, ... ])
                                    self.endInsertRows()
                                    

                                    Stop worrying about when it's being called, just make it do what it's asked to do.

                                    G Offline
                                    G Offline
                                    Geuse
                                    wrote on 27 Feb 2023, 16:45 last edited by
                                    #17

                                    @JonB Yes, I just fail to understand how it all works. I'm very grateful for your patience.
                                    And if I understand you correctly,
                                    this:
                                    self.__items.insert(row + i, [ 0, "", None, ... ])

                                    obviously doesn't work in my example. It says
                                    # TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'

                                    I can't seem to understand how I should alter that to make it work in my case. Mostly because I don't understand this
                                    [ 0, "", None, ... ].

                                    My data looks like this
                                    {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}

                                    I can do something like this:

                                    def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                                            self.beginInsertRows(parent, row, row + count - 1)
                                            for i in range(count):
                                                self.__items.insert(row + i, {"name":"", "description":"", "status":"", "color":[217,204,0], "thumbnail":""})
                                            self.endInsertRows()
                                            return True
                                    

                                    and it will create a new row with this data, but at arbitrary rows each time. sometimes at the end, sometimes in the middle. And that confuses me.
                                    But I gather I should not put in my own data like this.

                                    SGaistS 1 Reply Last reply 27 Feb 2023, 19:59
                                    0
                                    • G Geuse
                                      27 Feb 2023, 16:45

                                      @JonB Yes, I just fail to understand how it all works. I'm very grateful for your patience.
                                      And if I understand you correctly,
                                      this:
                                      self.__items.insert(row + i, [ 0, "", None, ... ])

                                      obviously doesn't work in my example. It says
                                      # TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'

                                      I can't seem to understand how I should alter that to make it work in my case. Mostly because I don't understand this
                                      [ 0, "", None, ... ].

                                      My data looks like this
                                      {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}

                                      I can do something like this:

                                      def insertRows(self, row, count, parent=QtCore.QModelIndex()):
                                              self.beginInsertRows(parent, row, row + count - 1)
                                              for i in range(count):
                                                  self.__items.insert(row + i, {"name":"", "description":"", "status":"", "color":[217,204,0], "thumbnail":""})
                                              self.endInsertRows()
                                              return True
                                      

                                      and it will create a new row with this data, but at arbitrary rows each time. sometimes at the end, sometimes in the middle. And that confuses me.
                                      But I gather I should not put in my own data like this.

                                      SGaistS Offline
                                      SGaistS Offline
                                      SGaist
                                      Lifetime Qt Champion
                                      wrote on 27 Feb 2023, 19:59 last edited by
                                      #18

                                      @Geuse what do you mean by arbitrary ?

                                      Interested in AI ? www.idiap.ch
                                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                                      G 1 Reply Last reply 27 Feb 2023, 20:02
                                      0
                                      • SGaistS SGaist
                                        27 Feb 2023, 19:59

                                        @Geuse what do you mean by arbitrary ?

                                        G Offline
                                        G Offline
                                        Geuse
                                        wrote on 27 Feb 2023, 20:02 last edited by Geuse
                                        #19

                                        @SGaist I understand it can't be arbitrary, but my item isn't always being inserted where I drop it, between the two items in the list. very seldom is it inserted correctly. Most of the time it is added to the bottom.

                                        G 1 Reply Last reply 27 Feb 2023, 20:20
                                        0
                                        • G Geuse
                                          27 Feb 2023, 20:02

                                          @SGaist I understand it can't be arbitrary, but my item isn't always being inserted where I drop it, between the two items in the list. very seldom is it inserted correctly. Most of the time it is added to the bottom.

                                          G Offline
                                          G Offline
                                          Geuse
                                          wrote on 27 Feb 2023, 20:20 last edited by Geuse
                                          #20

                                          I got a suggestion from ChatGPT and it kind of works better now. I'm not neglecting the help or effort all of you are providing. I'm just trying things to see if anything makes it act less weird. I understand I have a special use case and that I'm using Maya(though code wise it shouldn't be an issue).
                                          But I removed both the insertRows and removeRows and added moveRows instead and it works better actually. Though it doesn't insert them correct each time. There has to be something funky in how it calculates the height of the element?
                                          And I can't drag from below and insert above, then it crashes.
                                          I just thought I should post the code here

                                          import maya.OpenMayaUI as omui
                                          
                                          import os
                                          import sys
                                          
                                          from PySide2 import QtWidgets, QtGui, QtCore
                                          from shiboken2 import wrapInstance
                                          
                                          # スクリプトのフォルダ
                                          #CURRENT_PATH = os.path.dirname(__file__)
                                          CURRENT_PATH = os.path.normpath(r'D:\01_personal_files\20_MAYA_CUSTOM_CONFIG\10_maya_custom_config_2022\14_MODULES_DAG\16_dag_wip')
                                          # オリジナルのRole
                                          DESCRIPTION_ROLE = QtCore.Qt.UserRole
                                          STATUS_ROLE = QtCore.Qt.UserRole + 1
                                          THUMB_ROLE = QtCore.Qt.UserRole + 2
                                          
                                          # Modelに入るデータ
                                          SAMPLE_DATA = [
                                              {"name":"Ben", "description":"Support", "status":"OK", "color":[217,204,0], "thumbnail":"ben.png"},
                                              {"name":"Chris", "description":"Modeling", "status":"NOT OK", "color":[127,197,195], "thumbnail":"chris.png"},
                                              {"name":"Daniel", "description":"Manager", "status":"OK", "color":[217,204,166], "thumbnail":"daniel.png"},
                                              {"name":"Natalie", "description":"Sales", "status":"NOT OK", "color":[237,111,112], "thumbnail":"natalie.png"},
                                              {"name":"Mike", "description":"Animation", "status":"NOT OK", "color":[127,197,195], "thumbnail":"mike.png"}
                                          ]
                                          
                                          
                                          class CustomListModel(QtCore.QAbstractListModel):
                                          
                                              def __init__(self, parent=None, data=None):
                                          
                                                  super(CustomListModel, self).__init__(parent)
                                                  self.__items = data
                                          
                                              
                                              def rowCount(self, parent=QtCore.QModelIndex()):
                                                  return len(self.__items)
                                          
                                              def data(self, index, role=QtCore.Qt.DisplayRole):
                                          
                                                  if not index.isValid():
                                                      return None
                                          
                                                  if not 0 <= index.row() < len(self.__items):
                                                      return None
                                          
                                                  if role == QtCore.Qt.DisplayRole:
                                                      return self.__items[index.row()]["name"]
                                          
                                                  elif role == DESCRIPTION_ROLE:
                                                      return self.__items[index.row()]["description"]
                                          
                                                  elif role == STATUS_ROLE:
                                                      return self.__items[index.row()]["status"]
                                          
                                                  elif role == THUMB_ROLE:
                                                      return self.__items[index.row()]["thumbnail"]
                                          
                                                  elif role == QtCore.Qt.BackgroundRole:
                                                      color = self.__items[index.row()]["color"]
                                                      return QtGui.QColor(color[0], color[1], color[2])
                                          
                                                  else:
                                                      return None
                                          
                                              def moveRows(self, sourceParent, sourceRow, count, destinationParent, destinationChild):
                                                  if sourceParent != destinationParent:
                                                      return False
                                              
                                                  self.beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild)
                                                  for i in range(count):
                                                      if destinationChild > sourceRow:
                                                          self.__items.insert(destinationChild - count + i, self.__items.pop(sourceRow))
                                                      else:
                                                          self.__items.insert(destinationChild + i, self.__items.pop(sourceRow))
                                              
                                                  self.endMoveRows()
                                                  return True
                                          
                                          
                                              def supportedDropActions(self):
                                                  return  QtCore.Qt.CopyAction | QtCore.Qt.MoveAction;
                                          
                                          
                                              def flags(self, index):
                                                  defaultFlags = QtCore.QAbstractListModel.flags(self, index)
                                                  
                                                  if index.isValid():
                                                      return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
                                                  else:
                                                      return QtCore.Qt.ItemIsDropEnabled | defaultFlags
                                          
                                          
                                          class CustomListDelegate(QtWidgets.QStyledItemDelegate):
                                          
                                              # Listの各アイテムの大きさ
                                              ITEM_SIZE_HINT = [300, 70]
                                          
                                              # 各アイテム内の表示に使う定数
                                              MARGIN = 20
                                              THUMB_AREA_WIDTH = 70
                                              THUMB_WIDTH = 60
                                              STATUS_AREA_WIDTH = 24
                                          
                                              # Painterが使うブラシやペンの定義
                                              BG_DEFAULT = QtGui.QBrush(QtGui.QColor(247,248,242))
                                              BG_SELECTED = QtGui.QBrush(QtGui.QColor(255,255,204))
                                              BORDER_DEFAULT = QtGui.QPen(QtGui.QColor(255,255,255), 0.5, QtCore.Qt.SolidLine)
                                              TEXT_BLACK_PEN = QtGui.QPen(QtGui.QColor(106,107,109), 0.5, QtCore.Qt.SolidLine)
                                              SHADOW_PEN = QtGui.QPen(QtGui.QColor(220,220,220, 100), 1, QtCore.Qt.SolidLine)
                                              FONT_H1 = QtGui.QFont("Corbel", 12, QtGui.QFont.Normal, True)
                                              FONT_H2 = QtGui.QFont("Corbel", 11, QtGui.QFont.Normal, True)
                                          
                                          
                                              def __init__(self, parent=None):
                                          
                                                  super(CustomListDelegate, self).__init__(parent)
                                          
                                          
                                          
                                              # 背景描画メソッド paintメソッド内で呼ばれている
                                              def drawBackground(self, painter, rect, selected, color):
                                          
                                                  if selected:
                                                      baseColor = self.BG_SELECTED
                                          
                                                  else:
                                                      baseColor = self.BG_DEFAULT
                                          
                                                  painter.setPen(self.BORDER_DEFAULT)
                                                  painter.setBrush(baseColor)
                                                  painter.drawRoundedRect(rect, 1, 1)
                                          
                                                  painter.setPen(self.SHADOW_PEN)
                                                  painter.drawLine(rect.bottomLeft().x(), rect.bottomLeft().y()+2, rect.bottomRight().x(), rect.bottomRight().y()+2)
                                          
                                                  r = QtCore.QRect(rect.left(),
                                                                   rect.top(),
                                                                   self.THUMB_AREA_WIDTH,
                                                                   rect.height())
                                          
                                                  painter.setPen(QtCore.Qt.NoPen)
                                                  painter.setBrush(QtGui.QBrush(color))
                                                  painter.drawRoundedRect(r, 1, 1)
                                          
                                          
                                              # サムネイル描画メソッド paintメソッド内で呼ばれている
                                              def drawThumbnail(self, painter, rect, thumbnail):
                                          
                                                  r = QtCore.QRect(rect.left() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                                                   rect.top() + (self.THUMB_AREA_WIDTH - self.THUMB_WIDTH) / 2,
                                                                   self.THUMB_WIDTH,
                                                                   self.THUMB_WIDTH)
                                                  thumbImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", thumbnail)).scaled(self.THUMB_WIDTH, self.THUMB_WIDTH)
                                                  painter.drawPixmap(r, thumbImage)
                                          
                                          
                                              # 名前テキスト描画メソッド paintメソッド内で呼ばれている
                                              def drawName(self, painter, rect, name):
                                          
                                                  painter.setFont(self.FONT_H1)
                                                  painter.setPen(self.TEXT_BLACK_PEN)
                                                  r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                                                   rect.top(),
                                                                   rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                                                   rect.height() / 2)
                                                  painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, "Name : " + name)
                                                  painter.setPen(self.TEXT_BLACK_PEN)
                                                  painter.drawLine(r.bottomLeft(), r.bottomRight())
                                          
                                          
                                              # ディスクリプションテキスト描画メソッド paintメソッド内で呼ばれている
                                              def drawDescription(self, painter, rect, description):
                                          
                                                  painter.setFont(self.FONT_H2)
                                                  painter.setPen(self.TEXT_BLACK_PEN)
                                                  r = QtCore.QRect(rect.left() + self.THUMB_AREA_WIDTH + self.MARGIN,
                                                                   rect.top() + rect.height() / 2,
                                                                   rect.width() - self.THUMB_AREA_WIDTH - self.STATUS_AREA_WIDTH - self.MARGIN * 3,
                                                                   rect.height() / 2)
                                                  painter.drawText(r, QtCore.Qt.AlignVCenter|QtCore.Qt.AlignLeft, description)
                                          
                                          
                                              # ステータス画像描画メソッド paintメソッド内で呼ばれている
                                              def drawStatus(self, painter, rect, status):
                                                  painter.setFont(self.FONT_H2)
                                                  painter.setPen(self.TEXT_BLACK_PEN)
                                                  r = QtCore.QRect(rect.right() - self.STATUS_AREA_WIDTH - self.MARGIN,
                                                                   rect.center().y() - self.STATUS_AREA_WIDTH / 2,
                                                                   self.STATUS_AREA_WIDTH,
                                                                   self.STATUS_AREA_WIDTH)
                                                  statusIcon = "notok.png"
                                          
                                                  if status == "OK":
                                                      statusIcon = "ok.png"
                                          
                                                  statusImage = QtGui.QPixmap(os.path.join(CURRENT_PATH, "images", statusIcon))
                                                  painter.drawPixmap(r, statusImage)
                                          
                                          
                                              # 描画メインメソッド
                                              def paint(self, painter, option, index):
                                          
                                                  selected = False
                                          
                                                  if option.state & QtWidgets.QStyle.State_Selected:
                                                      selected = True
                                          
                                                  name = index.data(QtCore.Qt.DisplayRole)
                                                  description = index.data(DESCRIPTION_ROLE)
                                                  status = index.data(STATUS_ROLE)
                                                  color = index.data(QtCore.Qt.BackgroundRole)
                                                  thumbnail = index.data(THUMB_ROLE)
                                          
                                                  self.drawBackground(painter, option.rect, selected, color)
                                                  self.drawThumbnail(painter, option.rect, thumbnail)
                                                  self.drawName(painter, option.rect, name)
                                                  self.drawDescription(painter, option.rect, description)
                                                  self.drawStatus(painter, option.rect, status)
                                          
                                          
                                              # 各アイテムの大きさ
                                              def sizeHint(self, option, index):
                                          
                                                  return QtCore.QSize(self.ITEM_SIZE_HINT[0], self.ITEM_SIZE_HINT[1])
                                          
                                          
                                          
                                          
                                                  
                                                  
                                                  
                                          #----------------------------------------------------------------------------
                                          ## メインUI
                                          
                                          def maya_main_window():
                                              main_window_ptr = omui.MQtUtil.mainWindow()
                                              return wrapInstance(int(main_window_ptr), QtWidgets.QWidget)
                                              
                                          
                                          class GUI(QtWidgets.QMainWindow):
                                          
                                              def __init__(self, parent=maya_main_window()):
                                                  super(GUI, self).__init__(parent)
                                          
                                                  self.setWindowTitle('Main Window')
                                                  self.resize(350, 600)
                                                  self.setMaximumSize(525, 700)
                                                  self.initUI()
                                          
                                              def initUI(self):
                                                  # リストの設定
                                                  myListView = QtWidgets.QListView(self)
                                                  myListView.setSpacing(10)
                                                  myListView.setAutoFillBackground(False)
                                                  url = os.path.join(CURRENT_PATH, "images", "bg.png").replace("\\", "/")
                                                  myListView.setStyleSheet("background: url("+url+") center center;")
                                                  myListView.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
                                                          
                                                  # modelを作る
                                                  myListModel = CustomListModel(data = SAMPLE_DATA)
                                                  # delegateを作る
                                                  myListDelegate = CustomListDelegate()
                                          
                                                  # listview
                                                  myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                                                  myListView.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
                                                  myListView.setDefaultDropAction(QtCore.Qt.MoveAction)
                                                  myListView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
                                                  myListView.setDragEnabled(True)
                                                  myListView.setAcceptDrops(True)
                                                  myListView.setDropIndicatorShown(True)
                                          
                                                  # modelとdelegateをリストにセット
                                                  myListView.setItemDelegate(myListDelegate)
                                                  myListView.setModel(myListModel)
                                                  #
                                                  self.setCentralWidget(myListView)
                                          
                                          
                                          
                                          
                                          #
                                          if __name__ == "__main__":
                                              try:
                                                  test_dialog.close()
                                                  test_dialog.deleteLater()
                                              except:
                                                  pass
                                          
                                              test_dialog = GUI()
                                              test_dialog.show()
                                          
                                          SGaistS 1 Reply Last reply 27 Feb 2023, 20:28
                                          0

                                          1/22

                                          23 Feb 2023, 17:13

                                          • Login

                                          • Login or register to search.
                                          1 out of 22
                                          • First post
                                            1/22
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved