PyQt5 Open File Dialog and Text Editor

PyQt5 Open File Dialog and Text Editor

A small text editor is a good first PyQt5 project because it uses several common Qt ideas: a main window, a central widget, menus, actions, and a file dialog.

The example below creates a simple editor that can open a text file, display it in a QTextEdit, edit the content, and save it back to disk.

Example

import sys

from PyQt5.QtWidgets import (
    QAction,
    QApplication,
    QFileDialog,
    QMainWindow,
    QMessageBox,
    QTextEdit,
)


class TextEditor(QMainWindow):
    def __init__(self):
        super().__init__()

        self.current_file = None
        self.editor = QTextEdit()
        self.setCentralWidget(self.editor)

        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("PyQt5 Text Editor")
        self.resize(800, 600)

        open_action = QAction("Open", self)
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.open_file)

        save_action = QAction("Save", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_file)

        save_as_action = QAction("Save As", self)
        save_as_action.setShortcut("Ctrl+Shift+S")
        save_as_action.triggered.connect(self.save_file_as)

        exit_action = QAction("Exit", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)

        file_menu = self.menuBar().addMenu("File")
        file_menu.addAction(open_action)
        file_menu.addAction(save_action)
        file_menu.addAction(save_as_action)
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

    def open_file(self):
        file_name, _ = QFileDialog.getOpenFileName(
            self,
            "Open File",
            "",
            "Text Files (*.txt);;Python Files (*.py);;All Files (*)",
        )

        if not file_name:
            return

        try:
            with open(file_name, "r", encoding="utf-8") as file:
                self.editor.setPlainText(file.read())
        except OSError as error:
            QMessageBox.critical(self, "Open Error", str(error))
            return

        self.current_file = file_name
        self.setWindowTitle(f"PyQt5 Text Editor - {file_name}")

    def save_file(self):
        if self.current_file is None:
            self.save_file_as()
            return

        try:
            with open(self.current_file, "w", encoding="utf-8") as file:
                file.write(self.editor.toPlainText())
        except OSError as error:
            QMessageBox.critical(self, "Save Error", str(error))

    def save_file_as(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self,
            "Save File",
            "",
            "Text Files (*.txt);;Python Files (*.py);;All Files (*)",
        )

        if not file_name:
            return

        self.current_file = file_name
        self.save_file()
        self.setWindowTitle(f"PyQt5 Text Editor - {file_name}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = TextEditor()
    window.show()
    sys.exit(app.exec_())

How It Works

QMainWindow gives the application a standard window structure. The QTextEdit widget is placed in the center of the window with setCentralWidget().

QAction objects define the menu commands. Each action is connected to a method with the signal-slot mechanism:

open_action.triggered.connect(self.open_file)

The file dialog returns two values: the selected file path and the selected filter. In this example, only the path is needed:

file_name, _ = QFileDialog.getOpenFileName(self, "Open File")

If the user cancels the dialog, file_name is empty, so the function returns without doing anything.

Running the Program

Install PyQt5 if it is not already available:

pip install PyQt5

Save the script as text_editor.py, then run:

python text_editor.py

This is still a minimal editor. Useful next steps would be adding unsaved-change warnings, line numbers, recent files, and support for different encodings.

Leave a Reply