Search code examples
pythonpyqtpyqt5qfiledialog

QFileDialog preview


I am trying to get a Qt5 QFileDialog to show previews for images when selecting them for opening.

Method 1: Extending the QFileDialog
I used this implementation of the dialog and it worked with Qt 5.6.1 .

Now I am using Qt 5.9.2 and it does not work anymore, giving the following error:

Traceback (most recent call last):
  File "main.py", line 74, in mouseDoubleClickEvent
    self.openFileDialog()
  File "main.py", line 123, in openFileDialog
    openDialog = QFileDialogPreview(self, 'Open file', './', self.getDialogFilter())
  File "QFileDialogPreview.py", line 22, in __init__
    self.layout().addLayout(box, 1, 3, 1, 1)
TypeError: addLayout(self, QLayout, stretch: int = 0): too many arguments

Here is the translated class. I am using Python 3.5.2:

from PyQt5.QtWidgets import QFileDialog, QLabel, QVBoxLayout
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt

class QFileDialogPreview(QFileDialog):
    def __init__(self, parent, caption, direcotry, filter):
        super().__init__(parent, caption, direcotry, filter)

        self.setObjectName("FileDialogPreview")
        box = QVBoxLayout(self)

        self.setFixedSize(self.width() + 250, self.height())

        self.mpPreview = QLabel("Preview", self)
        self.mpPreview.setFixedSize(250, 250)
        self.mpPreview.setAlignment(Qt.AlignCenter)
        self.mpPreview.setObjectName("labelPreview")
        box.addWidget(self.mpPreview)

        box.addStretch()

        self.layout().addLayout(box, 1, 3, 1, 1)

        self.currentChanged.connect(self.onChange)
        self.fileSelected.connect(self.onFileSelected)
        self.filesSelected.connect(self.onFilesSelected)

        self._fileSelected = None
        self._filesSelected = None

    def onChange(self, path):
        pixmap = QPixmap(path)

        if(pixmap.isNull()):
            self.mpPreview.setText("Preview")
        else:
            self.mpPreview.setPixmap(pixmap.scaled(self.mpPreview.width(), self.mpPreview.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def onFileSelected(self, file):
        self._fileSelected = file

    def onFilesSelected(self, files):
        self._filesSelected = files

    def getFileSelected(self):
        return self._fileSelected

    def getFilesSelected(self):
        return self._filesSelected

Method 2: Separate window for previews

I also tried adding a separate window for the preview. While it shows the image when it gets selected in the QFileDialog I fail to attach it to the side of the Dialog itself.
I cannot get the position of the Dialog mapped to global screen coordinates using the mapToGlobal method on dialog.gemoetry(). All I get is a QRect containing the position 0,0 with a mismatching size 100x30 in this case, while the dialog is about 800x600 and roughly at 2000, 500.


Solution

  • When you put box = QVBoxLayout(self), you are placing a new layout, what you must do is use the QFileDialog own Qt, for this we enable the DontUseNativeDialog flag, in addition to removing the self from the box statement:

    class QFileDialogPreview(QFileDialog):
        def __init__(self, *args, **kwargs):
            QFileDialog.__init__(self, *args, **kwargs)
            self.setOption(QFileDialog.DontUseNativeDialog, True)
    
            box = QVBoxLayout()
    
            self.setFixedSize(self.width() + 250, self.height())
    
            self.mpPreview = QLabel("Preview", self)
            self.mpPreview.setFixedSize(250, 250)
            self.mpPreview.setAlignment(Qt.AlignCenter)
            self.mpPreview.setObjectName("labelPreview")
            box.addWidget(self.mpPreview)
    
            box.addStretch()
    
            self.layout().addLayout(box, 1, 3, 1, 1)
    
            self.currentChanged.connect(self.onChange)
            self.fileSelected.connect(self.onFileSelected)
            self.filesSelected.connect(self.onFilesSelected)
    
            self._fileSelected = None
            self._filesSelected = None
    
        def onChange(self, path):
            pixmap = QPixmap(path)
    
            if(pixmap.isNull()):
                self.mpPreview.setText("Preview")
            else:
                self.mpPreview.setPixmap(pixmap.scaled(self.mpPreview.width(), self.mpPreview.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
    
        def onFileSelected(self, file):
            self._fileSelected = file
    
        def onFilesSelected(self, files):
            self._filesSelected = files
    
        def getFileSelected(self):
            return self._fileSelected
    
        def getFilesSelected(self):
            return self._filesSelected
    

    Output:

    enter image description here