Roni
Roni

Reputation: 649

Autocomplete bug in tkinter- HTML editor

I'm currently working on an HTML editor application with Python tkinter. When you create a new project or open an existing file, a text widget is displayed on the screen, along with a Run button to run your HTML code. When you type any HTML start tag, the script will immediately add an end tag to that element (autocomplete). I'm using a function called get_stringvar that is bound by any key release. That function is in charge of the autocomplete. When I type my first start tag, the script autocompletes the end tag. But when I type my second start tag, the end tag isn't being autocompleted. Can someone help me fix that? Some of my code:

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
from tkinter import messagebox
import webbrowser
import os

window = tk.Tk()
window.title("HTML Editor")
window.configure(bg="grey")
window.state("zoomed")
title = tk.Label(window, text="HTML Editor", font=("Arial Rounded MT Bold", 40, "underline"), bg="grey")
title.place(x=400, y=20)


def get_stringvar(event):
    content = text_box.get("1.0", tk.END)
    keys = ["Return", "Up", "Down", "Left", "Right"]

    if event.keysym in keys:
        return

    line = content.splitlines()[-1]
    if "/" not in line and line != "<!DOCTYPE html>":
        if ("<" in line and ">" in line) and line.index(">") + 1 == len(line):
            index = line.index("<")
            index2 = line.index(">") + 1
            new_line = "<" + "/" + line[index + 1:index2 - 1] + ">"
            text_box.insert(tk.END, new_line)
            line2 = content.splitlines().index(line) + 1
            text_box.mark_set("insert", "%d.%d" % (line2, index2))




create = tk.Button(window, text="Create a new file", width=17, height=3, font=("Arial Rounded MT Bold", 20),
                   command=save_file)
create.place(x=420, y=200)
open_e = tk.Button(window, text="Open an existing file", width=17, height=3, font=("Arial Rounded MT Bold", 20),
                   command=open_file)
open_e.place(x=420, y=350)

window.protocol("WM_DELETE_WINDOW", on_closing)
frame = tk.Frame(window, bd=2, relief="raised")

text_box = tk.Text(window, font=("Courier New", 10), fg="black")
text_box.bind("<KeyRelease>", get_stringvar)

scroll_bar = tk.Scrollbar(window, command=text_box.yview)

run_b = tk.Button(frame, text="Run", width=6, height=2, bg="white", command=run_code)

text_box.configure(yscrollcommand=scroll_bar.set)

window.mainloop()

Updated code (Thank you so much, @TheLizzard):

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
from tkinter import messagebox
import webbrowser
import os

window = tk.Tk()
window.title("HTML Editor")
window.configure(bg="grey")
window.state("zoomed")
title = tk.Label(window, text="HTML Editor", font=("Arial Rounded MT Bold", 40, "underline"), bg="grey")
title.place(x=400, y=20)


def get_stringvar(event):
    line = text_box.get("insert linestart", "insert")
    if (line[-1] == ">") and ("<" in line):
        new_line = "</" + line[line.rindex("<") + 1:-1] + ">"
        text_box.insert("insert", new_line)
        text_box.mark_set("insert", f"insert-{len(new_line)}c")
        return "break"

create = tk.Button(window, text="Create a new file", width=17, height=3, font=("Arial Rounded MT Bold", 20),
                   command=save_file)
create.place(x=420, y=200)
open_e = tk.Button(window, text="Open an existing file", width=17, height=3, font=("Arial Rounded MT Bold", 20),
                   command=open_file)
open_e.place(x=420, y=350)

window.protocol("WM_DELETE_WINDOW", on_closing)
frame = tk.Frame(window, bd=2, relief="raised")

text_box = tk.Text(window, font=("Courier New", 10), fg="black")
text_box.bind("<Tab>", get_stringvar)

scroll_bar = tk.Scrollbar(window, command=text_box.yview)

run_b = tk.Button(frame, text="Run", width=6, height=2, bg="white", command=run_code)

text_box.configure(yscrollcommand=scroll_bar.set)

window.mainloop()

Upvotes: 0

Views: 147

Answers (1)

TheLizzard
TheLizzard

Reputation: 7680

Try this:

import tkinter as tk


def tab_pressed(event):
    line = text_box.get("insert linestart", "insert")
    if (line[-1:] == ">") and ("<" in line):
        new_line = "</" + line[line.rindex("<")+1:-1] + ">"
        text_box.insert("insert", new_line)
        text_box.mark_set("insert", f"insert-{len(new_line)}c")
        # Stop the tab from being added
        return "break"

    # Remove these 2 lines if you like tabs over spaces
    text_box.insert("insert", " "*4)
    return "break"


window = tk.Tk()

text_box = tk.Text(window)
text_box.bind("<Tab>", tab_pressed)
text_box.pack(side="left", fill="both", expand=True)

scroll_bar = tk.Scrollbar(window, command=text_box.yview)
scroll_bar.pack(side="left", fill="y")

text_box.configure(yscrollcommand=scroll_bar.set)

window.mainloop()

Instead of binding to "<KeyPress>" or "<KeyRelease>", it will me more user friendly if you bind to "<Tab>". Also your code only works if it's the user is writing on the last line. Instead of using "end", you should use "insert". Also please look at this it will help you a lot. Also my code uses line.rindex("<") to get the index of the right most "<" in the line.

The code above autocompletes the html tag only if the user presses "<Tab>". To test it just type in <aaa> then press Tab (works only if the cursor is after the >).

Upvotes: 1

Related Questions