15
Make a simple text editor using Python
Today we'll be making a simple text editor using Python and Tkinter.
- It is a standard Python interface to the Tk GUI toolkit shipped with Python.
- Most probably, you would have it installed. Try running
python -m tkinter
in your console. If it doesn't give an error, it means you have tkinter installed and ready to use!
- Just make sure you have Python and pip working, then run
pip install tk
in the console.
- Python 3.9.x
- Code editor (VSCode, PyCharm, etc)
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):
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:
- New File
- Save File
- Save File As
- Open File
- Quit Editor
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:
- Copy
- Cut
- Paste
- Select All
- Undo
- Redo
- Find
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:
- Change Background
- Change Font Group
- Bold Text
- Italic Text
- Underline Text
- Over Strike Text
- Add Date
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!!!
15