Markdown编辑器大家应该都知道,在程序员中很受欢迎。很多人都在创建Markdown编辑器,有些人很有创意,有些人则很无聊。虽然许多开发人员不想使用Tkinter构建Markdown编辑器,但如果您已经熟悉Python和Tkinter,则可以轻松跳转到本指南。在我们开始之前,让我们解释一下为什么人们不想使用tkinter来构建Markdown编辑器。这是因为没有默认的简单方法来显示markdown输入的html数据。甚至没有默认的tkinter组件来显示html数据。您可以简单地编写/编辑markdown,但没有简单的方法在应用程序中显示输出。然而,现在有了tk_html_widgets,它可以帮助我们显示html输出。现在让我们开始构建。开始构建:首先,确保安装了Python3和Tkinter。如果没有,您可以从这里下载它:python.org/downloads(Tkinter包含在Python中)。我们需要的其他东西是tkhtmlview和markdown2。您可以通过运行pipinstalltkhtmlviewmarkdown2或pip3installtkhtmlviewmarkdown2(如果您有多个Python版本)来安装它们。现在启动您最喜欢的编辑器或IDE并创建一个新文件(例如www.linuxidc.com.py(我将其命名为linuxidc.comeditor))。我们将从导入必要的库开始。fromtkinterimport*fromtkinterimportfont,filedialogfrommarkdown2importMarkdownfromtkhtmlviewimportHTMLLabel在第一行中,我们从tkinter包中导入(几乎)所有内容。在第二行中,我们导入字体和文件对话框。需要字体来设置输入字段的样式(例如字体、字体大小),并导入文件对话框以打开降价文件进行编辑(和/或保存我们的降价文件)。第三行导入Markdown,帮助我们将Markdown源码转为html,并使用HTMLLabel(第四行导入)显示在输出域中。之后,我们将创建一个名为Window的框架类,它将继承自tkinters的Frame类。它将保存我们的输入和输出字段。classWindow(Frame):def__init__(self,master=None):Frame.__init__(self,master)self.master=masterself.myfont=font.Font(family="Helvetica",size=14)self.init_window()definit_window(self):self.master.title("linuxidc.comeditor")self.pack(fill=BOTH,expand=1)在这个代码块中,我们首先定义了一个名为Window的类,它继承了tkinter的Framewidget类。现在,在初始化函数中,我们将master作为参数传递,用作框架的父级。在下一行中,我们初始化一个框架。接下来,我们声明一个名为self.myfont的自定义字体对象,其字体系列为Helvetica(您可以选择任何字体系列),大小为15,将在我们的降价输入字段中使用。最后,我们调用init_window函数,将我们的应用程序带到核心。在init_window函数中,我们首先将窗口的标题设置为linuxidc.com编辑器。在下一行self.pack(fill=BOTH,expand=1)中,我们告诉Frame占据窗口的全部空间。我们将fill关键字参数设置为BOTH,这实际上是从tkinter库中导入的。它告诉框架在水平和垂直方向都填充窗口,并且expand关键字参数设置为1(表示True),这告诉我们框架是可扩展的。简而言之,无论我们如何拉伸或最大化窗口大小,框架都会填满窗口。现在,如果你运行www.linuxidc.com.py脚本,你将看不到任何东西,因为我们只定义了类,但从未调用它。为了解决这个问题,我们将以下代码放在脚本的末尾:root=Tk()root.geometry("800x600")app=Window(root)app.mainloop()接下来,将窗口的几何形状设置为800x600长方体,800是窗口的高度,600是窗口的宽度。在下一行中,您可以看到我们正在创建一个Window对象。我们将root变量压入框架的根,并将其存储在名为app的变量中。接下来要做的是调用mainloop函数,它告诉我们的应用程序运行!现在运行www.linuxidc.com.py脚本。如果一切都正确,您会看到一个像这样的空白窗口:但它只是一个空白窗口。要在窗口中写东西,我们需要添加一个文本字段来写我们的标记。为此,我们将使用来自tkinter的文本小部件。...definit_window(self):self.master.title("linuxidc.comeditor")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)(在这里,我们告诉文本小部件使??用自定义字体而不是默认的小字体!)。..输入字段的字体大小将增加到15。运行脚本以检查是否一切正常。现在,我认为是时候添加输出框了,我们将在编写时看到markdown源代码的html输出。为此,我们需要在init_window函数中添加一个HTMLLabel,如下所示:self.outputbox=HTMLLabel(self,width="1",background="white",html="
linuxidc.com
")self.outputbox.pack(fill=BOTH,expand=1,side=RIGHT)self.outputbox.fit_height()我们使用tkhtmlview中的HTMLLabel,宽度还是1,我们设置宽度为1是因为窗口将在输入字段和输出框之间以1:1的比例共享(您将在运行脚本时明白我的意思)。html关键字参数存储第一次显示的值。然后,将其包裹在窗口中,将side作为RIGHT放在输入字段的右侧。fit_height()使文本适合小部件。现在按如下方式运行代码:现在,如果您开始在输入字段中书写,输入时输出将不会更新。那是因为我们没有告诉我们的程序这样做。为此,我们首先将一个事件绑定到编辑器。然后,当您修改文本时,输出将更新,如下所示:self.inputeditor.bind("<
>",self.onInputChange)将这一行放在init_window()函数中。此行告诉输入编辑器在文本更改时调用onInputChange函数。但是因为我们还没有那个函数,所以我们需要把它写出来。...defonInputChange(self,event):self.inputeditor.edit_modified(0)md2html=Markdown()self.outputbox.set_html(md2html.convert(self.inputeditor.get("1.0",END)))第一行,我们使用edit_modified(0)重置修改后的标志,以便我们可以重用它。否则,在第一次事件调用之后,它将不再起作用。接下来,我们创建一个名为md2html的Markdown对象。最后一行(上面用红色标记的那一行),首先我们......等待!最后一行可能会让一些读者感到困惑。我把它分成三行。markdownText=self.inputeditor.get("1.0",END)html=md2html.convert(markdownText)self.outputbox.set_html(html)在第一行中,我们从输入字段的顶部到底部获取降价文本。第一个参数self.inputeditor.get告诉它从第一行的字符0开始扫描(1.0=>[LINE_NUMBER].[CHARACTER_NUMBER]),最后一个参数告诉它在到达末尾时停止扫描。然后,我们使用md2html.convert()函数将扫描的markdown文本转换为html,并将其存储在html变量中。最后,我们告诉outputbox使用.set_html()函数来显示输出!运行脚本。你会看到一个几乎可以正常运行的降价编辑器。当您在输入字段中键入内容时,输出也会更新。但是……我们的工作还没有完成。用户至少需要能够打开和保存他们的文本。为此,我们将向菜单栏添加一个文件菜单。从这里,用户可以打开和保存文件,也可以退出应用程序。在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="另存为",command=self.savefile)self.filemenu.add_separator()self.filemenu.add_command(label="退出",command=self.quit)self.mainmenu.add_cascade(label="File",menu=self.filemenu)self.master.config(menu=self.mainmenu)简而言之:在这里,我们定义了一个新菜单,框架作为其父菜单。接下来,我们定义另一个菜单和上一个菜单作为它的父菜单。它将作为我们的文件菜单。然后使用add_command()和add_separator()函数添加3个子菜单(打开、另存为和退出)和一个分隔符。打开子菜单执行打开文件功能,另存为子菜单执行保存文件功能。最后,Exit将执行内置函数quit,该函数将关闭程序。然后使用add_cascade()函数告诉第一个菜单对象包含filemenu变量。这包括标记文件中的所有子菜单。最后,我们使用self.master.config()告诉窗口使用主菜单作为窗口的菜单栏。它看起来像这样,但暂时不要运行它。会提示错误,openfile和savefile函数没有定义。正如您现在看到的,我们必须在Window类中定义两个函数,我们将在其中使用tkinter的文件对话框。首先让我们定义打开文件的函数:defopenfile(self):openfilename=filedialog.askopenfilename(filetypes=(("MarkdownFile","*.md,*.mdown,*.markdown"),("TextFile","*.txt"),("AllFiles","*.*")))ifopenfilename:try:self.inputeditor.delete(1.0,END)self.inputeditor.insert(END,open(openfilename).read())except:print("Couldnotopenfile!")在这里,我们首先向用户展示一个文件浏览器对话框,允许他们使用filedialog.askopenfilename()选择要打开的文件。使用filetypes关键字参数,我们通过传递包含受支持文件(基本上所有类型的文件)的元组来告诉对话框仅打开这些类型的文件:具有.md、.mdown、.markdown扩展名的文件具有扩展名文本文件。txt在带有通配符扩展的下一行,我们告诉对话框打开任何扩展名的文件。然后我们检查用户是否选择了一个文件。如果是,我们尝试打开文件。然后删除输入域中从第一行第0个字符到域末的所有文本。接下来,我们打开并读取所选文件的内容,并将内容插入到输入字段中。如果我们的程序无法打开文件,它将打印错误。但是等等,这不是处理错误的好方法。我们在这里可以做的是像这样向用户显示一条错误消息:为此,我们首先从tkinter包中导入messageboxmessagebox。fromtkinterimportmessageboxasmbox然后,我们将用下面的行替换该行,而不是像上面那样只打印错误消息,以便向用户显示正确的错误消息。mbox.showerror("Erroropeningselectedfile","Oops!,thefileyouselected:{}cannotbeopened!".format(openfilename))这将产生一条错误消息,就像我上面显示的文件无法打开一样当时的画面。mbox.showerror函数,第一个参数是消息框的标题。第二个是要显示的消息。现在,我们需要编写一个savefile函数来保存markdown输入。defsavefile(self):filedata=self.inputeditor.get("1.0",END)savefilename=filedialog.asksaveasfilename(filetypes=(("MarkdownFile","*.md"),("TextFile","*.txt")),title="保存Markdown文件")ifsavefilename:try:f=open(savefilename,"w")f.write(filedata)except:mbox.showerror("错误保存文件","糟糕!,file:{}Saveerror!".format(savefilename))在这里,我们首先扫描输入字段的所有内容并将其存储在一个变量中。然后,我们传入两种文件类型(.md和.txt)。如果用户选择了一个文件名,我们将尝试将输入字段的内容保存在变量filedata中。如果发生异常,我们将向用户显示一条错误消息,说明程序无法保存文件。不要忘记测试您的应用程序以检查是否有任何错误!如果你的程序没有错误并且运行完美,它应该是这样的:OK,本文就到此为止,你学会了吗?