Python Forum
[Tkinter] Indentation for Tkinter-made Text Editor - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: [Tkinter] Indentation for Tkinter-made Text Editor (/thread-27478.html)



Indentation for Tkinter-made Text Editor - ShakeyPakey - Jun-08-2020

Hello, there.

I am writing my own programming language in Python, and along with it, my own text editor, made with the Tkinter module. I already have basic functionality set up, and it is very similar in use and abilities to Microsoft Notepad.
As of now, you are able to write text, save the text file, open the text file, create a new text file; the entire code is below:

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox


class Menubar:

    def __init__(self, parent):
        font_specs = ("ubuntu", 14)

        menubar = tk.Menu(parent.master, font=font_specs)
        parent.master.config(menu=menubar)

        file_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0)
        file_dropdown.add_command(label="New File",
                                  accelerator="Ctrl+N",
                                  command=parent.new_file)
        file_dropdown.add_command(label="Open File",
                                  accelerator="Ctrl+O",
                                  command=parent.open_file)
        file_dropdown.add_command(label="Save",
                                  accelerator="Ctrl+S",
                                  command=parent.save)
        file_dropdown.add_command(label="Save As",
                                  accelerator="Ctrl+Shift+S",
                                  command=parent.save_as)
        file_dropdown.add_separator()
        file_dropdown.add_command(label="Exit",
                                  command=parent.master.destroy)

        about_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0)
        about_dropdown.add_command(label="Release Notes",
                                   command=self.show_release_notes)
        about_dropdown.add_separator()
        about_dropdown.add_command(label="About",
                                   command=self.show_about_message)

        menubar.add_cascade(label="File", menu=file_dropdown)
        menubar.add_cascade(label="About", menu=about_dropdown)

    def show_about_message(self):
        box_title = "About TextEditor"
        box_message "A simple text editor."
        messagebox.showinfo(box_title, box_message)

    def show_release_notes(self):
        box_title = "Release Notes"
        box_message = "Version 0.1 - new."
        messagebox.showinfo(box_title, box_message)


class Statusbar:

    def __init__(self, parent):

        font_specs = ("ubuntu", 12)

        self.status = tk.StringVar()
        self.status.set("TextEditor v1.0")

        label = tk.Label(parent.textarea, textvariable=self.status, fg="black",
                         bg="lightgrey", anchor='sw', font=font_specs)
        label.pack(side=tk.BOTTOM, fill=tk.BOTH)

    def update_status(self, *args):
        if isinstance(args[0], bool):
            self.status.set("Your File Has Been Saved!")
        else:
            self.status.set("TextEditor")


class TextEditor:

    def __init__(self, master):
        master.title("Untitled - TextEditor")
        master.geometry("1200x700")

        font_specs = ("ubuntu", 18)

        self.master = master
        self.filename = None

        self.textarea = tk.Text(master, font=font_specs)
        self.scroll = tk.Scrollbar(master, command=self.textarea.yview)
        self.textarea.configure(yscrollcommand=self.scroll.set)
        self.textarea.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.menubar = Menubar(self)
        self.statusbar = Statusbar(self)

        self.bind_shortcuts()


    def set_window_title(self, name=None):
        if name:
            self.master.title(name + " - TextEditor")
        else:
            self.master.title("Untitled - TextEditor")

    def new_file(self, *args):
        self.textarea.delete(1.0, tk.END)
        self.filename = None
        self.set_window_title()

    def open_file(self, *args):
        self.filename = filedialog.askopenfilename(
            defaultextension=".txt",
            filetypes=[("All Files", "*.*"),
                       ("Text Files", "*.txt"),
                       ("Python Scripts", "*.py"),
                       ("Markdown Documents", "*.md"),
                       ("JavaScript Files", "*.js"),
                       ("HTML Documents", "*.html"),
                       ("CSS Documents", "*.css")])
        if self.filename:
            self.textarea.delete(1.0, tk.END)
            with open(self.filename, "r") as f:
                self.textarea.insert(1.0, f.read())
            self.set_window_title(self.filename)

    def save(self, *args):
        if self.filename:
            try:
                textarea_content = self.textarea.get(1.0, tk.END)
                with open(self.filename, "w") as f:
                    f.write(textarea_content)
                self.statusbar.update_status(True)
            except Exception as e:
                print(e)
        else:
            self.save_as()

    def save_as(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(
                initialfile="Untitled.txt",
                defaultextension=".txt",
                filetypes=[("All Files", "*.*"),
                        ("Text Files", "*.txt"),
                        ("Python Scripts", "*.py"),
                        ("Markdown Documents", "*.md"),
                        ("JavaScript Files", "*.js"),
                        ("HTML Documents", "*.html"),
                        ("CSS Documents", "*.css")])
            textarea_content = self.textarea.get(1.0, tk.END)
            with open(new_file, "w") as f:
                f.write(textarea_content)
            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status(True)
        except Exception as e:
            print(e)

    def bind_shortcuts(self):
        self.textarea.bind('<Control-n>', self.new_file)
        self.textarea.bind('<Control-o>', self.open_file)
        self.textarea.bind('<Control-s>', self.save)
        self.textarea.bind('<Control-S>', self.save_as)
        self.textarea.bind('<Key>', self.statusbar.update_status)


if __name__ == "__main__":
    master = tk.Tk()
    pt = TextEditor(master)
    master.mainloop()
The editor is functional and acts as a basic text editor, but I would to create a more advanced editor: with syntax highlighting and indentation. For now, I'm focusing on indentation.

I mentioned I am writing my own programming language (based off of Python), but for now, because I want to test if the editor is working, i'm going to be testing out indentation with Python. So for example, if it detects an if statement (such as if <variable> == 1:) or a function (such as def function():), or anything else that requires indentation, then the editor automatically indents four spaces when the user goes on a new line.

Is there any way I can achieve this with Tkinter?

Thank you.


RE: Indentation for Tkinter-made Text Editor - MianDoesCoding - Jun-08-2020

(Jun-08-2020, 10:05 AM)ShakeyPakey Wrote: Hello, there.

I am writing my own programming language in Python, and along with it, my own text editor, made with the Tkinter module. I already have basic functionality set up, and it is very similar in use and abilities to Microsoft Notepad.
As of now, you are able to write text, save the text file, open the text file, create a new text file; the entire code is below:

import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox


class Menubar:


    def __init__(self, parent):
        font_specs = ("ubuntu", 14)

        menubar = tk.Menu(parent.master, font=font_specs)
        parent.master.config(menu=menubar)

        file_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0)
        file_dropdown.add_command(label="New File",
                                  accelerator="Ctrl+N",
                                  command=parent.new_file)
        file_dropdown.add_command(label="Open File",
                                  accelerator="Ctrl+O",
                                  command=parent.open_file)
        file_dropdown.add_command(label="Save",
                                  accelerator="Ctrl+S",
                                  command=parent.save)
        file_dropdown.add_command(label="Save As",
                                  accelerator="Ctrl+Shift+S",
                                  command=parent.save_as)
        file_dropdown.add_separator()
        file_dropdown.add_command(label="Exit",
                                  command=parent.master.destroy)

        about_dropdown = tk.Menu(menubar, font=font_specs, tearoff=0)
        about_dropdown.add_command(label="Release Notes",
                                   command=self.show_release_notes)
        about_dropdown.add_separator()
        about_dropdown.add_command(label="About",
                                   command=self.show_about_message)

        menubar.add_cascade(label="File", menu=file_dropdown)
        menubar.add_cascade(label="About", menu=about_dropdown)

    def show_about_message(self):
        box_title = "About TextEditor"
        box_message "A simple text editor."
        messagebox.showinfo(box_title, box_message)

    def show_release_notes(self):
        box_title = "Release Notes"
        box_message = "Version 0.1 - new."
        messagebox.showinfo(box_title, box_message)


class Statusbar:

    def __init__(self, parent):

        font_specs = ("ubuntu", 12)

        self.status = tk.StringVar()
        self.status.set("TextEditor v1.0")

        label = tk.Label(parent.textarea, textvariable=self.status, fg="black",
                         bg="lightgrey", anchor='sw', font=font_specs)
        label.pack(side=tk.BOTTOM, fill=tk.BOTH)

    def update_status(self, *args):
        if isinstance(args[0], bool):
            self.status.set("Your File Has Been Saved!")
        else:
            self.status.set("TextEditor")


class TextEditor:

    def __init__(self, master):
        master.title("Untitled - TextEditor")
        master.geometry("1200x700")

        font_specs = ("ubuntu", 18)

        self.master = master
        self.filename = None

        self.textarea = tk.Text(master, font=font_specs)
        self.scroll = tk.Scrollbar(master, command=self.textarea.yview)
        self.textarea.configure(yscrollcommand=self.scroll.set)
        self.textarea.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.menubar = Menubar(self)
        self.statusbar = Statusbar(self)

        self.bind_shortcuts()


    def set_window_title(self, name=None):
        if name:
            self.master.title(name + " - TextEditor")
        else:
            self.master.title("Untitled - TextEditor")

    def new_file(self, *args):
        self.textarea.delete(1.0, tk.END)
        self.filename = None
        self.set_window_title()

    def open_file(self, *args):
        self.filename = filedialog.askopenfilename(
            defaultextension=".txt",
            filetypes=[("All Files", "*.*"),
                       ("Text Files", "*.txt"),
                       ("Python Scripts", "*.py"),
                       ("Markdown Documents", "*.md"),
                       ("JavaScript Files", "*.js"),
                       ("HTML Documents", "*.html"),
                       ("CSS Documents", "*.css")])
        if self.filename:
            self.textarea.delete(1.0, tk.END)
            with open(self.filename, "r") as f:
                self.textarea.insert(1.0, f.read())
            self.set_window_title(self.filename)

    def save(self, *args):
        if self.filename:
            try:
                textarea_content = self.textarea.get(1.0, tk.END)
                with open(self.filename, "w") as f:
                    f.write(textarea_content)
                self.statusbar.update_status(True)
            except Exception as e:
                print(e)
        else:
            self.save_as()

    def save_as(self, *args):
        try:
            new_file = filedialog.asksaveasfilename(
                initialfile="Untitled.txt",
                defaultextension=".txt",
                filetypes=[("All Files", "*.*"),
                        ("Text Files", "*.txt"),
                        ("Python Scripts", "*.py"),
                        ("Markdown Documents", "*.md"),
                        ("JavaScript Files", "*.js"),
                        ("HTML Documents", "*.html"),
                        ("CSS Documents", "*.css")])
            textarea_content = self.textarea.get(1.0, tk.END)
            with open(new_file, "w") as f:
                f.write(textarea_content)
            self.filename = new_file
            self.set_window_title(self.filename)
            self.statusbar.update_status(True)
        except Exception as e:
            print(e)

    def bind_shortcuts(self):
        self.textarea.bind('<Control-n>', self.new_file)
        self.textarea.bind('<Control-o>', self.open_file)
        self.textarea.bind('<Control-s>', self.save)
        self.textarea.bind('<Control-S>', self.save_as)
        self.textarea.bind('<Key>', self.statusbar.update_status)


if __name__ == "__main__":
    master = tk.Tk()
    pt = TextEditor(master)
    master.mainloop()
The editor is functional and acts as a basic text editor, but I would to create a more advanced editor: with syntax highlighting and indentation. For now, I'm focusing on indentation.

I mentioned I am writing my own programming language (based off of Python), but for now, because I want to test if the editor is working, i'm going to be testing out indentation with Python. So for example, if it detects an if statement (such as if <variable> == 1:) or a function (such as def function():), or anything else that requires indentation, then the editor automatically indents four spaces when the user goes on a new line.

Is there any way I can achieve this with Tkinter?

Thank you.

Hey, So firstly i think that this is really cool for a project, but i found an error when running.
With Box_title you put an = but with box_message theres no = meaning the the thing wont run.


RE: Indentation for Tkinter-made Text Editor - ShakeyPakey - Jun-08-2020

(Jun-08-2020, 11:42 AM)MianDoesCoding Wrote: Hey, So firstly i think that this is really cool for a project, but i found an error when running.
With Box_title you put an = but with box_message theres no = meaning the the thing wont run.

Oops, forgot to fix that. It's easily fixed, though.

Anyways, do you have any ideas for what I can do with indentation? And thanks for being interested in the project :)


RE: Indentation for Tkinter-made Text Editor - MianDoesCoding - Jun-08-2020

(Jun-08-2020, 11:59 AM)ShakeyPakey Wrote:
(Jun-08-2020, 11:42 AM)MianDoesCoding Wrote: Hey, So firstly i think that this is really cool for a project, but i found an error when running.
With Box_title you put an = but with box_message theres no = meaning the the thing wont run.

Oops, forgot to fix that. It's easily fixed, though.

Anyways, do you have any ideas for what I can do with indentation? And thanks for being interested in the project :)
if you want, we could work together on it!
my email is [email protected]


RE: Indentation for Tkinter-made Text Editor - menator01 - Jun-08-2020

If you are doing this after code has already written on the line above, you should be able to do the auot indent with the insert.
Going by the documentation you can use a column, row index for positioning.
Here is a link on stackoverflow. The first response has an example.
And a link to EffBot
As a side not python3's f-string recognizes &nbsp; as a space character. I found this out experimenting on something for displaying. I do not know about the tkinters text widget but, the labels will use it.
Stackoverflow
Tikinter Book Text Widget