2

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Hize75
  • 23
  • 3
  • If you would draw what your expected result should be like, people could help you easier. – user1767754 Dec 01 '17 at 18:26
  • Either a normal file open dialog extended by a widget to the right side, or a separate window to the right of the dialog containing my preview widget – Hize75 Dec 01 '17 at 18:30

2 Answers2

5

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
0

In addition to @eyllanesc answer on the topic, I initially couldn't figure out how to call the class specifically to open multiple files (off-course, the preview will only work when a single file is selected). So the lines below show how to do this. (Posting this as a separate answer, not a comment since the code contains multiple lines)

## For selecting a single file
def openBtn_single_clicked(self):
    
    filedialog = QFileDialogPreview(self,"Open File",
        "","Image Files (*.png *.jpg *.jpeg)")
    filedialog.setFileMode(QFileDialog.ExistingFile)
    if filedialog.exec_() == QFileDialogPreview.Accepted:
        print(filedialog.getFileSelected())

    return


## For selecting multiple files
def openBtn_multiple_clicked(self):
    filedialog = QFileDialogPreview(self,"Open File",
        "","PDF Files (*.pdf)")
    filedialog.setFileMode(QFileDialog.ExistingFiles)
    if filedialog.exec_() == QFileDialogPreview.Accepted:
        print(filedialog.getFilesSelected())

    return

Both the functions need to be off-course attached to buttons via the commands

openBtnSingleFile.clicked.connect(self.openBtn_single_clicked)
openBtnMultipleFiles.clicked.connect(self.openBtn_multiple_clicked)
ASarkar
  • 469
  • 5
  • 16