I have text box my_text on gui at location
my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")
my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")
I want to add lines numbers to this text box using Bryan Oakley code here.
My code:
import tkinter as tk
from tkinter import *
root = Tk()
my_text = Text(root, width=98, height=35, font=("Helvetica", 10), bg="cyan", fg="black")
my_text.grid(row=4, column=0, padx=(20, 50), pady=(20, 0), rowspan=3, sticky="e")
text_file = open("sample.xml", 'r')
s = text_file.read()
my_text.delete("1.0", "end")
my_text.insert(END, s)
class TextLineNumbers(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, *args, **kwargs)
self.root = Tk()
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2,y,anchor="nw", text=linenum)
i = self.textwidget.index("%s+1line" % i)
def run(self):
self.root.mainloop()
linenos = TextLineNumbers()
linenos.attach(my_text)
linenos.redraw()
root.mainloop()
Code is not displaying line number. It is displaying just text. How to display linenumbers in text box? Thanks in advance.
You mentioned this great example, why not just modify it to suit your needs? I was curious and modified the Example class from given link by adding a button to call a function load_xml which loads files via filechooser, deletes the previous data in the CustomText widget and inserts the new data:
import tkinter as tk
from tkinter import filedialog
import os
class TextLineNumbers(tk.Canvas):
def __init__(self, *args, **kwargs):
tk.Canvas.__init__(self, *args, **kwargs)
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True:
dline = self.textwidget.dlineinfo(i)
if dline is None:
break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2, y, anchor="nw", text=linenum)
i = self.textwidget.index("%s+1line" % i)
class CustomText(tk.Text):
def __init__(self, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
# create a proxy for the underlying widget
self._orig = self._w + "_orig"
self.tk.call("rename", self._w, self._orig)
self.tk.createcommand(self._w, self._proxy)
def _proxy(self, *args):
# let the actual widget perform the requested action
cmd = (self._orig,) + args
result = self.tk.call(cmd)
# generate an event if something was added or deleted,
# or the cursor position changed
if (args[0] in ("insert", "replace", "delete") or
args[0:3] == ("mark", "set", "insert") or
args[0:2] == ("xview", "moveto") or
args[0:2] == ("xview", "scroll") or
args[0:2] == ("yview", "moveto") or
args[0:2] == ("yview", "scroll")):
self.event_generate("<<Change>>", when="tail")
# return what the actual widget returned
return result
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.text = CustomText(self)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.text.tag_configure("bigfont", font=("Helvetica", "24", "bold"))
self.linenumbers = TextLineNumbers(self, width=30)
self.linenumbers.attach(self.text)
self.vsb.pack(side="right", fill="y")
self.linenumbers.pack(side="left", fill="y")
self.text.pack(side="right", fill="both", expand=True)
# new button to call load_xml and show status
self.load_button = tk.Button(root, text="Load file", command=self.load_xml)
self.load_button.pack(side="top")
self.text.bind("<<Change>>", self._on_change)
self.text.bind("<Configure>", self._on_change)
self.text.insert("end", "one\ntwo\nthree\n")
self.text.insert("end", "four\n", ("bigfont",))
self.text.insert("end", "five\n")
def _on_change(self, event):
self.linenumbers.redraw()
def load_xml(self):
"""Load any file, delete current text and insert new data"""
input_file = filedialog.askopenfilename(title="Load a textfile",
filetypes=(("XML", "*.xml"),
("Text", "*.txt"),
("All files", "*.*")),
initialdir=os.getcwd())
if input_file:
self.text.delete("1.0", "end")
self.load_button.config(text=f"Currently loaded: {input_file.split(os.sep)[-1]}")
with open(input_file, 'r') as f:
self.text.insert("end", f.read())
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Related
I want to highlight a given token inside a input text field (We can have several token highlighted) and when the user has the mouse over this token we get up dialogox.
I tried the following:
import tkinter as tk
from tkinter import *
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.l1 = tk.Label(self, text="Hover over me")
self.l2 = tk.Label(self, text="", width=40)
self.l1.pack(side="top")
self.l2.pack(side="top", fill="x")
self.inputText = tk.Text(root, height = 10, width = 70, bg = "light yellow")
self.inputText.insert('1.0', "token1 token2 token3 etc.")
self.inputText.pack()
self.display_annotate = tk.Button(self, height = 2, width = 20, text ="Annotate text", command = lambda: self.add_highlighter())
self.display_annotate.place(x = 750, y = 20)
print(self.__dict__.keys())
self.l1.bind("<Enter>", lambda event, text="text": self.on_enter(text=text))
self.l1.bind("<Leave>", self.on_leave)
def take_input(self,):
text_to_annotate = self.inputText.get("1.0", "end-1c")
print(text_to_annotate)
return text_to_annotate
def on_enter(self, text):
self.l2.configure(text=text)
def on_leave(self, event):
self.l2.configure(text="")
def add_highlighter(self):
self.inputText.tag_add("start", "1.0", "1.5")
self.inputText.bind("<Enter>", lambda event, text="ali": self.on_enter(text=text))
self.inputText.tag_config("start", background= "black", foreground= "white")
if __name__ == "__main__":
root = tk.Tk()
scrollb = tk.Scrollbar(root)
scrollb.pack(side = tk.RIGHT, fill=tk.Y)
var1 = tk.IntVar()
var2 = tk.IntVar()
root.geometry("900x500+10+10")
root.title('Annotation page')
Example(root).pack(side="top", fill="both", expand="true")
root.mainloop()
It works to highlight the concerned token 1 between the caracters 0 and 5. But it is not working when I haveover the mouse over token 1. noting that it is working for the label.
Any suggestion?
If I got you right, I've added a label when you hover over a token (together with rotating tokens)
import tkinter as tk
from tkinter import *
def on_enter(text, obj, e=None):
obj.float_lbl = Label(root, text=text)
obj.float_lbl.place(x=e.x + obj.winfo_x(), y=e.y + obj.winfo_y())
def on_leave(obj, event=None):
obj.float_lbl.destroy()
class Example(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.l1 = tk.Label(self, text="Hover over me")
self.l1.pack(side="top")
# creating new Text object with new methods and properties
self.inputText = InputText()
# creating 15 new tokens
for i in range(15):
self.inputText.add_token(f"token{i + 1}")
self.inputText.pack()
# binding rotate_tag method to button
self.display_annotate = tk.Button(self, height=2, width=20, text="Annotate text",
command=self.inputText.rotate_tag)
self.display_annotate.place(x=750, y=20)
self.l1.bind("<Enter>", lambda event, text="text": on_enter(text=text, e=event, obj=self.l1))
self.l1.bind("<Leave>", lambda event: on_leave(event=event, obj=self.l1))
class Token:
"""
Token object for InputText
"""
def __init__(self, name: str, pos: int, tag_parent: Text):
self.token_name = name
self.begin = "1." + str(pos)
self.end = "1." + str(pos + len(name))
self.parent = tag_parent
self.add_tag()
self.bind_tag()
def add_tag(self):
self.parent.tag_add(self.token_name, self.begin, self.end)
def bind_tag(self):
self.parent.tag_bind(self.token_name, "<Enter>",
lambda event, text="text": on_enter(obj=self.parent, e=event, text=self.token_name))
self.parent.tag_bind(self.token_name, "<Leave>",
lambda event, text="text": on_leave(obj=self.parent, event=event))
def highlight_tag(self):
self.parent.tag_config(self.token_name, background='red', foreground='white')
def disable_tag(self):
self.parent.tag_config(self.token_name, background='gray', foreground='black')
class InputText(Text):
"""
Text object with methods to add tokens and rotate tags
"""
def __init__(self):
super(InputText, self).__init__(root, height=10, width=70, bg="light yellow")
self.tokens = []
self.last = 0
self.current_tag = None
def add_token(self, name):
self.insert(END, name + " ")
self.tokens.append(Token(name, self.last, self))
self.last += (1 + len(name))
def rotate_tag(self):
if self.current_tag is None:
self.current_tag = 0
else:
self.tokens[self.current_tag - 1].disable_tag()
self.tokens[self.current_tag].highlight_tag()
self.current_tag = min(self.current_tag + 1, len(self.tokens) - 1)
if __name__ == "__main__":
root = tk.Tk()
scrollb = tk.Scrollbar(root)
scrollb.pack(side=tk.RIGHT, fill=tk.Y)
var1 = tk.IntVar()
var2 = tk.IntVar()
root.geometry("900x500+10+10")
root.title('Annotation page')
Example().pack(side="top", fill="both", expand="true")
root.mainloop()
I have created a frame that holds a specialized text widget. In my app I have a button to change the color mode from light to dark and vice versa. everything works quite well with the color mode change except the specialized text widget. I have narrowed down the problem and have found that it is the width=134, height=30 that is causing the problem. No idea why though. Any ideas?
Code to reproduce problem:
from tkinter import *
class ultra_text(Frame):
def __init__(self, *args, **kwargs):
self.window = kwargs.pop("window", None)
self.color_mode = kwargs.pop("color_mode")
Frame.__init__(self, *args, **kwargs)
if "height" and "width" in kwargs:
self.text = Text(self, height=kwargs["height"], width=kwargs["width"], borderwidth=2, relief=RIDGE, wrap=NONE, undo=True, font=("Courier New bold", 15))
else:
self.text = Text(self, font=("Courier New bold", 15), wrap="none", undo=True, borderwidth=2, relief=RIDGE, width=120, height=30)
self.scrollbar = Scrollbar(self, orient=VERTICAL, command=self.text.yview)
self.text.configure(yscrollcommand=self.scrollbar.set)
self.numberLines = TextLineNumbers(self, width=40)
self.numberLines.attach(self.text)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.numberLines.pack(side=LEFT, fill=Y, padx=(5, 0))
self.text.pack(side=RIGHT, fill=BOTH, expand=True)
self.text.bind("<Key>", self.onPressDelay)
self.text.bind("<Button-1>", self.numberLines.redraw)
self.scrollbar.bind("<Button-1>", self.onScrollPress)
self.text.bind("<MouseWheel>", self.onPressDelay)
self.window.bind("<KeyRelease>", self.redraw())
#Place tag_config here
def change_color(self, new_color):
if new_color == "Dark":
bg = "black"
fg = "white"
else:
bg = "white"
fg = "black"
self.text.config(bg=bg, fg=fg, insertbackground=fg)
self.numberLines.config(bg=bg)
def onScrollPress(self, *args):
self.scrollbar.bind("<B1-Motion>", self.numberLines.redraw)
def onScrollRelease(self, *args):
self.scrollbar.unbind("<B1-Motion>", self.numberLines.redraw)
def onPressDelay(self, *args):
self.after(2, self.numberLines.redraw)
def get(self, *args, **kwargs):
return self.text.get(*args, **kwargs)
def insert(self, *args, **kwargs):
return self.text.insert(*args, **kwargs)
def delete(self, *args, **kwargs):
return self.text.delete(*args, **kwargs)
def index(self, *args, **kwargs):
return self.text.index(*args, **kwargs)
def redraw(self):
self.numberLines.redraw()
class TextLineNumbers(Canvas):
def __init__(self, *args, **kwargs):
Canvas.__init__(self, *args, **kwargs, highlightthickness=0)
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2, y, anchor="nw", text=linenum, fill="#808090") #606366
i = self.textwidget.index("%s+1line" % i)
if __name__ == "__main__":
window = Tk()
color_mode = "Light"
window.geometry("%dx%d+0+0" % (window.winfo_screenwidth(), window.winfo_screenheight()))
my_text = ultra_text(window, window = window, color_mode="Dark", width=134, height=30)#Problem is here <- get rid of ", width=134, height=30" and it works. Keep it and it has the bug. How do I get rid of the bug while keeping the width and height?
my_text.place(relx=.5, rely=.5, anchor=CENTER)
def change_color_mode():
global my_text
global color_mode
if color_mode == "Dark":
color_mode = "Light"
else:
color_mode = "Dark"
if color_mode == "Dark":
bg = "black"
else:
bg = "white"
my_text.change_color(color_mode) #This part is fine and does the bulk of the color_changing
my_text.configure(bg=bg)
change_color_mode_button.config(highlightbackground=bg)
window.configure(bg=bg)
change_color_mode_button = Button(window, text="Change Color Mode", command=change_color_mode)
change_color_mode_button.pack()
window.mainloop()
I figured out the solution; if we remove the width and height from the main part it keeps its size because of the else statement but gets rid of the bug.
Code:
from tkinter import *
class ultra_text(Frame):
def __init__(self, *args, **kwargs):
self.window = kwargs.pop("window", None)
self.color_mode = kwargs.pop("color_mode")
Frame.__init__(self, *args, **kwargs)
if "height" and "width" in kwargs:
self.text = Text(self, height=kwargs["height"], width=kwargs["width"], borderwidth=2, relief=RIDGE, wrap=NONE, undo=True, font=("Courier New bold", 15))
else:
self.text = Text(self, font=("Courier New bold", 15), wrap="none", undo=True, borderwidth=2, relief=RIDGE, width=120, height=30)
self.scrollbar = Scrollbar(self, orient=VERTICAL, command=self.text.yview)
self.text.configure(yscrollcommand=self.scrollbar.set)
self.numberLines = TextLineNumbers(self, width=40)
self.numberLines.attach(self.text)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.numberLines.pack(side=LEFT, fill=Y, padx=(5, 0))
self.text.pack(side=RIGHT, fill=BOTH, expand=True)
self.text.bind("<Key>", self.onPressDelay)
self.text.bind("<Button-1>", self.numberLines.redraw)
self.scrollbar.bind("<Button-1>", self.onScrollPress)
self.text.bind("<MouseWheel>", self.onPressDelay)
self.window.bind("<KeyRelease>", self.redraw())
#Place tag_config here
def change_color(self, new_color):
if new_color == "Dark":
bg = "black"
fg = "white"
else:
bg = "white"
fg = "black"
self.text.config(bg=bg, fg=fg, insertbackground=fg)
self.numberLines.config(bg=bg)
def onScrollPress(self, *args):
self.scrollbar.bind("<B1-Motion>", self.numberLines.redraw)
def onScrollRelease(self, *args):
self.scrollbar.unbind("<B1-Motion>", self.numberLines.redraw)
def onPressDelay(self, *args):
self.after(2, self.numberLines.redraw)
def get(self, *args, **kwargs):
return self.text.get(*args, **kwargs)
def insert(self, *args, **kwargs):
return self.text.insert(*args, **kwargs)
def delete(self, *args, **kwargs):
return self.text.delete(*args, **kwargs)
def index(self, *args, **kwargs):
return self.text.index(*args, **kwargs)
def redraw(self):
self.numberLines.redraw()
class TextLineNumbers(Canvas):
def __init__(self, *args, **kwargs):
Canvas.__init__(self, *args, **kwargs, highlightthickness=0)
self.textwidget = None
def attach(self, text_widget):
self.textwidget = text_widget
def redraw(self, *args):
'''redraw line numbers'''
self.delete("all")
i = self.textwidget.index("#0,0")
while True :
dline= self.textwidget.dlineinfo(i)
if dline is None: break
y = dline[1]
linenum = str(i).split(".")[0]
self.create_text(2, y, anchor="nw", text=linenum, fill="#808090") #606366
i = self.textwidget.index("%s+1line" % i)
if __name__ == "__main__":
window = Tk()
color_mode = "Light"
window.geometry("%dx%d+0+0" % (window.winfo_screenwidth(), window.winfo_screenheight()))
my_text = ultra_text(window, window = window, color_mode="Dark")
my_text.place(relx=.5, rely=.5, anchor=CENTER)
def change_color_mode():
global my_text
global color_mode
if color_mode == "Dark":
color_mode = "Light"
else:
color_mode = "Dark"
if color_mode == "Dark":
bg = "black"
else:
bg = "white"
my_text.change_color(color_mode) #This part is fine and does the bulk of the color_changing
my_text.configure(bg=bg)
change_color_mode_button.config(highlightbackground=bg)
window.configure(bg=bg)
change_color_mode_button = Button(window, text="Change Color Mode", command=change_color_mode)
change_color_mode_button.pack()
window.mainloop()
It seems that because it is inputting the width and height from kwargs that it alters the width and height variables to have a problem but I can't be quite sure.
Hello, please i'm making a tkinter window with an image and two
buttons, and using those buttons previous
and next to switch between images whenever i click the buttons
the problem is that i dont know how to change the image everytime i
click one of the buttons
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
import os
BASE_DIR = os.path.dirname(os.path.relpath(__file__))
image_dir = os.path.join(BASE_DIR, "my_training_face")
class MainUi(tk.Tk):
def listDir(dir):
global names
global dict
names = []
dict = {}
fileNames = os.listdir(dir)
for fileName in fileNames:
names.append(fileName)
i = 0
while i < len(names):
dict[i] = (names[i])
i = i + 1
return dict
listDir(image_dir)
def get_name(self, cmpt):
try:
self.name = names[cmpt]
return self.name
except:
return "Empty Case"
def get_nbrHab(self):
try:
self.nbr = len(names)
return self.nbr
except:
pass
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.geometry("300x400")
self.geometry("+500+100")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.place(relx=0, rely=0, relwidth=1, relheight=1)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.configure(background='white')
# self.master.configure(background='black')
self.label = tk.Label(self, text=MainUi.get_name(self,0))
self.label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Next", width=7, command = lambda: self.next_clicked())
button1.place(relx=0.8, rely=0.9)
button2 = ttk.Button(self, text="Back", width=7, command = lambda: self.back_clicked())
button2.place(relx=0.1, rely=0.9)
self.imgs = []
self.resizing = []
self.finals = []
try:
for i in range(MainUi.get_nbrHab(self)):
self.imgs.append(Image.open(image_dir + "/" + MainUi.get_name(self, self.compteur) + "/1.jpg"))
self.resizing.append(self.imgs[i].resize((160,120), Image.ANTIALIAS))
self.finals.append(ImageTk.PhotoImage(self.resizing[i]))
except:
return
self.label_img = tk.Label(self, bg='white', image= self.finals[0])
self.label_img.image = self.finals[0]
self.label_img.place(relx=0.21, rely=0.15)
compteur = 0
def next_clicked(self):
self.label.config(text=MainUi.get_name(self,self.compteur))
print(self.compteur)
self.label_img.config(image=self.finals[self.compteur])
self.label_img.image=self.finals[self.compteur]
self.compteur += 1
def back_clicked(self):
self.compteur-=1
app = MainUi()
app.mainloop()
I usually use .config() to update the widgets for example ->
from tkinter import *
root = Tk()
label = Label(root, text='This will be updated!')
btn = Button(root, text="Update It", command=lambda : label.config(text='Its Updated!'))
btn.pack()
root.mainloop()
Same thing can be done using Images and Other Widgets Like Buttons
There is issue in your code:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
...
try:
for i in range(MainUi.get_nbrHab(self)):
# "self.compteur" is not yet defined here
# also you should use "i" instead of "self.compteur"
self.imgs.append(Image.open(image_dir + "/" + MainUi.get_name(self, self.compteur) + "/1.jpg"))
...
So the correct code should be:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
...
self.compteur = 0
try:
for i in range(MainUi.get_nbrHab(self)):
self.imgs.append(Image.open(os.path.join(image_dir, MainUi.get_name(self, i), "1.jpg")))
...
Note that I don't know the last argument 1.jpg is correct or not, but it seems not required.
Then the functions for Back and Next buttons should be something like below:
def next_clicked(self):
if self.compteur+1 < len(self.finals):
self.compteur += 1
self.label.config(text=MainUi.get_name(self, self.compteur))
self.label_img.config(image=self.finals[self.compteur])
#self.label_img.image=self.finals[self.compteur] # this line is not necessary actually
def back_clicked(self):
if self.compteur > 0:
self.compteur -= 1
self.label.config(text=MainUi.get_name(self,self.compteur))
self.label_img.config(image=self.finals[self.compteur])
Here is the code. This is just for reference. This is root15.png, root14.png,rot13.png respectively.
from tkinter import *
class ChangeImage:
def __init__(self,root):
self.root = root
self.root.geometry("700x600")
self.list1=[r"C:\Users\91996\Documents\Visual studio code\Visual Studio\rot13.png",r"C:\Users\91996\Documents\Visual studio code\Visual Studio\root15.png",r'C:\Users\91996\Documents\Visual studio code\Visual Studio\root14.png']
self.compteur=0
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl=Label(self.root,image=self.im)
self.im_lbl.pack()
Button(self.root,text="Next Picture",command=self.next_picture).pack(pady=5)
Button(self.root,text="Back Picture",command=self.back_picture).pack(pady=5)
def next_picture(self):
if self.compteur>=len(self.list1)-1:
pass
else:
self.compteur+=1
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl.config(image=self.im)
def back_picture(self):
if self.compteur<=0:
pass
else:
self.compteur-=1
self.im=PhotoImage(file=self.list1[self.compteur])
self.im_lbl.config(image=self.im)
root=Tk()
ob=ChangeImage(root)
root.mainloop()
How to duplicate tkinter text widget so that you can add it in every Notebook tab? I am writing an editor using tkinter, and I have also added undo functions. The problem is, when I add a new tab, the undo function works only for that tab. When I delete that tab, it is not working for other tabs either.
from tkinter.ttk import Notebook
import tkinter.messagebox
class TextClass(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
self.text = Text(self, bg='white', foreground="black", undo=True,
insertbackground='black', height=35, width=135,
selectbackground="blue")
self.scrollbar = Scrollbar(self, orient=VERTICAL, command=self.text.yview)
self.text.configure(yscrollcommand=self.scrollbar.set)
self.numberLines = TextLineNumbers(self, width=40, bg='#2A2A2A')
self.numberLines.attach(self.text)
self.scrollbar.pack(side=RIGHT, fill=Y)
self.numberLines.pack(side=LEFT, fill=Y, padx=(5, 0))
self.text.pack(fill='both', expand=True)
self.text.bind("<Key>", self.onPressDelay)
self.text.bind("<Button-1>", self.numberLines.redraw)
self.scrollbar.bind("<Button-1>", self.onScrollPress)
self.text.bind("<MouseWheel>", self.onPressDelay)
def undo():
try:
self.text.edit_undo()
except TclError:
tkinter.messagebox.showerror(
"Nothing to Undo",
"You have not typed anything to undo. Please type and try again!"
)
undo_button = Button(toolbar, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
width=6)
undo_button.place(x=170, y=5)
hover(undo_button, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
def redo():
try:
self.text.edit_redo()
except TclError:
tkinter.messagebox.showerror(
"Nothing to Redo",
"You have not done an undo to redo. Please type and try again!"
)
redo_button = Button(toolbar, text='Redo', relief='ridge', command=redo, bg='black', fg='white',
width=6)
redo_button.place(x=230, y=5)
hover(redo_button, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
def onScrollPress(self, *args):
self.scrollbar.bind("<B1-Motion>", self.numberLines.redraw)
def onScrollRelease(self, *args):
self.scrollbar.unbind("<B1-Motion>", self.numberLines.redraw)
def onPressDelay(self, *args):
self.after(2, self.numberLines.redraw)
def get(self, *args, **kwargs):
return self.text.get(*args, **kwargs)
def insert(self, *args, **kwargs):
return self.text.insert(*args, **kwargs)
def delete(self, *args, **kwargs):
return self.text.delete(*args, **kwargs)
def index(self, *args, **kwargs):
return self.text.index(*args, **kwargs)
def redraw(self):
self.numberLines.redraw()
class TextLineNumbers(Canvas):
Canvas.text_widget = None
def attach(self, text_widget):
self.text_widget = text_widget
def redraw(self, *args):
self.delete("all")
i = self.text_widget.index("#0,0")
while True:
d_line = self.text_widget.dlineinfo(i)
if d_line is None:
break
y = d_line[1]
line_numbers = str(i).split(".")[0]
self.create_text(2, y, anchor="nw", text=line_numbers, fill="white")
i = self.text_widget.index("%s+1line" % i)
def add_tab():
global tab
tab = Frame(notebook)
notebook.add(tab, text=f'{"Untitled1.txt": ^20}')
global text
text = TextClass(tab, bg='white')
text.pack()
text.text.focus()
def close_tab():
notebook.forget('current')
def hover(widget, entrance_foreground, exit_fg, on_entrance, on_exit):
widget.bind("<Enter>", func=lambda e: widget.config(
bg=on_entrance,
fg=entrance_foreground
))
widget.bind("<Leave>", func=lambda e: widget.config(
bg=on_exit,
fg=exit_fg
))
root = Tk()
root.config(bg='white')
root.geometry("1260x680")
toolbar = Frame(root, bg='white', height=45)
toolbar.pack(expand=False, fill='x')
notebook = Notebook(root)
tab = Frame(notebook)
notebook.add(tab, text=f'{"Untitled.txt": ^20}')
notebook.place(x=50, y=60)
text = TextClass(tab, bg='white')
text.pack()
add_tab_btn = Button(toolbar, text='Add new tab', command=add_tab, relief='ridge', bg='black', fg='white')
hover(add_tab_btn, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
add_tab_btn.place(x=15, y=5)
delete_tab_btn = Button(toolbar, text='Delete tab', command=close_tab, relief='ridge', bg='black', fg='white')
hover(delete_tab_btn, on_entrance='white', on_exit='black', entrance_foreground='black', exit_fg='white')
delete_tab_btn.place(x=100, y=5)
root.after(200, text.redraw())
scr_menu()
root.mainloop()
As I suspected you keep making new undo/redo buttons over the old ones each time you create a new tab. This means that the button only works for the last tab. To fix that problem you have to move the undo/redo buttons inside the notebook. Just as a proof of concept change this line:
undo_button = Button(toolbar, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
width=6)
to
undo_button = Button(self, text='Undo', relief='ridge', command=undo, bg='black', fg='white',
width=6)
i have written the "instruction" for my tkinter program on a .txt file and when the Instruction button is pressed, I would like a separate window to be created with the instruction copied from the text file into the output box.
I would be very grateful if anyone could help me.
this is what I have done so far but it doesn't work at all.
instructionBtn = Button(window, text="?", command=start, height = 5, width =30,fg="black", bg="lime green", font=("Comic Sans MS", 10))
instructionBtn.grid(row=29, column=1, sticky=S)
window.mainloop()
def instruction():
instructionwindow = Tk() #create window
instructionwindow.geometry("500x350")
instructionwindow.title("Instruction")
instructionwindow.configure(background='white')
instructionFile=open("Instruction.txt","r")
instruction.read
textboxOutput = Text(window, wrap="Instruction.txt", width = 150, height = 20)
textboxOutput.grid(row = 20, column = 0, columnspan=10)
instruction.mainloop()
When I want a second window, often a message box will do the trick, like so:
from Tkinter import *
from tkMessageBox import showinfo
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=LEFT)
self.instr = Button(frame, text="Instruction", command=self.instruction)
self.instr.pack(side=LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instructions available'
showinfo("Instructions", message)
root = Tk()
app = App(root)
root.mainloop()
Or, if you prefer an OOP style:
# template: https://stackoverflow.com/a/17470842/8747
# Use Tkinter for python 2, tkinter for python 3
import Tkinter as tk
import tkMessageBox
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = tk.Button(
self, text="QUIT", fg="red", command=self.quit)
self.instr = tk.Button(
self, text="Instruction", command=self.instruction)
self.button.pack(side=tk.LEFT)
self.instr.pack(side=tk.LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instruction available'
msg = tkMessageBox.showinfo("Instruction", message)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Of course, sometimes a message box isn't flexible enough, and you need to create a top-level window:
# template: https://stackoverflow.com/a/17470842/8747
# Use Tkinter for python 2, tkinter for python 3
import Tkinter as tk
import tkMessageBox
class Instruction(tk.Toplevel):
def __init__(self, parent, message, *args, **kwargs):
tk.Toplevel.__init__(self, parent, *args, **kwargs)
self.msg = tk.Message(self, text=message)
self.button = tk.Button(self, text="Dismiss", command=self.destroy)
self.msg.pack()
self.button.pack()
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = tk.Button(
self, text="QUIT", fg="red", command=self.quit)
self.instr = tk.Button(
self, text="Instruction", command=self.instruction)
self.button.pack(side=tk.LEFT)
self.instr.pack(side=tk.LEFT)
def instruction(self):
try:
with open("instruction.txt") as fp:
message = fp.read()
except IOError:
message = 'No instruction available'
msg = Instruction(self, message)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()