I'm a beginner of learning GUI.
My python version is 2.7 and I'm using Windows.
I've searched tkinter in folder there is only one python file which is in C:\python27.
Here is my code:
import Tkinter as tk
class Electronic_Signature_User_Program(tk.TK):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self, *args, **kwargs)
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 (Loginpage, Login_Confirm):
frame = Loginpage(container,self)
self.frames[Loginpage] = frame
frame.grid(row=0,column=0,sticky="nsew")
self.show_frame(Loginpage)
def show_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class Loginpage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button1 = tk.Button(self,text="Login_Confirm",command=lambda:controller.show_frame(Login_Confirm))
button1.pack()
class Login_Confirm(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
button2 = tk.Button(self,text="Loginpage",command=lambda:controller.show_frame(Loginpage))
button2.pack()
app = Electronic_Signature_User_Program()
app.title('UoL 702 Electrinic Signature User Program')
app.mainloop()
In the very first class declaration you have TK where it should be Tk.
Related
I am creating frames for my project and so far I have it where it goes from the home page to the main page when the button is clicked. The problem I am facing is when I try to go from the home page to the log page where I am faced with an issue of calling the show_frame() function (located in MainApplication) in class MainPage.
How would I go about using arguments in MainPage so I can move from main page to log page?
class MainApplication(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# initialize frames
self.frames = {}
for F in (HomePage, MainPage, LogPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
# show home page frame first
self.show_frame(HomePage)
def show_frame(self, cont): # <-- FUNCTION HERE
frame = self.frames[cont]
frame.tkraise()
class HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
continue_button = ttk.Button(self, text="Enter program", width=15,
command=lambda: controller.show_frame(MainPage)) # <-- works here
continue_button.pack()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def success_actions(self):
self.run_script_button["text"] = "View log"
self.run_script_button.configure(
command=lambda: controller.show_frame(LogPage)) # <-- want to use here
class LogPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
pass
It works only in HomePage because you made it inside the __init__() method but in the MainPage you need it outside.
To solve this try setting controller as an instance variable:
class MainPage(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
def success_actions(self):
self.run_script_button["text"] = "View log"
self.run_script_button.configure(
command=lambda: self.controller.show_frame(LogPage))
Pardon me for my bad grammar or explanation, since I didn't know how to explain this properly.
I try to build some gui that could switch between frame, using script from this as base Switch between two frames in tkinter.
In this case, I will have a few frame that had similar design, but different function when the button is pressed. For example, I have 2 frames that have similar 2 entries and 1 button, but the button do different command (where at sub01 frame it will multiply and at sub02 frame will divide)
This is my code:
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.grid(row=1,columnspan=4,sticky='nsew')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (sub01, sub02):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=1,sticky="nsew")
self.choices = {'sub01','sub02'}
self.tkvar = tk.StringVar()
self.tkvar.set('sub01')
self.popMenu = tk.OptionMenu(self,self.tkvar,*self.choices)
self.popMenu.grid(row=0)
self.show_frame()
self.button1 = tk.Button(self, text="Go to Layer",command=lambda: self.show_frame())
self.button1.grid(row=0, column=1)
def show_frame(self):
'''Show a frame for the given page name'''
page_name = self.tkvar.get()
frame = self.frames[page_name]
frame.tkraise()
class sub01(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This SubLayer 1")
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='we')
i += 1
self.btn = tk.Button(self,text="multiply", command=lambda : self.multiply())
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def multiply(self):
pass
class sub02(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This SubLayer 2")
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='w')
i += 1
self.btn = tk.Button(self,text="divide",command=lambda : self.divide())
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def divide(self):
pass
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
This code itself works, but when I need to create more of these frames, it becomes inconvenient. How could I make this code simpler? Like having that similar frame as a class, and the button as other class that do differ behaviour depend of the layer shown.
Thank you in advance
The canonical way to do this sort of thing is to create a class hierarchy for your Page classes and put common functionality in the base classes and derive subclasses from them that specify the behavior that differs between them. Below is how you could do that with the sample code in your question.
Since the things that are different between them are:
The text displayed on the Label.
The text displayed on the Button.
The code in that's execute when the Button is clicked.
This means the derived classes only need to know what code to run in a generically named btn_func() method and what the text to displayed on the two widgets. The code below illustrates how to do that.
Note that I've changed the spelling of your class names to conform to the naming conventions describe in PEP 8 - Style Guide for Python Code.
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.grid(row=1,columnspan=4,sticky='nsew')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Sub01, Sub02):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=1,sticky="nsew")
self.choices = {'Sub01','Sub02'}
self.tkvar = tk.StringVar()
self.tkvar.set('Sub01')
self.popMenu = tk.OptionMenu(self,self.tkvar,*self.choices)
self.popMenu.grid(row=0)
self.show_frame()
self.button1 = tk.Button(self, text="Go to Layer",command=lambda: self.show_frame())
self.button1.grid(row=0, column=1)
def show_frame(self):
'''Show a frame for the given page name'''
page_name = self.tkvar.get()
frame = self.frames[page_name]
frame.tkraise()
class BaseSubLayer(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text=self.lbl_text)
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='we')
i += 1
self.btn = tk.Button(self,text=self.btn_func_name, command=self.btn_func)
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def btn_func(self):
raise NotImplementedError
class Sub01(BaseSubLayer):
lbl_text = 'This SubLayer 1'
btn_func_name = 'multiply'
def btn_func(self):
print('Running multiply() method.')
class Sub02(BaseSubLayer):
lbl_text = 'This SubLayer 2'
btn_func_name = 'divide'
def btn_func(self):
print('Running divide() method.')
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
In my gui I want to import data from a file, display it in a listbox, do a computation, show the result in a listbox and then export the data as a csv.
I have been able to do most of the GUI but the interaction between classes makes no sense to me, here is my code:
class MainApplication(tk.Tk):
def __init__(self, master, *args, **kwargs):
self.master = master
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames["computation"] = computation(master=self.container, controller=self)
self.frames["openFile"] = openFile(master=self.container, controller=self)
...
def get_page(self, c):
return self.frames[c]
class computation(tk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.controller = controller
self.listbox = Listbox(self.master)
self.listbox.pack()
def add_listbox_item(self):
for i in df.columns:
self.listbox.insert(END,i)
class openFile(tk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.controller = controller
self.openfile = Button(self.master, command=self.import_df)
def import_df(self):
self.filename = filedialog.askopenfilename(...)
global df
df = pd.read_csv(self.filename)
computation = self.controller.get_page("computation")
computation.add_listbox_item()
Everything seems to be executing, even a print statement in add_listbox_item but the listbox just wont be filled with the data it's being fed, any ideas?
I am making a game with levels and in each level, I will need to be using different operators and/or different ranges. My problem is that I don't know how to change the variables in a function from a different class. I would like to do this so I don't need to copy and paste my code making it lengthy. I'd like to use self.Answer and self.strQuestion for mulitple scope.
The code below is just to make the classes functional.
from tkinter import *
import tkinter as tk
import random
from Tkinter import messagebox
class BattleMaths(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
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, levelone, leveltwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
lvl1_button = Button(self, text="LEVEL 1", command=lambda: controller.show_frame(levelone))
lvl1_button.place(relx=0.5, rely=0.5, anchor='center')
I want to put the questions def into class leveltwo while changing it to self.Answer = int(numOne) * int(numTwo) and self.strQuestion = "{} x {}".format(str(numOne), str(numTwo))
class levelone(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def widgets(self):
#widgets here
def question(self):
self.UserAnswer = ''
numOne = random.randrange(1,10)
numTwo = random.randrange(1,10)
self.Answer = int(numOne) + int(numTwo) #change this
self.strQuestion = "{} + {}".format(str(numOne), str(numTwo)) #and change this
def answer(self):
#answer checker
class leveltwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#question def here
root = BattleMaths()
root.title("Battle Maths")
root.geometry("400x250")
root.resizable(0,0)
root.mainloop()
Create the variables you want in the main class (BattleMaths), then you can alter them in the child classes via controller.my_variable.
Example: self.Answer created in BattleMaths and accessed in levelone via controller.Answer
I am trying to access the Text widget defined in class FirstPage from outside of the class.
I tried to solve this problem by creating a new instance of FirstPage, but could not find the right arguments to use. Also tried to use instance of GUI to gain the access, but unsuccessfully.
My problem is solved when I can use text.insert(0.0, t) from outside of the classes. It would help me modify the text displayed with Tkinter by functions that are not directly related with the GUI.
The origin of the code I am trying to use is found: Switch between two frames in tkinter
Also I removed lines that were not necessary for this question..
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
text = tk.Text(self , height=25, width=80)
text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.mainloop()
EDIT:
Here is the working code:
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
page_name = FirstPage.__name__
self.frames[page_name] = frame
def get_page(self, page_name):
return self.frames[page_name]
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.text = tk.Text(self , height=25, width=80)
self.text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")
app.mainloop()
There's nothing special you need to do. As with any python object, you simply need a reference to the object in order to manipulate it.
The concept in the code you started with is to have a "controller" that controls access to all of the pages, since that object is where the pages are created. You can add a function in the controller that gives you a reference to a page, and then you can use that to call a function on that page.
Here's the changes you need to make to the controller:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
...
page_name = FirstPage.__name__
self.frames[page_name] = frame
...
def get_page(self, page_name):
return self.frames[page_name]
You also need to modify FirstPage to keep a reference to the widget so that you can access it later:
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
...
self.text = tk.Text(...)
...
From within any other code you can now access the text widget via get_page (but your pages must save a reference to the controller for this to work).
class AnotherPage(tk.Frame):
def __init__(self, parent, controller):
...
self.controller = controller
...
def some_function(self):
...
first_page = self.controller.get_page("FirstPage")
text = first_page.text.get("1.0", "end-1c")
...
first_page.text.insert("end", "some new text\n")
Note that this technique works outside of any GUI pages. In your code, app is the controller, so you can do something like this:
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")