Рубрики
Без рубрики

Как построить редактор игрушек Markown с Python и Tkinter

Автор оригинала: Palash Bauri.

Редакторы Markdown являются тенденциями в наши дни. Все создают редактор Markown, а некоторые из них являются инновационными, а некоторые из них скучны.

Однако как для себя, однако, я всегда был фанатом делать вещи, которые не были сделаны другими. (Я объясню ниже, почему другие dev не хотят строить редактор Markown с Tkinter.)

Если вы уже знакомы с Python и Tkinter, вы можете легко попасть в это руководство.

Но если вы только начинаете с Python и/или TKinter, вы можете проверить эти: Учебники Python: TreeCodecamp Python Учебник , Python 3 Playlist от Sentdex , FreeCodecamp Python для начинающих и т. д. (больше можно найти один поиск Google) Учебники Tkinter: Основы Tkinter , FreeCodecamp Tkinter курс , Jetewboston tkinter плейлист и т.д. (больше можно найти один поиск Google)

Итак, прежде чем мы начнем, я хочу объяснить, почему люди не хотят строить редакторов Markdown с Tkinter. Это потому, что нет просто простого способа отображения HTML-вывода ввода Markdown. Нет даже виджета Tkinter по умолчанию для отображения HTML-данных. Вы можете просто просто написать/редактировать markdown, но нет простого способа отображения вывода внутри вашего приложения.

Но однажды, пока я бродил по улицам Интернета, я нашел что-то интересное: tk_html_widgets Отказ Это может отображать выход HTML!

Но о том, что у него были некоторые проблемы: шрифты были слишком маленькими, и у него не было поддержки для прикрепления удаленных фотографий. Поэтому, как обычно, я создал свои собственные вилки и исправил некоторые проблемы, и вроде улучшена стабильность. Я назвал это TKHTMLVIEW Отказ ?

Тьфу, я думаю, я скучаю по тебе?, Так что давайте перестанем говорить и начать здание.

? ️ Начать здание:

Сначала убедитесь, что у вас установлен Python 3 и TKinter. Если нет, вы можете скачать их отсюда: python.org/downloads (Tkinter уже упакован с Python).

Другие вещи, которые нам понадобятся, это TKHTMLVIEW и Markdown2 Отказ Вы можете установить их за помощью PIP Установить tkhtmlview markdown2 или PIP3 Установите TKHTMLVIEW Markdown2 (Если у вас есть несколько версий Python).

Теперь выстреливайте свой любимый редактор или IDE и создайте новый файл (например, TDown.py (Я назвал редактор stdown )). Мы начнем, импортируя необходимые библиотеки.

from tkinter import *
from tkinter import font , filedialog
from markdown2 import Markdown
from tkhtmlview import HTMLLabel

В первой линии мы импортируем все (почти) из пакета Tkinter.

Во второй строке мы импортируем шрифт и filedialog. шрифт Необходимо стиль (например, шрифт, размер шрифта) нашего поля ввода, а filedialog импортируется в открытые файлы markdown для редактирования (и/или для сохранения нашей markdown).

В 3-м линии Markdown импортируется, чтобы помочь нам преобразовать нашу источник разметки в HTML и отображать его в поле вывода, используя HTMLLABEL (который мы импортируем на 4-й строке).

После этого мы создадим кадр класс под названием окно, которое будет унаследовано от Tkinter Рамка класс. Он будет держать наши входные и выходные поля.

class Window(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.master = master
        self.myfont = font.Font(family="Helvetica", size=14)
        self.init_window()

    def init_window(self):
        self.master.title("TDOWN")
        self.pack(fill=BOTH, expand=1)

Здесь, в этом блоке кода, мы сначала определим окно класса, которое наследует класс виджета кадра Tkinter.

Теперь в функции инициализации мы принимаем мастер в качестве аргумента, который будет служить родителем кадра. В следующей строке мы инициализируем кадр.

Далее мы объявляем пользовательский объект шрифта под названием Self.myfont С семьей шрифта Helvetica (Вы можете выбрать любую шрифту, которую вы хотите) и размер 14 который будет использоваться в нашей области ввода Markdown.

Наконец мы называем init_window Функция, где мы поставьте сердце нашего приложения.

В init_window Функция мы впервые установили заголовок окна как Отказ Отказ В следующей строке self.pack (заполнить = оба,) Мы говорим нашему кадру, чтобы взять полное пространство нашего окна.

Мы устанавливаем заполнить Аргумент ключевых слов на Оба который фактически импортируется из библиотеки Tkinter. Он говорит раму заполнить окно как горизонтально, так и вертикально, а также Развернуть Аргумент ключевых слов установлен на 1 (означает True ), который говорит нашему кадру расширяемой. Проще говоря, кадр заполнит окно, независимо от того, как мы растягиваем размер окна или максимизируем его.

Теперь, если вы запустите TDown.py Сценарий, вы ничего не увидите, потому что мы определили только класс, но никогда не называли это.

Чтобы исправить это, мы собираемся поставить это в конце нашего сценария:

root = Tk()
root.geometry("700x600")
app = Window(root)
app.mainloop()

Здесь мы создаем объект TK и хранить его в корневой переменной, которая будет служить корнем нашего окна класса.

Далее мы устанавливаем геометрию нашего окна до 700×600 – 700 – высота, а 600 – ширина окна. В следующей строке вы можете увидеть, что мы создаем окно объекта. Мы нажимаем корень Переменная как корень кадра и хранить его в переменной под названием приложение Отказ

Следующая вещь, которую мы делаем, это просто позвоните в функцию MainLoop, которая говорит нашему приложению! ?

Теперь запустите TDown.py скрипт Вы увидите такое пустое окно, если вы сделали все правильно:

Но это просто пустое окно. Чтобы написать что-то в окне, нам нужно добавить текстовое поле, где мы будем писать нашу уценку. Сделать это, мы собираемся использовать Текст Виджет от Tkinter.

...
def init_window(self):
    self.master.title("TDOWN")
    self.pack(fill=BOTH, expand=1)

    self.inputeditor = Text(self, width="1")
    self.inputeditor.pack(fill=BOTH, expand=1, side=LEFT)

Не путайтесь с (Три точка), я поместил их там только для обозначения того, что в блоке этого кода есть несколько строк кода.

Здесь мы создаем текстовый виджет с шириной 1 Отказ Не царапайте голову – здесь размеры выполняются с использованием соотношений. Вы поймете его более четко в ближайшие несколько секунд, когда мы помещаем в поле вывода. ?

Затем мы упаковываем его в рамку и сообщите, что это как горизонтально, так и вертикально растягиваемым.

Когда вы запускаете скрипт, вы увидите, что поле многослойного ввода заняло все нашими Мир Окно. Если вы начнете в нем написать, вы можете заметить, что символы такие крошечные!

Я уже знал, что эта проблема возникнет. Вот почему я сказал вам ранее создать пользовательский объект шрифта ( Self.myfont ). Теперь, если вы сделаете что-то подобное:

self.inputeditor = Text(self, width="1" , font=self.myfont)

(Здесь мы говорим наш текстовый виджет, чтобы использовать наш пользовательский шрифт вместо по умолчанию Tiny!)

… Размер шрифта поля ввода будет увеличен до 14. Запустите скрипт, чтобы проверить, все отлично работало.

Теперь я думаю, что пришло время добавить выходной ящик, где мы увидим HTML-вывод источника нашей Markdown, пока мы пишем.

Для этого мы собираемся добавить htmllabel, что-то вроде этого внутри init_window Функция:

self.outputbox = HTMLLabel(self, width="1", background="white", html="

Welcome

") self.outputbox.pack(fill=BOTH, expand=1, side=RIGHT) self.outputbox.fit_height()

Мы используем HTMLLABEL от TKHTMLVIEW с шириной 1 (очередной раз). Мы устанавливаем ширину до 1, потому что окно будет передано между полевым полем ввода и выводом с соотношением 1: 1 (Вы поймете, что я имею в виду, когда вы запускаете скрипт).

HTML Ключевое слово Аргумент хранит значение, которое будет показано в первый раз.

Затем мы упаковываем его в окно с сторона как Правильно Чтобы положить его в правую сторону входного поля. fit_height () Заставляет тексты вписываться внутри виджета (насколько я знаю …?)

Теперь запустите код.

Теперь, если вы начнете писать в поле ввода, вы можете быть разочарованы (но не будь!) Чтобы увидеть, что вывод не обновляется, так как мы вводите. Это потому, что мы не сказали нашей программе сделать это еще.

Для этого мы сначала связываем событие с нашим редактором. Затем всякий раз, когда текст изменен, вывод будет обновлен, что-то вроде этого:

self.inputeditor.bind("<>", self.onInputChange)

Положите эту строку внутри функции init_window ().

Так что в основном эта линия говорит Inputitor позвонить в OninPutchange Функция всякий раз, когда текст изменен. Но, поскольку у нас еще нет этой функции, нам нужно написать это.

...
def onInputChange(self , event):
    self.inputeditor.edit_modified(0)
    md2html = Markdown()
    self.outputbox.set_html(md2html.convert(self.inputeditor.get("1.0" , END)))

В первой строке, используя edit_modified (0) Мы сбрасываем модифицированный флаг, чтобы его можно было повторно использовать. В противном случае после первого звонка событий он больше не будет работать.

Далее мы создаем объект Markdown с именем MD2HTML. На последней строке, где сначала мы …. подожди! Последняя строка может быть запутана для некоторых читателей. Итак, позвольте мне сломать его на три линии.

markdownText = self.inputeditor.get("1.0" , END)
html = md2html.convert(markdownText)
self.outputbox.set_html(html)

Здесь, в первой строке, мы принесете текст разметки сверху до нижней части входного поля. Первый аргумент, Self.inputeditor.get , говорит ему, чтобы начать сканирование с 0-го символа первой линии (1.0 => [Line_number]. [CharAnce_Number]), и последний аргумент сообщает ему, чтобы остановить сканирование, которое он имеет при достижении конца.

Затем мы конвертируем отсканированную отметку текста на HTML, используя md2html.Convert () Функция и хранить его в HTML Переменная.

Наконец, мы скажем выводки для отображения вывода, используя .set_html () Функция!

Запустите скрипт. Вы увидите функционирующую (почти) редактор Markowndown. Как вы вводите в поле ввода, вывод также будет обновлен!

Но … наша работа еще не закончена. Пользователи должны быть в состоянии хотя бы открыть и сохранить свой текст.

Для этого мы собираемся добавить Файл Меню в меню. Именно здесь пользователи смогут открывать и сохранять файлы, а также выйти из приложения.

В init_window Функция мы добавим эти строки:

self.mainmenu = Menu(self)
self.filemenu = Menu(self.mainmenu)
self.filemenu.add_command(label="Open", command=self.openfile)
self.filemenu.add_command(label="Save as", command=self.savefile)
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.quit)
self.mainmenu.add_cascade(label="File", menu=self.filemenu)
self.master.config(menu=self.mainmenu)

Я сделаю это быстрым:

Здесь мы определяем новое меню с рамкой как его родитель.

Далее мы определяем другое меню и предыдущее меню как его родитель. Это будет служить нашим Файл меню.

Затем мы добавляем 3-х меню (открыть, сохранить как и выйти) и сепаратор, используя add_command () и add_separator () Функции. Открыть Подменю будет выполнять OpenFile Функция, Сохранить как Подменю будет выполнять SaveFile функция. И наконец Выход будет выполнять встроенную функцию выйти который закроет программу.

Затем используя add_cascade () Функция мы говорим, что первый объект меню включает Филемену Переменная. Это включает в себя все наши подруги с этикеткой Файл Отказ

Наконец мы используем Self.master.config () Чтобы сказать наше окно для использования Mainmenu как меню нашего окна.

Это будет выглядеть что-то подобное, но пока не беги. Вы получите ошибки, говорящие, что OpenFile & SaveFile Функции не определены.

Как вы можете увидеть сейчас, мы должны определить две функции внутри класса Window, где мы будем использовать Tkinter Filedialog.

Сначала давайте определим функцию, чтобы открыть файлы:

def openfile(self):
    openfilename = filedialog.askopenfilename(filetypes=(("Markdown File", "*.md , *.mdown , *.markdown"),
                                                                  ("Text File", "*.txt"), 
                                                                  ("All Files", "*.*")))
    if openfilename:
        try:
            self.inputeditor.delete(1.0, END)
            self.inputeditor.insert(END , open(openfilename).read())
        except:
            print("Cannot Open File!")     

Здесь, сначала мы показываем пользователю диалоговое окно браузера файлов, которое позволяет им выбрать файл для открытия filedialog.askOpenfilename () Отказ С FileTypes Аргумент ключевых слов Мы говорим об этом диалоге, чтобы открыть только эти типы файлов, передавая кортеж с поддерживаемыми файлами (в основном все типы файлов):

  • Файлы разметки с .md , .mdown , .Markdown Расширения,
  • Текстовые файлы с .txt расширение,
  • А в следующем ряду используют расширение подстановки, мы сообщим диалогом открывать файлы с любым расширением.

Затем мы проверяем, выбрал ли пользователь файл или нет. Если да, мы пытаемся открыть файл. Затем мы удаляем весь текст внутри поля ввода из 0-го символа первой строки до конца поля.

Далее мы открываем и прочитаем содержимое выбранного файла и вставьте содержимое в поле ввода.

Если наша программа не может открыть файл, он распечатает ошибку. Но подожди, это не хороший способ обрабатывать ошибки. Что мы можем сделать, здесь отображается сообщение об ошибке пользователю, которое выглядит так:

Сделать это, мы сначала импортируем Messagebox из пакета Tkinter.

from tkinter import messagebox as mbox

Тогда вместо того, чтобы просто печатать сообщение об ошибке, как мы сделали выше, мы заменим эту строку ниже, чтобы показать правильное сообщение об ошибке для пользователя.

mbox.showerror("Error Opening Selected File" , "Oops!, The file you selected : {} can not be opened!".format(openfilename))

Это создаст сообщение об ошибке, как приведенный выше скриншот, я показал вам, когда файл не может быть открыт.

В Mbox.showerror Функция, первый аргумент – название почтового ящика. Второе – это сообщение для отображения.

Теперь нам нужно написать SaveFile Функция, чтобы сохранить нашу входную информацию.

def savefile(self):
        filedata = self.inputeditor.get("1.0" , END)
        savefilename = filedialog.asksaveasfilename(filetypes = (("Markdown File", "*.md"),
                                                                  ("Text File", "*.txt")) , title="Save Markdown File")
        if savefilename:
            try:
                f = open(savefilename , "w")
                f.write(filedata)
            except:
                mbox.showerror("Error Saving File" , "Oops!, The File : {} can not be saved!".format(savefilename))

Вот сначала мы сканируем все содержимое поля ввода и храните его в переменной. Затем мы просим пользователя файла имени файла, где они хотят сохранить содержимое, предоставив опцию для двух типов типов файлов (.MD и .txt).

Если пользователь выбирает имя файла, мы пытаемся сохранить содержимое поля ввода, хранящегося в переменной Filedata Отказ Если возникает исключение, мы показываем пользователю сообщение об ошибке, указав, что программа не смогла сохранить файл.

Не забудьте проверить ваше приложение, чтобы проверить наличие ошибок! Если вы и я и я не сделал никаких ошибок, наши программы должны работать идеально и торгуют что-то подобное:

Полный исходный код для этого «TDOND» редактор Markown доступен в Гадость а также в Repl.it Где вы можете проверить редактор в вашем браузере!

Если вы попадаете в какие-либо проблемы, когда вы проходите через эту статью, вы можете дать мне знать в комментариях или в ДМ мне в Twitter на @Bauripalash Отказ

Некоторые заметки:

  • Сначала помните, что это просто игрушечный редактор. Если вы хотите построить более мощный редактор, вы можете использовать любые другие библиотеки GUI, такие как wxpython, pyqt, kivy и многие другие, которые, по крайней мере, имеют лучшую поддержку HTML ( Полный список ).

  • В этой статье я только показал, как построить Базовый редактор. Вы также можете добавить еще много классных функций, таких как экспортировка в качестве HTML или PDF, добавление кнопок для упрощения начисления записи … etc и т. Д.

  • Модули HTML-рендеринга TKHTMLVIEW или TK_HTML_WIDGETS не являются полностью стабильными и поддерживают только некоторые основные функциональные возможности HTML, поэтому больше не ожидайте.

Итак … Я надеюсь, что вы наслаждались этой статьей и узнали некоторые новые вещи. Не забудьте дать мне знать, если вы застряли где-то или не можете что-то понять.

Последнее, но не менее важное, пожалуйста, дайте мне знать, если бы я сделал какие-либо ошибки выше. И я хотел бы услышать ваши идеи или предложения через комментарии или DM.

Спасибо. ?