20
Make a simple text editor using Python
Today we'll be making a simple text editor using Python and Tkinter.
python -m tkinter
in your console. If it doesn't give an error, it means you have tkinter installed and ready to use!pip install tk
in the console.Now that that's out of the way, let's get started!!!
First, we'll make a file structure sort of thing.
We'll need these files (they can be in the root directory):
We'll need these files (they can be in the root directory):
main.py
file_menu.py
edit_menu.py
format_menu.py
help_menu.py
For the
main.py
file, we'll start by importing the tkinter library and the menu files we made just now:# import statements
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
from tkinter.font import Font
from tkinter.scrolledtext import *
import file_menu
import edit_menu
import format_menu
import help_menu
Then, we'll make a tkinter window like this:
# creating a tkinter window
root = Tk()
Then, we'll give our text editor some dimensions and a title like this:
# gives the window a title and dimensions
root.title("TextEditor-newfile")
root.geometry("300x250+300+300")
root.minsize(width=400, height=400)
Then, we'll add this code:
# i don't really know how to explain what this code does, but yeah it's important
text = ScrolledText(root, state='normal', height=400, width=400, wrap='word', pady=2, padx=3, undo=True)
text.pack(fill=Y, expand=1)
text.focus_set()
Then, we'll make a
menubar
:# creating a menubar
menubar = Menu(root)
Then, we'll add our menus to the menubar:
# adding our menus to the menubar
file_menu.main(root, text, menubar)
edit_menu.main(root, text, menubar)
format_menu.main(root, text, menubar)
help_menu.main(root, text, menubar)
Finally, we'll add this code so this editor actually functions:
# running the whole program
root.mainloop()
Let's move on to our menu files, shall we?
First, we'll import stuff:
# import statements
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
Then, we'll make a class
File
:# creating File
class File():
Then we'll add all the functions we need to our
File
class:def newFile(self):
self.filename = "Untitled"
self.text.delete(0.0, END)
def saveFile(self):
try:
t = self.text.get(0.0, END)
f = open(self.filename, 'w')
f.write(t)
f.close()
except:
self.saveAs()
def saveAs(self):
f = asksaveasfile(mode='w', defaultextension='.txt')
t = self.text.get(0.0, END)
try:
f.write(t.rstrip())
except:
showerror(title="Oops!", message="Unable to save file...")
def openFile(self):
f = askopenfile(mode='r')
self.filename = f.name
t = f.read()
self.text.delete(0.0, END)
self.text.insert(0.0, t)
def quit(self):
entry = askyesno(title="Quit", message="Are you sure you want to quit?")
if entry == True:
self.root.destroy()
def __init__(self, text, root):
self.filename = None
self.text = text
self.root = root
In the above code, we've added some functions:
Then, we'll make a main function (note that this function should not be under the
File
class):def main(root, text, menubar):
filemenu = Menu(menubar)
objFile = File(text, root)
filemenu.add_command(label="New", command=objFile.newFile)
filemenu.add_command(label="Open", command=objFile.openFile)
filemenu.add_command(label="Save", command=objFile.saveFile)
filemenu.add_command(label="Save As...", command=objFile.saveAs)
filemenu.add_separator()
filemenu.add_command(label="Quit", command=objFile.quit)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)
In the above code, we made a
main
function which basically executes all functions under the File
class.Finally, we add some error-handling sort of thingy:
if __name__ == "__main__":
print("Please run 'main.py'")
We'll start by importing stuff:
# import statements
from tkinter import *
from tkinter.simpledialog import *
from tkinter.filedialog import *
from tkinter.messagebox import *
Then, we'll make a class
Edit
:# creating Edit
class Edit():
Then we'll add all the necessary functions under this
Edit
class:def popup(self, event):
self.rightClick.post(event.x_root, event.y_root)
def copy(self, *args):
sel = self.text.selection_get()
self.clipboard = sel
def cut(self, *args):
sel = self.text.selection_get()
self.clipboard = sel
self.text.delete(SEL_FIRST, SEL_LAST)
def paste(self, *args):
self.text.insert(INSERT, self.clipboard)
def selectAll(self, *args):
self.text.tag_add(SEL, "1.0", END)
self.text.mark_set(0.0, END)
self.text.see(INSERT)
def undo(self, *args):
self.text.edit_undo()
def redo(self, *args):
self.text.edit_redo()
def find(self, *args):
self.text.tag_remove('found', '1.0', END)
target = askstring('Find', 'Search String:')
if target:
idx = '1.0'
while 1:
idx = self.text.search(target, idx, nocase=1, stopindex=END)
if not idx: break
lastidx = '%s+%dc' % (idx, len(target))
self.text.tag_add('found', idx, lastidx)
idx = lastidx
self.text.tag_config('found', foreground='white', background='blue')
def __init__(self, text, root):
self.clipboard = None
self.text = text
self.rightClick = Menu(root)
In the above code, we make some functions:
Then, we make a
main
function outside the Edit
class:def main(root, text, menubar):
objEdit = Edit(text, root)
editmenu = Menu(menubar)
editmenu.add_command(label="Copy", command=objEdit.copy, accelerator="Ctrl+C")
editmenu.add_command(label="Cut", command=objEdit.cut, accelerator="Ctrl+X")
editmenu.add_command(label="Paste", command=objEdit.paste, accelerator="Ctrl+V")
editmenu.add_command(label="Undo", command=objEdit.undo, accelerator="Ctrl+Z")
editmenu.add_command(label="Redo", command=objEdit.redo, accelerator="Ctrl+Y")
editmenu.add_command(label="Find", command=objEdit.find, accelerator="Ctrl+F")
editmenu.add_separator()
editmenu.add_command(label="Select All", command=objEdit.selectAll, accelerator="Ctrl+A")
menubar.add_cascade(label="Edit", menu=editmenu)
root.bind_all("<Control-z>", objEdit.undo)
root.bind_all("<Control-y>", objEdit.redo)
root.bind_all("<Control-f>", objEdit.find)
root.bind_all("Control-a", objEdit.selectAll)
objEdit.rightClick.add_command(label="Copy", command=objEdit.copy)
objEdit.rightClick.add_command(label="Cut", command=objEdit.cut)
objEdit.rightClick.add_command(label="Paste", command=objEdit.paste)
objEdit.rightClick.add_separator()
objEdit.rightClick.add_command(label="Select All", command=objEdit.selectAll)
objEdit.rightClick.bind("<Control-q>", objEdit.selectAll)
text.bind("<Button-3>", objEdit.popup)
root.config(menu=menubar)
Finally, we'll do some error-handling thingy:
if __name__ == "__main__":
print("Please run 'main.py'")
We'll start by importing stuff:
# import statements
from tkinter import *
from tkinter.colorchooser import askcolor
from tkinter.font import Font, families
from tkinter.scrolledtext import *
import time
Then, we make a class
Format
:class Format():
Then, we add the necessary functions:
def __init__(self, text):
self.text = text
def changeBg(self):
(triple, hexstr) = askcolor()
if hexstr:
self.text.config(bg=hexstr)
def changeFg(self):
(triple, hexstr) = askcolor()
if hexstr:
self.text.config(fg=hexstr)
def bold(self, *args): # Works only if text is selected
try:
current_tags = self.text.tag_names("sel.first")
if "bold" in current_tags:
self.text.tag_remove("bold", "sel.first", "sel.last")
else:
self.text.tag_add("bold", "sel.first", "sel.last")
bold_font = Font(self.text, self.text.cget("font"))
bold_font.configure(weight="bold")
self.text.tag_configure("bold", font=bold_font)
except:
pass
def italic(self, *args): # Works only if text is selected
try:
current_tags = self.text.tag_names("sel.first")
if "italic" in current_tags:
self.text.tag_remove("italic", "sel.first", "sel.last")
else:
self.text.tag_add("italic", "sel.first", "sel.last")
italic_font = Font(self.text, self.text.cget("font"))
italic_font.configure(slant="italic")
self.text.tag_configure("italic", font=italic_font)
except:
pass
def underline(self, *args): # Works only if text is selected
try:
current_tags = self.text.tag_names("sel.first")
if "underline" in current_tags:
self.text.tag_remove("underline", "sel.first", "sel.last")
else:
self.text.tag_add("underline", "sel.first", "sel.last")
underline_font = Font(self.text, self.text.cget("font"))
underline_font.configure(underline=1)
self.text.tag_configure("underline", font=underline_font)
except:
pass
def overstrike(self, *args): # Works only if text is selected
try:
current_tags = self.text.tag_names("sel.first")
if "overstrike" in current_tags:
self.text.tag_remove("overstrike", "sel.first", "sel.last")
else:
self.text.tag_add("overstrike", "sel.first", "sel.last")
overstrike_font = Font(self.text, self.text.cget("font"))
overstrike_font.configure(overstrike=1)
self.text.tag_configure("overstrike", font=overstrike_font)
except:
pass
def addDate(self):
full_date = time.localtime()
day = str(full_date.tm_mday)
month = str(full_date.tm_mon)
year = str(full_date.tm_year)
date = day + '/' + month + '/' + year
self.text.insert(INSERT, date, "a")
In the above code, we added some functions:
Then, we make a
main
function:def main(root, text, menubar):
objFormat = Format(text)
fontoptions = families(root)
font = Font(family="Arial", size=10)
text.configure(font=font)
formatMenu = Menu(menubar)
fsubmenu = Menu(formatMenu, tearoff=0)
ssubmenu = Menu(formatMenu, tearoff=0)
for option in fontoptions:
fsubmenu.add_command(label=option, command=lambda option=option: font.configure(family=option))
for value in range(1, 31):
ssubmenu.add_command(label=str(value), command=lambda value=value: font.configure(size=value))
formatMenu.add_command(label="Change Background", command=objFormat.changeBg)
formatMenu.add_command(label="Change Font Color", command=objFormat.changeFg)
formatMenu.add_cascade(label="Font", underline=0, menu=fsubmenu)
formatMenu.add_cascade(label="Size", underline=0, menu=ssubmenu)
formatMenu.add_command(label="Bold", command=objFormat.bold, accelerator="Ctrl+B")
formatMenu.add_command(label="Italic", command=objFormat.italic, accelerator="Ctrl+I")
formatMenu.add_command(label="Underline", command=objFormat.underline, accelerator="Ctrl+U")
formatMenu.add_command(label="Overstrike", command=objFormat.overstrike, accelerator="Ctrl+T")
formatMenu.add_command(label="Add Date", command=objFormat.addDate)
menubar.add_cascade(label="Format", menu=formatMenu)
root.bind_all("<Control-b>", objFormat.bold)
root.bind_all("<Control-i>", objFormat.italic)
root.bind_all("<Control-u>", objFormat.underline)
root.bind_all("<Control-T>", objFormat.overstrike)
root.grid_columnconfigure(0, weight=1)
root.resizable(True, True)
root.config(menu=menubar)
Finally, we add some error-handling stuff:
if __name__ == "__main":
print("Please run 'main.py'")
We'll start by importing stuff
# import statements
from tkinter import *
from tkinter.messagebox import *
Then, we'll make a
Help
class and add an About
function to it:class Help():
def about(root):
showinfo(title="About", message="Hello, this is a text editor made by Insidious using Python")
We then make a
main
function:def main(root, text, menubar):
help = Help()
helpMenu = Menu(menubar)
helpMenu.add_command(label="About", command=help.about)
menubar.add_cascade(label="Help", menu=helpMenu)
root.config(menu=menubar)
Finally, we add some error-handling sort of thing:
if __name__ == "__main":
print("Please run 'main.py'")
And that should be it!
If there is anything missing, or I did something wrong, kindly point it out in the comments :D
Thanks!!!
20