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
Forum Update on Monday, May 27th 2025

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.
  • SGaistS SGaist

    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 last edited by
    #5

    @SGaist Thanks,
    I will look into that.

    1 Reply Last reply
    0
    • G Geuse

      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 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
      0
      • G Geuse

        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 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
        0
        • G Geuse

          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 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
          0
          • SGaistS SGaist

            @Geuse

            myListWidget.setDragDropMode(QAbstractItemView.InternalMove);
            

            Is what you need.

            G Offline
            G Offline
            Geuse
            wrote on 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
            0
            • G Geuse

              @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 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
              0
              • JonBJ JonB

                @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 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
                0
                • jeremy_kJ Offline
                  jeremy_kJ Offline
                  jeremy_k
                  wrote on 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

                    @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 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
                    0
                    • JonBJ JonB

                      @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 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
                      0
                      • G Geuse

                        @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 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
                        0
                        • G Geuse

                          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 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
                          0
                          • JonBJ JonB

                            @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 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
                            0
                            • G Geuse

                              @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 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
                              0
                              • SGaistS SGaist

                                @Geuse what do you mean by arbitrary ?

                                G Offline
                                G Offline
                                Geuse
                                wrote on 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
                                0
                                • G Geuse

                                  @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 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
                                  0
                                  • G Geuse

                                    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 Offline
                                    SGaistS Offline
                                    SGaist
                                    Lifetime Qt Champion
                                    wrote on last edited by
                                    #21

                                    @Geuse you should temporarily remove all code unrelated to the model. Just keep the list view and the model itself to do your testing.

                                    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
                                    0
                                    • SGaistS SGaist

                                      @Geuse you should temporarily remove all code unrelated to the model. Just keep the list view and the model itself to do your testing.

                                      G Offline
                                      G Offline
                                      Geuse
                                      wrote on last edited by Geuse
                                      #22

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

                                      @Geuse you should temporarily remove all code unrelated to the model. Just keep the list view and the model itself to do your testing.

                                      Thanks.
                                      I commented out my delegate and eventually I changed my spacing in my ListView from 10 to 0.

                                      myListView.setSpacing(0)
                                      

                                      and would you know, that fixed it...
                                      Now the dragging and dropping works both ways. It just doesn't look as nice anymore., maybe I can fix this in the delegate instead.

                                      And well, when dropping you have to hit it right in between where none of the other elements get highlighted, then it works perfect so I should be happy. Thanks for all the help and patience with me.

                                      Is it possible to make it look like the animation I posted here below? Can you style it or code it to behave that way?
                                      Add animation to the elements so they are pushed to open up an empty space in between the rows and simultaneously remove the item I'm dragging from its own position in the list?

                                      alt text

                                      1 Reply Last reply
                                      0

                                      • Login

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