1

I am trying to make a word processor of my own but I can't figure out how to press a button to bold only whatever the person will type next, not the whole textbox.


# from tkinter import *  # PEP8: `import *` is not preferred`
import customtkinter as tk
#from docx import Document



class TextWindow(tk.CTkToplevel):  # still use `Toplevel`

    def __init__(self, master):  # send main window as master/parent
        super().__init__(master)  # it will also set `self.master = master`

        ws = self.winfo_screenwidth()  # width of the screen
        hs = self.winfo_screenheight()  # height of the screen

        # calculate x and y coordinates for the Tk root window
        x = (ws / 2) - (300 / 2)
        y = (hs / 2) - (200 / 2)

        self.geometry('%dx%d+%d+%d' % (300, 200, x, y))
        self.title("New World Documents")
        tk.set_appearance_mode("system")
        tk.set_default_color_theme("green")

        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure((0, 1), weight=1)

        self.textbox = tk.CTkTextbox(self, width=400, corner_radius=0, font=("roboto", 12), wrap="word")
        self.textbox.grid(row=0, column=0, padx=20, pady=20, sticky="nsew", columnspan=2)

        self.button = tk.CTkButton(self, text="Save and quit", command=self.go_back)
        self.button.grid(row=1, column=0, padx=20, pady=20, sticky="ew")

        self.button1 = tk.CTkButton(self, text="Bold", command=self.bolden)
        self.button1.grid(row=1, column=1, padx=20, pady=20, sticky="ew")

        self.wm_protocol("WM_DELETE_WINDOW", self.on_close)

    def bolden(self):
        #Function to bolden what the user will type.
        self.textbox.configure(font=("roboto", 12, "bold"))

    def go_back(self):
        # Work in Progress.
        #document = Document()
        #text = self.textbox.get("1.0", "end-1c")
        #document.add_paragraph(text)
        #document.save("test.docx")
        #with open("filepy.txt", "a") as outf:
        #    outf.write(text)
        self.destroy()  # destroy only current window
        self.master.deiconify()  # show again main window

    def on_close(self):
        self.destroy()  # destroy current window
        self.master.destroy()  # destroy main window


class MainScreen(tk.CTk):

    def __init__(self):
        super().__init__()


        ws = self.winfo_screenwidth()  # width of the screen
        hs = self.winfo_screenheight()  # height of the screen

        # calculate x and y coordinates for the Tk root window
        x = (ws / 2) - (300 / 2)
        y = (hs / 2) - (200 / 2)

        self.geometry('%dx%d+%d+%d' % (300, 200, x, y))
        self.title("New World Documents")
        tk.set_appearance_mode("system")
        tk.set_default_color_theme("green")


        self.label = tk.CTkLabel(self, text="New World Documents", font=("roboto",25), width=200, height=25)
        self.label.pack()

        self.button = tk.CTkButton(self, text="New Document", command=self.StartWriting)
        self.button.pack()

    def StartWriting(self):
        self.withdraw()
        self.app = TextWindow(self)  # send main window as argument

    def start(self):
        self.mainloop()


MainScreen().start()

Currently the only thing the code will do when I press the bold button is make everything in the textbox bold. I only want to make the words that will be typed next bold just like in word, libreoffice, google docs, etc.

I have tried looking up an answer but every time I came close with tags the answer wouldn't help because it would only work for words being inserted from the code and not from words being typed in, it would also not work because I can't specify to add a tag for blank spaces or for future words that will be typed.

  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Friedrich Mar 12 '23 at 18:52

1 Answers1

0

When you press a key on the keyboard, it will get be processed by a binding of the <Key> event on the widget class. You can create a binding on the widget to the same event, and override the default behavior. If the bound function returns the string "break" it will prevent the binding on the widget class from being processed.

In the following example, a class is created that overrides the default binding with a custom function that will attach a list of tags to every inserted character.

class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.tags = []
        self.bind("<Key>", self._key)

    def _key(self, event):
        if event.char:
            # ie: if it's a printable character
            # insert the character along with the set of tags
            self.insert("insert", event.char, self.tags)

            # ... and prevent the default binding from processing
            # the event
            return "break"

You can create menu items or toolbar buttons to set the tags attribute to whatever you want. For example, you could configure a tag for "bold" and configure a toolbar button to add the "bold" tag when the button is selected.

The following example defines the tag "bold", and a checkbutton that adds or removes "bold" from the list of tags.

import tkinter as tk
from tkinter.font import Font

class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.tags = []
        self.bind("<Key>", self._key)

    def _key(self, event):
        if event.char:
            # ie: if it's a printable character
            # insert the character along with the set of tags
            self.insert("insert", event.char, self.tags)

            # ... and prevent the default binding from processing
            # the event
            return "break"


def set_attributes():
    tags = []
    for tag, var in vars.items():
        if var.get():
            tags.append(tag)
    text.tags = tags

root = tk.Tk()

text_font = Font(family="helvetica", size=20)
bold_font = Font(family="helvetica", size=20, weight="bold")

text = CustomText(root, font=text_font)
toolbar = tk.Frame(root)

toolbar.pack(side="top", fill="x")
text.pack(fill="both", expand=True)

vars = {"bold": tk.BooleanVar()}

bold_button = tk.Checkbutton(
    toolbar, text="Bold", command=set_attributes,
    onvalue=True, offvalue=False, indicatoron=False,
    variable=vars["bold"]
)
bold_button.pack(side="left")

text.tag_configure("bold", font=bold_font)

root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685