Создаем текстовый редактор с помощью Python и библиотек GUI

Раздел: GUI -> Разработка приложений

Разработка собственного текстового редактора позволяет лучше понять принципы работы GUI приложений и адаптировать инструмент под конкретные задачи. В этой части рассмотрены несколько подходов с использованием различных библиотек Python.

Создание текстового редактора на Python

Как создать простой редактор на Tkinter?

Tkinter входит в стандартную поставку Python, поэтому не требует установки. Это оптимальный выбор для быстрого прототипирования. Основные компоненты: окно, текстовое поле, меню.


import tkinter as tk
from tkinter import filedialog, messagebox

def open_file():
    file_path = filedialog.askopenfilename()
    if file_path:
        with open(file_path, 'r', encoding='utf-8') as f:
            text.delete('1.0', tk.END)
            text.insert(tk.END, f.read())

def save_file():
    file_path = filedialog.asksaveasfilename(defaultextension=".txt")
    if file_path:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(text.get('1.0', tk.END))

root = tk.Tk()
root.title("Простой редактор")
menu = tk.Menu(root)
root.config(menu=menu)
file_menu = tk.Menu(menu, tearoff=0)
menu.add_cascade(label="Файл", menu=file_menu)
file_menu.add_command(label="Открыть", command=open_file)
file_menu.add_command(label="Сохранить", command=save_file)
file_menu.add_separator()
file_menu.add_command(label="Выход", command=root.quit)
text = tk.Text(root, wrap='word', undo=True)
text.pack(fill=tk.BOTH, expand=True)
root.mainloop()

Python написать редактор (создание редактора на python)

Этот код создаёт окно с текстовым полем и меню. Функции open_file и save_file реализуют диалоги открытия и сохранения. Важно указывать кодировку utf-8 для корректной работы с русским текстом.

Типичные проблемы: отсутствие обработки исключений при чтении/записи, некорректная работа с большими файлами, отсутствие проверки изменений перед закрытием. Решение: добавить try-except, использовать threading для загрузки, реализовать отслеживание изменений.

Как добавить подсветку синтаксиса в редактор на Tkinter?

Если требуется редактор кода, можно использовать теги Text для раскраски ключевых слов. Ниже приведён пример для подсветки синтаксиса Python.


import tkinter as tk
import re

root = tk.Tk()
text = tk.Text(root)
text.pack(fill=tk.BOTH, expand=True)

def highlight(event=None):
    text.tag_remove("keyword", "1.0", tk.END)
    keywords = ["def", "class", "if", "else", "for", "while", "import", "from"]
    for kw in keywords:
        start = "1.0"
        while True:
            pos = text.search(kw, start, tk.END)
            if not pos:
                break
            end = f"{pos}+{len(kw)}c"
            text.tag_add("keyword", pos, end)
            start = end
    text.tag_config("keyword", foreground="blue", font=("Courier New", 10, "bold"))

text.bind("", highlight)
root.mainloop()

После каждого ввода символа функция highlight ищет ключевые слова и присваивает им тег с синим цветом. Для больших файлов это может замедлить работу, поэтому рекомендуется использовать advanced синтаксические анализаторы.

Проблемы: производительность при частом обновлении, нет поддержки многострочных конструкций. Решение: использовать библиотеку Pygments с предварительной разборкой.

Как реализовать редактор на PyQt5 с нумерацией строк?

PyQt5 предоставляет мощный виджет QPlainTextEdit, а также QScintilla для подсветки. Для нумерации строк можно subclass QPlainTextEdit.


import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QWidget, QVBoxLayout, QLabel
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtCore import Qt

class LineNumberArea(QWidget):
    def __init__(self, editor):
        super().__init__(editor)
        self.editor = editor

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(event.rect(), QColor(240, 240, 240))
        block = self.editor.firstVisibleBlock()
        lineNumber = block.blockNumber()
        top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
        while block.isValid() and top <= event.rect().bottom():
            if block.isVisible():
                number = str(lineNumber + 1)
                painter.setPen(Qt.black)
                painter.drawText(0, int(top), self.width(), self.editor.fontMetrics().height(), Qt.AlignRight, number)
            block = block.next()
            top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
            lineNumber += 1

class CodeEditor(QTextEdit):
    def __init__(self):
        super().__init__()
        self.lineNumberArea = LineNumberArea(self)
        self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
        self.updateRequest.connect(self.updateLineNumberArea)
        self.updateLineNumberAreaWidth()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        cr = self.contentsRect()
        self.lineNumberArea.setGeometry(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height())

    def lineNumberAreaWidth(self):
        return 30

    def updateLineNumberAreaWidth(self):
        self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)

    def updateLineNumberArea(self, rect, dy):
        if dy:
            self.lineNumberArea.scroll(0, dy)
        else:
            self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())

app = QApplication(sys.argv)
window = QMainWindow()
editor = CodeEditor()
window.setCentralWidget(editor)
window.show()
sys.exit(app.exec_())

Этот код отображает нумерацию строк слева от текста. Для реального редактора стоит добавить меню, открытие/сохранение и подсветку синтаксиса через QScintilla.

Установка PyQt5 может вызвать сложности (pip install pyqt5). Также лицензия GPL ограничивает коммерческое использование без покупки коммерческой лицензии.

Как сделать редактор для мобильных устройств на Kivy?

Kivy ориентирован на кроссплатформенные приложения с сенсорным управлением. Основной виджет TextInput поддерживает многострочный текст.


from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.core.window import Window

class EditorApp(App):
    def build(self):
        layout = BoxLayout(orientation='vertical')
        self.text_input = TextInput(font_size=20)
        layout.add_widget(self.text_input)
        btn_layout = BoxLayout(size_hint_y=0.1)
        btn_open = Button(text='Открыть')
        btn_save = Button(text='Сохранить')
        btn_layout.add_widget(btn_open)
        btn_layout.add_widget(btn_save)
        layout.add_widget(btn_layout)
        return layout

if __name__ == '__main__':
    EditorApp().run()

Этот пример создаёт интерфейс с текстовым полем и кнопками. Для открытия/сохранения файлов на мобильных устройствах потребуется использовать Storage API или диалоги Kivy.

Основная проблема Kivy - его приложения выглядят не нативно, и для работы с файлами нужны дополнительные библиотеки (plyer). Также сообщество меньше, чем у Tkinter или PyQt.

Расширенные примеры

Рассмотрим более сложные реализации редакторов с дополнительными возможностями.

Пример 1: Редактор с закладками и поиском на Tkinter

Добавление функционала поиска и замены, а также закладок (bookmarks) для быстрого перехода по файлу.

Пример

import tkinter as tk
from tkinter import scrolledtext

class EditorWithSearch:
    def __init__(self, root):
        self.root = root
        self.text = scrolledtext.ScrolledText(root, wrap='word', undo=True)
        self.text.pack(fill=tk.BOTH, expand=True)
        self.create_menu()
        self.search_var = tk.StringVar()

    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        edit_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Правка", menu=edit_menu)
        edit_menu.add_command(label="Найти", command=self.open_search_dialog)
        search_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Поиск", menu=search_menu)

    def open_search_dialog(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("Поиск")
        tk.Label(dialog, text="Искать:").grid(row=0, column=0)
        entry = tk.Entry(dialog, textvariable=self.search_var)
        entry.grid(row=0, column=1)
        tk.Button(dialog, text="Найти далее", command=self.find_next).grid(row=1, column=0, columnspan=2)

    def find_next(self):
        self.text.tag_remove("found", "1.0", tk.END)
        query = self.search_var.get()
        if query:
            idx = "1.0"
            while True:
                idx = self.text.search(query, idx, nocase=1, stopindex=tk.END)
                if not idx:
                    break
                end_idx = f"{idx}+{len(query)}c"
                self.text.tag_add("found", idx, end_idx)
                idx = end_idx
            self.text.tag_config("found", background="yellow")

root = tk.Tk()
app = EditorWithSearch(root)
root.mainloop()
Результат: главное окно с текстовым полем, при выборе пункта меню "Найти" открывается диалог, после ввода и нажатия кнопки все найденные совпадения подсвечиваются жёлтым.

Пример 2: Полноценный редактор кода на PyQt5 с QScintilla

QScintilla предоставляет подсветку синтаксиса, автодополнение и нумерацию строк "из коробки". Ниже пример интеграции.

Пример

from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.Qsci import QsciScintilla, QsciLexerPython
import sys

class CodeEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.editor = QsciScintilla()
        self.setCentralWidget(self.editor)
        lexer = QsciLexerPython()
        self.editor.setLexer(lexer)
        self.editor.setMarginWidth(0, 30)
        self.editor.setMarginLineNumbers(0, True)
        self.editor.setFolding(QsciScintilla.BoxedFoldStyle)
        self.setWindowTitle("Редактор кода")

app = QApplication(sys.argv)
window = CodeEditor()
window.show()
sys.exit(app.exec_())
Результат: окно с подсветкой синтаксиса Python, номера строк, сворачивание блоков кода. Диалоги открытия/сохранения можно добавить через QFileDialog.

Создание редактора на Python - comments

En
Python написать редактор (python)