Search code examples
pythonpyqtpyqt5qlabelqfiledialog

How can I change the label to display the file path with PyQt5?


I would like to change the text of a Label widget from the QLabel class to display the file path of an image file chosen by a user when they click a button to open the file dialog to choose an image from their computer. Here is the code:

class GUI(QMainWindow):
    #Load the GUI.
    def __init__(self):
        super().__init__()

        #Define the window icon.
        self.window_icon = QIcon("LightMap.png")

        #Make the UI.
        self.init_ui()

    #Fill the GUI.
    def init_ui(self):
        #Load the UI file.
        main_window = uic.loadUi("mainwindow.ui", self)

        #Set the window icon.
        self.setWindowIcon(self.window_icon)

        #Add commands for actions under the submenus.
        self.command_file_menu(main_window)
        self.command_settings_menu(main_window)
        self.command_help_menu(main_window)

        #Handle the case that the user clicks on the "Open Image" button.
        main_window.button_open_image.setStatusTip("Open Image")
        main_window.button_open_image.clicked.connect(self.open_file)

        #Make sure this variable has been declared so that we can click "Start Mapping" at any time.
        self.file_chosen = None

        #Handle the case that the user clicks on the "Start Mapping" button.
        main_window.button_start_mapping.setStatusTip("Start Mapping")
        main_window.button_start_mapping.clicked.connect(self.start_mapping)

        #Show the main window.
        self.show()

    #Add commands for actions under the File menu.
    def command_file_menu(self, main_window):
        #Back-end logic for Open Image.
        main_window.action_open_image.setShortcut("CTRL+O")
        main_window.action_open_image.setStatusTip("Open Image")
        main_window.action_open_image.triggered.connect(self.open_file)

    #Open an image file.
    def open_file(self):
        #Select the file dialog design.
        dialog_style = QFileDialog.DontUseNativeDialog
        dialog_style |= QFileDialog.DontUseCustomDirectoryIcons

        #Open the file dialog to select an image file.
        self.file_chosen, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
            "JPEG (*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif);; PNG (*.PNG *.png);; GIF (*.GIF *.gif);; Bitmap Files (*.BMP *.bmp *.DIB *.dib);; TIFF (*.TIF *.tif *.TIFF *.tiff);; ICO (*.ICO *.ico)", options=dialog_style)

        #Show the path of the file chosen.
        if self.file_chosen:
            #Change the text on the label to display the file path chosen.
        else:
            #Change the text on the label to say that "No file was selected. Please select an image."
            #This 'else' statement is used to catch the case where the user presses 'Cancel' after opening the file dialog,
            #which will close the file dialog without choosing any file, even if they had already previously chosen a file
            #from previously opening the file dialog.


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = GUI()
    sys.exit(app.exec_())

I have already tried to directly pass main_window into open_file() and directly set the label text like this: main_window.label_file_name.setText(self.file_chosen), but the file dialog opens immediately as I launch the GUI with the error TypeError: argument1 has unexpected type NoneType.

I managed to get this feature working with TkInter, but I haven't been able to figure out how to repeat that functionality over to PyQt5. Here is an example of my working TkInter code:

class GUI:    
    #Structure the GUI.
    def __init__(self):

        #Create a blank window.
        self.root = Tk()

        #Create the frame.
        frameRoot = Frame(self.root)
        frameRoot.pack()

        #Create the menu.
        menu = Menu(self.root)
        self.root.config(menu=menu)

        #Create the "File" submenu.
        fileMenu = Menu(menu, tearoff=0)
        menu.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_command(label="Open Image", command=self.openFile)

        #Make a button to open the image file.
        self.fileChosen = None #Prevent the user from mapping without first selecting an image.
        self.buttonFile = Button(frameRoot, text="Open Image...", command=self.openFile)
        self.buttonFile.grid(row=3, column=1)

        #Display the directory path of the file chosen.
        self.fileName = StringVar()
        self.fileName.set("No File Selected")
        self.labelFileName = Label(frameRoot, textvariable=self.fileName, fg="red")
        self.labelFileName.grid(row=4, column=1)

        #Keep the window open.
        self.root.mainloop()

    #Open the image file.
    def openFile(self):
        #Only accept the following file types.
        self.fileChosen = filedialog.askopenfilename(filetypes=[("Bitmap Files", "*.BMP *.bmp *.DIB *.dib"),
                                                              ("JPEG", "*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif"),
                                                              ("PNG", "*.PNG *.png"),
                                                              ("GIF", "*.GIF *.gif"),
                                                              ("TIFF", "*.TIF *.tif *.TIFF *.tiff"),
                                                              ("ICO", "*.ICO *.ico")
                                                             ])

        #If a file was selected, show the file path. Else, inform the user.
        if self.fileChosen:
            self.fileName.set(self.fileChosen)
        else:
            self.fileName.set("No image was selected. Please select an image.")

if __name__ == "__main__":
    #Create an object to access the class.
    g = GUI()

The key to my success with my TkInter implementation was the usage of the StringVar() class which comes from TkInter. I've tried doing a mix of TkInter and PyQt5 just to get this requirement working, but that just threw some more errors.


Solution

  • In your case you should be able to access the variable main_window since it allows me to access the label, for this you must pass it as a parameter, so I would suggest that you use a lambda function and modify your code to:

        [...]
        main_window.button_open_image.clicked.connect(lambda: self.open_file(main_window))
    
    #Open an image file.
    def open_file(self, main_window):
        #Select the file dialog design.
        dialog_style = QFileDialog.DontUseNativeDialog
        dialog_style |= QFileDialog.DontUseCustomDirectoryIcons
    
        #Open the file dialog to select an image file.
        self.file_chosen, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
            "JPEG (*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif);; PNG (*.PNG *.png);; GIF (*.GIF *.gif);; Bitmap Files (*.BMP *.bmp *.DIB *.dib);; TIFF (*.TIF *.tif *.TIFF *.tiff);; ICO (*.ICO *.ico)", options=dialog_style)
    
        #Show the path of the file chosen.
        if self.file_chosen:
            main_window.label_file_name.setText(self.file_chosen)
            #Change the text on the label to display the file path chosen.
        else:
            main_window.label_file_name.setText("No file was selected. Please select an image.")
            #Change the text on the label to say that "No file was selected. Please select an image."
            #This 'else' statement is used to catch the case where the user presses 'Cancel' after opening the file dialog,
            #which will close the file dialog without choosing any file, even if they had already previously chosen a file
            #from previously opening the file dialog.