Is there is a way to stop emitting signals for PyQt ListView?

432 Views Asked by At

I'm trying to build a PyQt GUI application which is converting file formats. I have a ListView and it's connected to my QAbstractListModel.

My problem is, as a beginner to MVC design I understand my view emitting signals whenever I hover my cursor, focus app window, etc. and the ListView accessing to my model's data() method and then repeating the whole process of icon setting and other stuff.

Is there is a way to stop emitting signals for my listview except for my drop event?

Because I don't need to update my view for hover, focus signals, etc.

I tried to use blockSignals (True) for the view but it's also blocking my drop signal which is I don't want to block it. But I'm not sure if I implemented it correctly. I added blockSingals into the init method of my model.

Here is my model:

class FileModel(QtCore.QAbstractListModel):
    
    def __init__(self, data = [], parent = None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self._data = data

        
    def rowCount(self, parent):
         return len(self._data) 
    
    def data(self, index, role):

# Here I print the role to show you
# my view check my model for every signal

        print(role)  
        if role == QtCore.Qt.DisplayRole:
            
            row = index.row()
            value = self._data[row]  

            
            return value
        
        if role == QtCore.Qt.DecorationRole:
            row = index.row()
            path = self._data[row].replace("/", "\\")
            image = self.get_icon(path, "small")
            qtImage = ImageQt.ImageQt(image)
            pixmap = QtGui.QPixmap.fromImage(qtImage)
            icon = QtGui.QIcon(pixmap)

            return icon        
    
    def insertRows(self, position, rows, items, parent):
        self.beginInsertRows(QtCore.QModelIndex(),
                             position,
                             position + rows - 1)

        for index in range(rows):
            self._data.insert(position, items[index])
            
        self.endInsertRows()
        
        return True
    
    def get_icon(self, PATH, size):  
        SHGFI_ICON = 0x000000100  
        SHGFI_ICONLOCATION = 0x000001000  
        if size == "small":  
            SHIL_SIZE= 0x00001  
        elif size == "large":  
            SHIL_SIZE= 0x00002  
        else:  
            raise TypeError("Invalid argument for 'size'. Must be equal to 'small' or 'large'")  

        ret, info = shell.SHGetFileInfo(PATH, 0, SHGFI_ICONLOCATION | SHGFI_ICON | SHIL_SIZE)  
        hIcon, iIcon, dwAttr, name, typeName = info  
        ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON)  
        hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))  
        hbmp = win32ui.CreateBitmap()  
        hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_x)  
        hdc = hdc.CreateCompatibleDC()  
        hdc.SelectObject(hbmp)  
        hdc.DrawIcon((0, 0), hIcon)  
        win32gui.DestroyIcon(hIcon)  

        bmpinfo = hbmp.GetInfo()  
        bmpstr = hbmp.GetBitmapBits(True)  
        img = Image.frombuffer(  
            "RGBA",  
            (bmpinfo["bmWidth"], bmpinfo["bmHeight"]),  
            bmpstr, "raw", "BGRA", 0, 1  
        )  

        if size == "small":  
            img = img.resize((20, 20), Image.ANTIALIAS)  

        return img

Here is my ui:

class Ui_Window(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.listView = FileView(MainWindow)
        self.listView.setGeometry(QtCore.QRect(35, 21, 721, 521))
        self.listView.setObjectName("listView")
        self.listView.setViewMode(QtWidgets.QListWidget.ListMode)


        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))



class FileView(Ui_Window, QListView): 
    def __init__(self,parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.setDragDropMode(QAbstractItemView.DragDrop)
        self.items = []
        self.model = FileModel(self.items)
        self.setModel(self.model)

        
    def setupUi(self, Form):
       super().setupUi(Form)

        
    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()

            links = []

            for url in event.mimeData().urls():
                if url.isLocalFile():
                    links.append(str(url.toLocalFile()))
                else:
                    links.append(str(url.toString()))
                        
            self.model.insertRows(0, len(links), links, QtCore.QModelIndex())
        

            
        else:
            event.ignore()    

My application nothing dropped yet:

My application nothing dropped yet

Here I dropped a file and hovered my mouse on the file couple of times:

Here I dropped a file and hovered my mouse on the file couple of times

As you can see from the second image whenever I click or hover etc. my data function is being run by the view.

1

There are 1 best solutions below

2
mugiseyebrows On

Every time view repaints iteself (for example when you scroll it), it fetches all necessary data from model, it's how things designed to work, I dont think it's good idea to interfere with this process. If you experiencing lags, you can optimize your model, for example - cache images for decoration role using dict {path: QImage} (or event better {ext: QImage}) and only call get_icon if cache does not contain entry.