I have been trying to develop a GUI application which can be used for batch run this week. One expected feature is to extract name of all SAS program files within a folder and then place them in a QListView. It is well known that batch processing sequence is required for some tasks like ADaM production and qc. For example, adsl must be placed as the first program for batch run. In this case, items of our QListView must be movable. To order the list in an easier way, I added two buttons. One is to move up items and another is to move down items.  Today’s post will present python code showing how to move up and move down items in QListView. Hope it will help as I found that there are no tutorials telling us how to achieve this goal from internet.

we can extract all indexes of selected items with attribute selectedIndexes from QListView class and then iterate over these indexes to move corresponding items.

How to move items up

Suppose that we have a list having 7 items and would like to move up item 3 and item 4 together, below figure shows the how to move them. Newly inserted items will be highlighted in yellow. While selected items will be highlighted in blue.

Let’s move item 3 at first. By using data attribute and row attribute of index, we can get text and row of item 3. By deducting 1 from row of item 3, we can get row of item 2. By using InsertRow and setData function of QStringListModel (slm in our post), a new row (see cell D5) having text as item 3 will be created. Now we need to move the old ‘item 3’ using removeRow function of QStringListModel. But here I need to remind you that the index for old ‘item 3’ (cell D7) was changed due to newly inserted ‘item 3’ (cell D5). Row for old ‘item 3’ must be increased by 1. Same approach can be used to move item 4.

From below figure, you can see that new ‘item 3’ is just on top of ‘item 2’ and old ‘item 3’ is just under ‘item 2’. Newly created ‘item 4’, ‘item 2’ and old ‘item 4’ also have the same relationship after item 3 was moved before item 2. This is the reason why we can iterate over selected indexes to move items one by one and why we need to move item 3 at first.

In order to move these items continuously, we also need to set status of these newly inserted items as selected (background color was changed from yellow to blue) by using slectionModel function.

How to move items down

Similar idea of ‘create new item and remove item’ can be used to move items down. But here we need to move last one of selected items (item 4 in our example) first. Only in this way, new item 3 will be under item 5 and old item 3 will sit above item 5, just like what we have for new item 4, item 5 and old item 4.

Another point is that we need to add 2 to row of selected item for inserted new row. Let’s take item 4 in column B as an example. To move item 4 under item 5, we need to insert new item at row of item 6. Row of item 6 minus row of item 4 equals to 2.

Python Code for Moving Items Up and Down in QListView

Here is a simple example code to generate a list having 7 items and two buttons for moving up/down items.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import qtawesome

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.whole_box = QVBoxLayout(self)
        self.splitter1 = QSplitter(Qt.Horizontal)

        self.group = QGroupBox('Example QListView')
        self.vbox = QVBoxLayout()
        self.list = QListView()
        self.slm = QStringListModel()
        self.list.setModel(self.slm)
        self.list.setUpdatesEnabled(True)
        self.list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list.setEditTriggers(QAbstractItemView.NoEditTriggers)
        str_lst = []
        for i in range(7):
            str_lst.append('SAS Program ' + str(i + 1))
        self.slm.setStringList(str_lst)

        self.vbox.addWidget(self.list)
        self.group.setLayout(self.vbox)

        self.splitter2 = QSplitter(Qt.Vertical)
        self.btn1 = QPushButton(qtawesome.icon('fa.caret-up',color='black'),'Move Up',)
        self.btn1.setFixedHeight(30)
        self.splitter2.addWidget(self.btn1)
        self.btn2 = QPushButton(qtawesome.icon('fa.caret-down',color='black'),'Move Down')
        self.btn2.setFixedHeight(30)
        self.splitter2.addWidget(self.btn2)
        self.lbl1 = QLabel(' ')
        self.splitter2.addWidget(self.lbl1)

        self.splitter1.addWidget(self.group)
        self.splitter1.addWidget(self.splitter2)

        self.whole_box.addWidget(self.splitter1)

        self.btn1.clicked.connect(lambda: self.move_up())
        self.btn2.clicked.connect(lambda: self.move_down())

    # Function to move up item
    def move_up(self):
        indexes = self.list.selectedIndexes()
        if len(indexes) > 0:
            try:
                indexes.sort()
                first_row = indexes[0].row() - 1
                if first_row >= 0:
                    for idx in indexes:
                        if idx != None:
                            txt = idx.data()
                            row = idx.row()
                            self.slm.insertRow(row - 1)
                            pre_idx = self.slm.index(row - 1)
                            self.slm.removeRow(row + 1)
                            self.slm.setData(pre_idx, txt, Qt.EditRole)
                            self.list.selectionModel().select(pre_idx, QItemSelectionModel.Select)
            except Exception as e:
                print(e)

        else:
            print('Select at least one item from list!')

    # Function to move down item
    def move_down(self):
        max_row = self.slm.rowCount()
        indexes = self.list.selectedIndexes()
        if len(indexes) > 0:
            try:
                indexes.sort()
                last_row = indexes[-1].row() + 1
                if last_row < max_row:
                    for idx in reversed(indexes):
                        if idx != None:
                            txt = idx.data()
                            row = idx.row()
                            self.slm.insertRow(row + 2)
                            post_idx = self.slm.index(row + 2)
                            self.slm.setData(post_idx, txt, Qt.EditRole)
                            self.list.selectionModel().select(post_idx, QItemSelectionModel.Select)
                            self.slm.removeRow(row)
            except Exception as e:
                print(e)
        else:
            print('Select at least one item from list!')


if __name__ == '__main__':

    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 400, 250)

Here shows what we will get if click ‘Move Up’ two times after selecting 3rd and 4th items.

Following shows result after clicking ‘Move Down’ two times.