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)
Related
I'm trying to have a function perform an action using an if statement on a boolean_var().
Essentially, boolean_var.get() -> if True -> popup messagebox message, else -> Pass.
It's correctly "get"-ing the True statement and returning a 1, but will not enact the function I've called/pointed to afterward.
Any advice, or even pointing me to any specific reading material that may help would be greatly appreciated.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import messagebox
class WotcPull(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.master=master
self.NewWindow = tk.Toplevel(master)
self.NewWindow.title("Sources")
self.NewWindow.minsize(600, 400)
self.NewWindow.geometry("600x400")
self.NewWindow.columnconfigure(0, weight=1)
self.NewWindow.rowconfigure(0, weight=1)
self.NewWindow.rowconfigure(1, weight=15)
SourceOutputFrame = tk.Frame(
self.NewWindow,
bg="#414041",
borderwidth=1,
padx=3,
pady=10
)
SourceOutputFrame.grid(column=0, row=1, sticky=tk.N+tk.S+tk.W+tk.E)
SourceOutputFrame.columnconfigure(0, weight=1)
SourceOutputFrame.rowconfigure
SubOutputFrame = tk.Frame(
master=SourceOutputFrame,
bg="#414041",
borderwidth=1,
padx=3,
pady=10
)
SubOutputFrame.pack(side='top',fill='both',expand=1)
SubOutputButton["state"] = "disabled"
wotc_var = tk.BooleanVar()
wotc_yes = tk.Radiobutton(
master=SubOutputFrame,
text="Yes",
variable=wotc_var,
value=True,
foreground= "#C9C1B2",
background= "#414041",
borderwidth=1,
font=(25)
).grid(row=0, column=1, sticky=tk.NSEW)#, command=self.callback)
wotc_no = tk.Radiobutton(
master=SubOutputFrame,
text="No",
variable=wotc_var,
foreground= "#C9C1B2",
background= "#414041",
borderwidth=1,
font=(25),
value=False
).grid(row=0, column=2)#, command=self.callback)
def popupmsg():
messagebox.showinfo(message="success") #<- This will not work and I just can't figure out why
def SaveSources():
#messagebox.showinfo(message=wotc_var.get()) <- This works fine and returns 1 if wotc_yes is checked
if wotc_var.get() == 1:
#messagebox.showinfo(message=wotc_var.get()) <- Also works fine and returns 1 if wotc_yes is checked
popupmsg #<- the problem function that will not work for me
else:
messagebox.showinfo(message=wotc_var.get())
SaveSourcesButton = tk.Button(
master=SubOutputFrame,
text='Save',
command=SaveSources,
height=3,
width=15,
bg="#414041",
fg="#C9C1B2"
)
SaveSourcesButton.grid(
row=4,
column=0,
sticky=tk.EW,
padx=10,
pady=10
)
I'm very new to python and I don't know why but my code keeps crashing when I implement the listener specifically at the
with Listener(on_press=press) as listener: listener.join() and when it crashes it doesn't give me a error message
here's the entirety of my code:
import tkinter as tk
from tkinter.constants import LEFT, N, NE, NW, W
from PIL import Image, ImageTk
import tkinter.ttk as ttk
import time
from pynput.keyboard import Key, Controller
from pynput.keyboard import Listener
self = tk.Tk()
self.title('Key spammer')
self.iconbitmap("D:\Vs code repos\Key spammer\keyboard-icon.ico")
#Set the geometry of frame
self.geometry("350x550")
self.resizable(False, False)
#Button exit function
def exit_prog():
exit()
def press(key):
print(key)
#press interval
self.grid_rowconfigure(20, weight=1)
self.grid_columnconfigure(20, weight=1)
labelframe = ttk.Labelframe(self,text= "Press interval")
labelframe.grid(row=1, column=0, padx= 25, sticky=N)
class Lotfi(ttk.Entry):
def __init__(self, master=None, **kwargs):
self.var = tk.StringVar()
ttk.Entry.__init__(self, master, textvariable=self.var, **kwargs)
self.old_value = ''
self.var.trace('w', self.check)
self.get, self.set = self.var.get, self.var.set
def check(self, *args):
if self.get().isdigit():
# the current value is only digits; allow this
self.old_value = self.get()
else:
# there's non-digit characters in the input; reject this
self.set(self.old_value)
#Entry interval
hoursentry = Lotfi(labelframe, width= 10)
hoursentry.grid(row=1, column=1, padx= 5, pady= 10)
hoursentry.insert(0, '0')
hourslabel = ttk.Label(labelframe, text= "hours")
hourslabel.grid(row=1, column=2)
minutesentry = Lotfi(labelframe, width= 10)
minutesentry.grid(row=1, column=3)
minutesentry.insert(0, '0')
minuteslabel = ttk.Label(labelframe, text= "mins")
minuteslabel.grid(row=1, column=4)
secondsentry = Lotfi(labelframe, width= 10)
secondsentry.grid(row=1, column=5)
secondsentry.insert(0, '0')
secondeslabel = ttk.Label(labelframe, text= "secs")
secondeslabel.grid(row=1, column=6)
labelframekey = ttk.Labelframe(self,text= "Key options")
labelframekey.grid(row=2, column=0, padx= 25, pady= 25, sticky=NW)
#labelframekey.pack(side= LEFT)
keypress = ttk.Label(labelframekey, text= "Key pressed", font= 10)
keypress.grid(row=2, column=0, sticky= N)
with Listener(on_press=press) as listener:
listener.join()
class Keyreg(ttk.Entry):
def __init__(self, master=None, **kwargs):
self.var = tk.StringVar()
ttk.Entry.__init__(self, master, textvariable=self.var, **kwargs)
self.old_value = ''
self.var.trace('w', self.check)
self.get, self.set = self.var.get, self.var.set
def check(self, *args):
if press:
self.set(self.old_value)
#Keyreg = Keyreg(labelframekey, width= 10)
#keyreg.grid(row=2, column=1, sticky= N)
'''
#Creation of Option buttons
button = ttk.Button(self, text = 'Exit', command = exit_prog, width = 25)
button.grid(row=2, column=1,padx= 5, ipadx=15, ipady=10)
button2 = ttk.Button(self, text = 'Exit', command = exit_prog, width = 25)
button2.grid(row=2, column=2, ipadx=15, ipady=10)
button3 = ttk.Button(self, text = 'Exit', command = exit_prog, width = 25)
button3.grid(row=3, column=1, ipadx=15, ipady=10)
button4 = ttk.Button(self, text = 'Exit', command = exit_prog, width = 25)
button4.grid(row=3, column=2, ipadx=15, ipady=10)
'''
#Top menu for keybinds
tk.mainloop()
I tried to figure out the problem myself but when i imported it to another tab and used multiple different methods it still never crashed on those test
Please help me
You didn't show full error message so I don't know what is your real problem but I see one problem.
Code
with Listener(on_press=press) as listener:
listener.join()
runs code which waits for the end of listener and it stops rest of code
if you want to run it at the same time as tkinter then you should run as
with Listener(on_press=press) as listener:
tk.mainloop()
listener.join()
or more similar to threading (which is used by listener)
listener = Listener(on_press=press)
listener.start()
tk.mainloop()
listener.join()
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()
I am new to python, the above figure is two UI, I was trying to insert the two tab from the right side UI into the left side UI. But I had met some error and no idea how to solve.
Now below is the original coding of the left side UI
import tkinter as tkk
from tkinter import *
from tkinter import messagebox
import os.path
import hashlib
import sys
import time
import getpass
from tkinter import filedialog
import platform
import getpass
import os,sys
import tkinter.font as font
from tkinter import ttk
from tkinter import StringVar
import Consts
import shutil
class Page(tkk.Frame):
def __init__(self, *args, **kwargs):
tkk.Frame.__init__(self, *args, **kwargs)
def show(self):
self.lift()
class Page1(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
my_system=platform.uname()
#computer information
comsys=my_system.system
comnode=my_system.node
comrel=my_system.release
comver=my_system.version
commac=my_system.machine
compro=my_system.processor
comuser=getpass.getuser()
label1 = tkk.Label(self, text="System: "+comsys,bg='white')
label1.grid(row=1,column=1,pady=10,sticky='w')
label2 = tkk.Label(self, text="Computer Name: "+comnode,bg='white')
label2.grid(row=2,column=1,pady=10,sticky='w')
label3 = tkk.Label(self, text="Release: "+comrel,bg='white')
label3.grid(row=3,column=1,pady=10,sticky='w')
label4 = tkk.Label(self, text="Version: "+comver,bg='white')
label4.grid(row=4,column=1,pady=10,sticky='w')
label5 = tkk.Label(self, text="Machine: "+commac,bg='white')
label5.grid(row=5,column=1, pady=10,sticky='w')
label6 = tkk.Label(self, text="Processor: "+compro,bg='white')
label6.grid(row=6,column=1, pady=10,sticky='w')
label7 = tkk.Label(self, text="Username: "+comuser,bg='white')
label7.grid(row=7,column=1,pady=10,sticky='w')
#computer usage hold first, no idea how to do
class Page2(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
tabControl=ttk.Notebook(self)
qsFrame = ttk.Frame(tabControl)
fsFrame = ttk.Frame(tabControl)
csFrame = ttk.Frame(tabControl)
#tab
tabControl.add(qsFrame, text='Quick Scan')
tabControl.add(fsFrame, text='Full Scan')
tabControl.add(csFrame, text='Custom Scan')
tabControl.pack(expand=1,fill="both")
class Page3(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
label = tkk.Label(self, text="This is page 3")
label.grid(row=2,column=1)
def mytools():
total, used, free = shutil.disk_usage("/")
print("Total:%d GB" %(total // (2**30)))
print("Used:%d GB" %(used // (2**30)))
print("Free:%d GB" %(free // (2**30)))
if free <= total/2:
clean = os.popen('Cleanmgr.exe/ sagerun:1').read()
#print(clean)
def btn1():
if __name__ =="__main__":
mytools()
button1=ttk.Button(self,text="Clean Up",command=btn1)
button1.grid(row=3,column=2)
class Page4(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs,bg='white')
label = tkk.Label(self, text="This is page 4")
label.grid(row=2,column=1)
class MainView(tkk.Frame):
def __init__(self, *args, **kwargs):
tkk.Frame.__init__(self, *args, **kwargs)
p1 = Page1(self)
p2 = Page2(self)
p3 = Page3(self)
p4 = Page4(self)
buttonframe = tkk.Frame(self)
container = tkk.Frame(self,bg='white')
buttonframe.pack(side="left", fill="x", expand=False)
container.pack(side="left", fill="both", expand=True)
buttonframe.grid_rowconfigure(0,weight=1)
buttonframe.grid_columnconfigure(0,weight=1)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p2.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p3.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p4.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
stats_btn = tkk.PhotoImage(file='C:/FYP/SecuCOM2022/icon&pic/stats.png')
scanner_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\scanner.png')
speedup_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\speedup.png')
settings_btn = tkk.PhotoImage(file='C:\FYP\SecuCOM2022\icon&pic\settings.png')
#logo
logo=tkk.PhotoImage(file="C:\FYP\SecuCOM2022\icon&pic\g (1).png")
label=tkk.Label(buttonframe,image=logo)
label.grid(row=0,column=0, padx=10,pady=10)
logo.image = logo
b1 = tkk.Button(buttonframe, image=stats_btn, command=p1.show, borderwidth=0)
b2 = tkk.Button(buttonframe, image=scanner_btn, command=p2.show, borderwidth=0)
b3 = tkk.Button(buttonframe, image=speedup_btn, command=p3.show, borderwidth=0)
b4 = tkk.Button(buttonframe, image=settings_btn, command=p4.show, borderwidth=0)
b1.image = stats_btn
b2.image = scanner_btn
b3.image = speedup_btn
b4.image = settings_btn
b1.grid(row=1,column=0,padx=10,pady=10)
b2.grid(row=2,column=0,padx=10,pady=10)
b3.grid(row=3,column=0,padx=10,pady=10)
b4.grid(row=4,column=0,padx=10,pady=10)
if __name__ == "__main__":
root= Tk()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
main.grid_rowconfigure(0,weight=1)
main.grid_columnconfigure(0,weight=1)
root.title("SecuCOM2022")
root.geometry("600x300")
root.maxsize(600,375)
root.minsize(600,375)
root.iconbitmap('C:\FYP\SecuCOM2022\icon&pic\g.png')
root.mainloop()
root.mainloop()
#GUI end`
Next below here is the right side UI coding, there are multiple files for it. I just show files related with the UI only.
Consts.py
ENTRY_WIDTH = 50
FileReportTab.py
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
from tkinter import StringVar
import time
import os.path
import sys
from VTPackage import Consts
class FileReportTab:
def __init__(self, root, frame, vtClient):
self.root = root
self.frame = frame
self.vtClient = vtClient
self.mainVTURLframe = ttk.LabelFrame(frame, text=' File report')
self.mainVTURLframe.grid(column=0, row=1, padx=8, pady=4)
ttk.Label(self.mainVTURLframe, text="Progress:").grid(column=0, row=1, sticky='W') # <== right-align
self.progressBar = ttk.Progressbar(self.mainVTURLframe, orient='horizontal', length=300, mode='determinate')
self.progressBar.grid(column=1, row=1)
ttk.Label(self.mainVTURLframe, text="File path:").grid(column=0, row=2, sticky='W') # <== right-align
self.filePath = StringVar()
filePathEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.filePath, state='readonly')
filePathEntry.grid(column=1, row=2, sticky='W')
ttk.Label(self.mainVTURLframe, text="Status:").grid(column=0, row=3, sticky='W') # <== right-align
self.status = StringVar()
statusEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.status, state='readonly')
statusEntry.grid(column=1, row=3, sticky='W')
ttk.Label(self.mainVTURLframe, text="Positive Indications:").grid(column=0, row=4, sticky='W') # <== right-align
self.positiveIndications = StringVar()
positiveIndicationsEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.positiveIndications, state='readonly')
positiveIndicationsEntry.grid(column=1, row=4, sticky='W')
ttk.Label(self.mainVTURLframe, text="SHA1:").grid(column=0, row=5, sticky='W') # <== right-align
self.sha1 = StringVar()
sha1Entry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.sha1, state='readonly')
sha1Entry.grid(column=1, row=5, sticky='W')
ttk.Label(self.mainVTURLframe, text="SHA256:").grid(column=0, row=6, sticky='W') # <== right-align
self.sha256 = StringVar()
sha256Entry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=self.sha256, state='readonly')
sha256Entry.grid(column=1, row=6, sticky='W')
chooseFileButton = ttk.Button(self.mainVTURLframe, text="Choose File", width=40, command=self._scanFile).grid(column=1, row=0)
self.scanCheckingTimeInterval = 25000 # This is the amount of time we are going to wait before asking VT again if it already processed our scan request
for child in self.mainVTURLframe.winfo_children():
child.grid_configure(padx=4, pady=2)
def showResults(self, results):
try:
#self.file_Path = self.filePath
self.sha1.set(results["sha1"])
self.sha256.set(results["sha256"])
self.positiveIndications.set(results["positives"])
if results["positives"] == 0:
messagebox.showwarning("Analysis Info","File is Safe.\nOur Scanners found nothing Malicious")
elif results["positives"] <= 5:
messagebox.showwarning("Analysis Alert", "Given File may be Malicious")
elif results["positives"] >= 5:
messagebox.showwarning("Analysis Alert", f"Given File is Malicious.\nAdvice you remove the file from your System!")
res = messagebox.askyesno("Analysis Alert","The given file is highly Malicious.\nDo you want to Delete it permanently?")
if res == 1:
print("Attemting to delete file...")
time.sleep(1)
os.remove(self.filePath1)
#if os.PathLike(_scanFile.filePath):
# os.remove(self.filePath)
else:
print("This file cannot be deleted. Please do not use the fie. It's Malicious")
except Exception as e:
messagebox.showerror('Error', e)
def checkStatus(self):
try:
self.scanResult = self.vtClient.get_file_report(self.scanID)
print(self.scanResult)
if self.scanResult["response_code"] == -2: # By reading the next line, you can understand what is the meaning of the -2 response ode
self.status.set("Scanning...")
self.progressBar['value'] = self.progressBar['value'] + 5
self.root.update_idletasks()
self.mainVTURLframe.after(self.scanCheckingTimeInterval, self.checkStatus)
else:
self.hasScanFinished = True
self.showResults(self.scanResult)
self.status.set("Finished!")
self.progressBar['value'] = 100
except Exception as e:
if "To much API requests" in str(e):
pass
def _scanFile(self):
try:
self.progressBar['value'] = 0
self.filePath1 = filedialog.askopenfilename(initialdir="/", title="Select file for VT", filetypes=(("EXE files", "*.exe"), ("all files", "*.*")))
if (self.filePath): # Only if the user chose a file, we will want to continue the process
self.filePath.set(self.filePath1)
self.status.set("Sending file...")
self.progressBar['value'] = 10
self.root.update_idletasks()
self.scanID = self.vtClient.scan_file(self.filePath1)
self.hasScanFinished = False
if not self.hasScanFinished:
self.scanResult = self.vtClient.get_file_report(self.scanID)
print(self.scanResult)
self.checkStatus()
# We could have been using time.sleep() or time.wait(), but then our UI would get stuck.
# by using after, we are initiating a callback in which does not blocks our event loop
except Exception as e:
messagebox.showerror('Error', e)
URLreportTab.py
from tkinter import ttk
from tkinter import StringVar
from VTPackage import Consts
class URLreportTab:
def __init__(self, root, frame, vtClient):
self.root = root
self.frame = frame
self.mainVTURLframe = ttk.LabelFrame(frame, text=' URL report tab!')
# using the tkinter grid layout manager
self.mainVTURLframe.grid(column=0, row=0, padx=8, pady=4)
ttk.Label(self.mainVTURLframe, text="URL:").grid(column=0, row=0, sticky='W') # What does sticky does? Sticky sayes where to stick the label to : N,S,E,W
urlEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH)
urlEntry.grid(column=1, row=0, sticky='E')
ttk.Label(self.mainVTURLframe, text="Positive Indications:").grid(column=0, row=1, sticky='W') # <== right-align
Positive = StringVar()
PositiveEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=Positive, state='readonly')
PositiveEntry.grid(column=1, row=1, sticky='W')
ttk.Label(self.mainVTURLframe, text="Detections:").grid(column=0, row=2, sticky='W') # <== right-align
detections = StringVar()
detectionsEntry = ttk.Entry(self.mainVTURLframe, width=Consts.ENTRY_WIDTH, textvariable=detections, state='readonly')
detectionsEntry.grid(column=1, row=2, sticky='W')
self.notificationFrame = ttk.LabelFrame(self.frame, text=' Notifications', width=40)
# using the tkinter grid layout manager
self.notificationFrame.grid(column=0, row=1, padx=8, pady=10, sticky='W')
ttk.Label(self.notificationFrame, text="Errors:").grid(column=0, row=0, sticky='W') # <== increment row for each
Error = StringVar()
ErrorEntry = ttk.Entry(self.notificationFrame, width=Consts.ENTRY_WIDTH, textvariable=Error, state='readonly')
ErrorEntry.grid(column=1, row=0, sticky='W')
def _cleanErrorMessage(): # We could have been doing this without a function, but it is more neat that way
Error.set("")
def _getReport():
# the _ notation before a function means that this function is internal to the class only. As python cannot really prevent you from using it outside the class (as C# for example) the notation is being used to warn other developers not to call this function outside the class
try:
_cleanErrorMessage() # Starting with cleaning the error message bar
if not urlEntry.get():
print('Please enter a URL')
Error.set("Please enter a URL!")
return
urlToCheck = urlEntry.get()
response = vtClient.get_url_report(urlToCheck)
print(response)
Positive.set(response["positives"])
scans = response["scans"]
findings = set()
for key, value in scans.items():
if value["detected"]:
findings.add(value["result"])
detections.set(",".join([str(finding) for finding in findings]))
except Exception as e:
print(e)
Error.set(e)
checkURLinVTButton = ttk.Button(self.mainVTURLframe, text='Check Now!', command=_getReport).grid(column=2, row=0)
# Instead of setting padding for each UI element, we can just iterate through the children of the main UI object.
for child in self.mainVTURLframe.winfo_children():
child.grid_configure(padx=4, pady=2)
for child in self.notificationFrame.winfo_children():
child.grid_configure(padx=4, pady=2)
VTApp.py
import tkinter as tk
import configparser
from tkinter import Menu
from tkinter import ttk
from tkinter import messagebox
from VTPackage import URLreportTab
from VTPackage import FileReportTab
from VTPackage import VTClient
config = configparser.ConfigParser()
config.read('config.ini')
class VTApp:
def __init__(self):
# Loading the config file
self.config = configparser.ConfigParser()
self.config.read('config.ini')
self.virusTotalAPIkey = config['VirusTotal']['apiKey']
self.vtClient = VTClient.VTClient(self.virusTotalAPIkey)
self.root = tk.Tk()
self.root.title("Virus Total UI")
self.menuBar = Menu()
self.root.config(menu=self.menuBar)
self.fileMenu = Menu(self.menuBar, tearoff=0)
self.fileMenu.add_command(label="New")
self.fileMenu.add_separator()
self.menuBar.add_cascade(label="File", menu=self.fileMenu)
if not self.vtClient.is_API_key_valid():
messagebox.showerror('Error', "API key is not valid! Check your config file")
def _quit():
self.root.quit() # The app will exist when this function is called
self.root.destroy()
exit()
self.fileMenu.add_command(label="Exit", command=_quit) # command callback
self.tabControl = ttk.Notebook(self.root) # Create Tab Control
self.urlFrame = ttk.Frame(self.tabControl)
self.urlTab = URLreportTab.URLreportTab(self.root, self.urlFrame, self.vtClient)
self.tabControl.add(self.urlFrame, text='URL')
self.fileFrame = ttk.Frame(self.tabControl)
self.fileTab = FileReportTab.FileReportTab(self.tabControl, self.fileFrame, self.vtClient)
self.tabControl.add(self.fileFrame, text='File')
self.tabControl.pack(expand=1, fill="both") # Pack to make visible
def start(self):
self.root.mainloop()
Main.py
from VTPackage import VTApp
vtApp = VTApp.VTApp()
vtApp.start()
This is the original code, Sorry for the spacing error, I copy&paste from vsc and it seem like the got some spacing error after Class. So basically this is the original code and I try like import VTApp and code inside class Page2 like
vtApp = VTApp.VTApp()
vtApp.start()
and change some coding in the VTApp.py but it doesn't work.... Does anyone know how to make the script works? I been trying and trying for a week and still couldn't get the solution.
You cannot move a widget from one window to another in tkinter. You will have to recreate the tab in the other window.
I'm building my first app with Tkinter and I'm stuck with instance of class.
I followed this tutorial https://pythonprogramming.net/change-show-new-frame-tkinter/ but some questions remain :
- how does mean controller as argument for the init line in the differents class ?
- I would like to create instance of classA in classB and inversely but I'm getting wrong with arguments !
- About organisation code, I created 2 class (one for each frame) and writting function related in the good one, should I split them in a 3rd class ? What is the most convenient way ?
Actualy the code is running but I would like to add some setup tools in the 2nd window !
What do I must do to can modify some parameters in the SettingsPage Class and update and use them in the StartPage class ? For example I would like to modify ecartementlogo.
setp = SettingsPage(controller, StartPage) give me AttributeError: type object 'StartPage' has no attribute 'tk'
Sorry for posting all the code but I not sure about what can I remove for the example
import tkinter as tk
from PIL import Image, ImageDraw, ImageColor, ImageFont, ImageChops, ImageColor, ImageFont, ImageTk as PIL
import os
import utilitary as utilitary
import tkinter.ttk as ttk
from tkinter import filedialog
from configparser import ConfigParser
LARGE_FONT= ("Verdana", 12)
basewidth = 400
config = ConfigParser()
config.read('config.ini')
class DotaCasterKit(tk.Tk):
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.iconbitmap(self,default='icone.ico')
tk.Tk.wm_title(self, "vs Creator")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, SettingsPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
menu = tk.Menu(container)
menu.config(background='#2B547E', fg='#2B547E')
tk.Tk.config(self, menu=menu)
file = tk.Menu(menu)
file.add_command(label="Préférences", command=self.show_settings)
file.add_command(label="Exit",command=quit)
menu.add_cascade(label="File", menu=file)
refresh = tk.Menu(menu)
menu.add_cascade(label="Refresh", menu=refresh)
def show_settings(self):
frame = self.frames[SettingsPage]
frame.tkraise()
class StartPage(tk.Frame):
def choose_background(self):
print(self.path_slogos)
self.background_file = filedialog.askopenfilename(initialdir='C:/Users/.../VS')
self.background = PIL.Image.open(self.background_file).convert('RGBA')
self.background_name = os.path.basename(self.background_file)
self.var1.set(self.background_name)
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def choose_slogos_path(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set(os.path.basename(self.path_slogos))
return self.path_slogos
def create_list_logos(self):
self.path_slogos = filedialog.askdirectory(initialdir='C:/Users/.../Logos')
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.files = []
self.list_files_names =[]
print(self.path_slogos)
for r, d, f in os.walk(self.path_slogos):
for file in f:
if '.png' in file and 'background' not in file:
self.files.append(os.path.join(r, file))
name = os.path.basename(file)
name = name[:-4]
self.list_files_names.append(name)
self.liste_1.config(values=self.list_files_names)
self.liste_2.config(values=self.list_files_names)
self.liste_1.current(0)
self.liste_2.current(0)
return self.list_files_names
def create_img(self):
self.composition = self.background
self.ecartementlogo = 550
rift_middle = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 150)
text_middle = 'VS'
text_match_format = self.entry_format_match.get()
rift_match_format = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 60)
rift_score = ImageFont.truetype(os.path.join('C:/Users...rift/', 'fort_foundry_rift_bold.otf'), 50)
self.score_1 = self.entry_score__1.get()
self.score_2 = self.entry_score__2.get()
self.1=self.liste_1.get()
self.2=self.liste_2.get()
self.logo_1 = PIL.Image.open(self.path_slogos+'/'+self.1+'.png').convert('RGBA')
self.logo_2 = PIL.Image.open(self.path_slogos+'/'+self.2+'.png').convert('RGBA')
#logo 1
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_1,
[960-int(self.ecartementlogo), 550],
[None, 300],
1)
#logo 2
self.composition = utilitary.draw_image_advanced(self.composition, self.logo_2,
[960+int(self.ecartementlogo), 550],
[None, 300],
1)
image_draw = ImageDraw.Draw(self.composition)
#insert text (VS + score)
utilitary.draw_text_center_align(image_draw, [960, 450], text_middle, font=rift_middle, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960, 600], text_match_format, font=rift_match_format, fill=utilitary.colors['white'])
utilitary.draw_text_center_align(image_draw, [960-self.ecartementlogo, 700], self.score_1, font=rift_score, fill=utilitary.colors['light_red'])
utilitary.draw_text_center_align(image_draw, [960+self.ecartementlogo, 700], self.score_2, font=rift_score, fill=utilitary.colors['light_red'])
if self.var4.get()==0:
pass
if self.var4.get()==1:
config.set('main', 'default_file_background', self.background_file)
config.set('main', 'default_path_slogos', self.path_slogos)
with open('config.ini', 'w') as f:
config.write(f)
print(self.background_file)
print(self.path_slogos)
print("settings saved")
print("image created")
self.miniature = self.composition
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas.itemconfig(self.image_on_canvas, image =self.miniature)
def save_img(self):
self.var5.set("Saved as " +self.1 + '_'+self.2+'.png')
self.composition.save('C:/.../'+self.1 + '_'+self.2+'.png')
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage)
### this line don't work ### wrong arguments
self.background_file = config.get('main', 'default_file_background')
self.path_slogos = config.get('main', 'default_path_slogos')
self.group3 = tk.LabelFrame(self, text=" A & Score")
self.group3.pack (side="left", padx=5, pady=5)
self.group4 = tk.LabelFrame(self, text=" B & Score")
self.group4.pack (side="right", padx=5, pady=5)
self.liste_1 = ttk.Combobox(self.group3)
self.liste_1.pack(side="top", padx=5, pady=5)
self.liste_2 = ttk.Combobox(self.group4)
self.liste_2.pack(side="top", padx=5, pady=5)
self.liste_1, self.liste_2 = utilitary.initial_list_logos(self.path_slogos, self.liste_1, self.liste_2)
self.liste_1.current(0)
self.liste_2.current(0)
self.group1 = tk.LabelFrame(self, text="Background")
self.group1.pack (side="top", padx=5, pady=5)
self.button_choose_background_file = tk.Button(self.group1, text="Choose Background")
self.button_choose_background_file.config(command=self.choose_background)
self.button_choose_background_file.pack (side="top", padx=5, pady=5)
self.var1 = tk.StringVar()
self.var1.set(os.path.basename(self.background_file))
self.label_name_background_file = tk.Label(self.group1, textvariable=self.var1)
self.label_name_background_file.pack (side="bottom", padx=5, pady=5)
self.group2 = tk.LabelFrame(self, text="Logos s",labelanchor='ne')
self.group2.pack (side="top", padx=5, pady=5)
self.var2 = tk.StringVar()
self.var2.set("Dossier : "+os.path.basename(self.path_slogos))
self.label_path_slogo = tk.Label(self.group2, textvariable=self.var2)
self.label_path_slogo.pack (side="bottom", padx=5, pady=5)
self.button_list_logos = tk.Button(self.group2, text="Choose logos path")
self.button_list_logos.config(command=self.create_list_logos)
self.button_list_logos.pack (side="top", padx=5, pady=5)
self.score_1 = tk.StringVar()
self.score_1.set("")
self.entry_score__1 = tk.Entry(self.group3,textvariable=self.score_1, width=5, justify='center')
self.entry_score__1.pack(side="bottom", padx=5, pady=5)
self.score_2 = tk.StringVar()
self.score_2.set("")
self.entry_score__2 = tk.Entry(self.group4,textvariable=self.score_2, width=5, justify='center')
self.entry_score__2.pack(side="bottom", padx=5, pady=5)
self.button_show = tk.Button(self, text="Show Image")
self.button_show.config(command=self.create_img)
self.button_show.pack (side="bottom", padx=5, pady=5)
self.button_save_img = tk.Button(self, text="Save as image")
self.button_save_img.config(command=self.save_img)
self.button_save_img.pack (side="bottom", padx=5, pady=5)
self.var5 = tk.StringVar ()
self.var5.set('')
self.label_name_save = tk.Label(self, textvariable =self.var5)
self.label_name_save.pack (side="bottom", padx=5, pady=5)
self.format_match = tk.StringVar()
self.format_match.set("Format")
self.entry_format_match = tk.Entry(self,textvariable=self.format_match, width=10, justify='center')
self.entry_format_match.pack()
self.var4 = tk.IntVar ()
self.var4.set(0)
self.button_save_settings = tk.Checkbutton(self, text="Save settings", variable = self.var4)
self.button_save_settings.pack(side = "bottom")
self.ecartementlogo = tk.IntVar ()
self.var4.set(550)
self.background=PIL.Image.open(self.background_file).convert('RGBA')
self.basewidth = 400
self.miniature = self.background
self.wpercent = (self.basewidth/float(self.miniature.size[0]))
self.hsize = int((float(self.miniature.size[1])*float(self.wpercent)))
self.miniature = self.miniature.resize((self.basewidth,self.hsize))
print(self.miniature.size)
self.miniature = PIL.PhotoImage(self.miniature)
self.canvas = tk.Canvas(self,width=400, height=225)
self.image_on_canvas = self.canvas.create_image(0, 0, anchor="nw", image=self.miniature)
self.canvas.pack()
class SettingsPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
startp = StartPage(controller, SettingsPage)
###this line work ### arguments are correct
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Edition Page",
command=lambda: controller.show_frame(StartPage))
button1.pack()
scale_x_logo_ = tk.Scale(self, orient='horizontal', from_=0, to=startp.background.size[0]/2,
resolution=10, length=350,
label='Placement horizontal logo', variable= startp.ecartementlogo)
scale_x_logo_.pack()
app = DotaCasterKit()
app.geometry("%dx%d%+d%+d" % (850,620,100,100))
app.mainloop()
This line used in the SettingsPage Class is ok : startp = StartPage(controller, SettingsPage)
I would say I need to write the reverse one but I having error with arguments (controller, StartPage) BUT this one setp = SettingsPage(controller, StartPage) don't work (in the init line of StartPage Class)
The issue is here:
class StartPage(tk.Frame):
# ... Omitting irrelevant stuff
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
setp = SettingsPage(controller, StartPage) # Right here
# ... More irrelevant stuff
You are passing the class StartPage to the SettingsPage, rather than an instance of that class. SettingsPage then tries to interpret the class as a widget, but a class is not a widget and does not have a tk attribute.
The best way to fix this is to remove that line, since you never use the variable setp. The corresponding line in SettingsPage.__init__ should also be removed.
You should also read the link suggested by #Brian Oakley. Your code currently has many issues. I have fixed one of them, and his link should help you fix more.