I want to have several Checkboxes in an CustomFrame, but currently have the following missbehaviour:
If I click on a Checkbox, the whole column gets active and I want to have one the clicked one to be active. Is it possible to "reuse" Checkboxes in tk-derived classes and declare them in Subclasses?
Mainwindow which uses SubFrames:
import tkinter as tk
import zmq
from time import sleep
import time
from threading import Thread
from glmPygameGlue import *
from networkRenderGlue import *
from quatLTI import *
import guiWidgets as gw
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
#super().__init__()
self.accWidget = gw.Widget_Vector(self, "ACC")
self.gyroWidget = gw.Widget_Vector(self, "GYRO")
self.magWidget = gw.Widget_Vector(self, "MAG")
self.accWidget.grid(column=0, row=0)
self.gyroWidget.grid(column=0, row=1)
self.magWidget.grid(column=0, row=2)
self.grid(column=0, row=0)
def AccBiasEnable(self):
print("window accBiasEnable")
def onClose(self):
exit()
root = tk.Tk()
app = Window(root)
root.protocol("WM_DELETE_WINDOW", app.onClose)
root.wm_title("Input-Gui Quaternion-HP")
root.geometry("500x500")
root.mainloop()
SubFrames:
import tkinter as tk
class TripleCheckboxVertical(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.xBias = tk.Checkbutton(self, text='bias', command=master.master.AccBiasEnable)
self.yBias = tk.Checkbutton(self, text='cos')
self.zBias = tk.Checkbutton(self, text='noise')
self.xBias.grid(column=0, row=0)
self.yBias.grid(column=1, row=0)
self.zBias.grid(column=2, row=0)
def callBackFunc(self, event=None):
print("cb event!1")
class Widget_Vector(tk.LabelFrame):
def __init__(self, master=None, text_=""):
tk.LabelFrame.__init__(self, master, text=text_)
self.master = master
self.labelX = tk.Label(self, text="x")
self.labelY = tk.Label(self, text="y")
self.labelZ = tk.Label(self, text="z")
self.cb3X = TripleCheckboxVertical(self)
self.cb3Y = TripleCheckboxVertical(self)
self.cb3Z = TripleCheckboxVertical(self)
self.labelX.grid(column=0, row=0)
self.labelY.grid(column=0, row=1)
self.labelZ.grid(column=0, row=2)
self.cb3X.grid(column=1,row=0)
self.cb3Y.grid(column=1,row=1)
self.cb3Z.grid(column=1,row=2)
Any ideas?
Thank you
To resolve your issue, add a variable to each checkbutton. I've created 3 BooleanVar which store the value of the checkbutton and makes them behave independently. I've also added a get method to return the value of each checkbox:
class TripleCheckboxVertical(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.xBiasVar = tk.BooleanVar()
self.yBiasVar = tk.BooleanVar()
self.zBiasVar = tk.BooleanVar()
self.xBias = tk.Checkbutton(self, text='bias', variable = self.xBiasVar, command=master.master.AccBiasEnable)
self.yBias = tk.Checkbutton(self, text='cos', variable = self.yBiasVar)
self.zBias = tk.Checkbutton(self, text='noise', variable = self.zBiasVar)
self.xBias.grid(column=0, row=0)
self.yBias.grid(column=1, row=0)
self.zBias.grid(column=2, row=0)
def get(self):
return {"bias":self.xBiasVar.get(), "cos":self.yBiasVar.get(), "noise":self.zBiasVar.get()}
I also added a get method to Widget_Vector:
def get(self):
return {"x":self.cb3X.get(), "y":self.cb3Y.get(), "z":self.cb3Z.get()}
If you wanted the value of x bias for GYRO, for example, you can do self.gyroWidget.get()["x"]["bias"]
Related
The two classes belong to different frames. The challenge is
to read data from the window ʻent_dataclass a parent ofForLeftFrame in a descendant class of ForRightFrameChild`.
When calling the parent class, a message appears in the console:
"name 'left_frame' is not defined". Can't figure out why?
Everything works in one frame. Please help me figure it out.
The code is as follows:
import tkinter as tk
#-----------
class ForLeftFrame():
def __init__(self, left_frame):
self.left_frame = left_frame
self.ent_data = tk.Entry(left_frame, width=8, bg='#3de',
fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class ForRightFrameChild(ForLeftFrame):
def __init__(self, right_frame):
self.right_frame = right_frame
super().__init__(self, left_frame)
self.left_frame = left_frame
self.transf_button = tk.Button(right_frame, text="Transfer to...",
bg='#489', fg='#dff', command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Frame):
"""Главный класс приложения"""
def __init__(self, master):
super().__init__()
left_frame = tk.Frame(master, bg='tan', relief='groove', bd=3)
left_frame.pack(side='left', fill='both', expand=1)
righr_frame = tk.Frame(master, bg='aqua', relief='groove', bd=3)
righr_frame.pack(side='right', fill='both', expand=1)
self.for_left_frame = ForLeftFrame(left_frame)
self.for_right_frame_child = ForRightFrameChild(righr_frame)
#-----------------------------
root = tk.Tk()
app = Application(root)
root.mainloop()
To achive this you need to have references and that is exactly what the error suggests.
So what I did in the code below is first to write your application as a subclass of tk.Tk() so the root parameter or you window is now your own class to modify, also it becomes the controller/director in this ensemble.
Also I created your frames as a subclass of frames, how you did it first with only the application class.
Now, all the magic is in this line and I will explain what happens here for a better understanding.
self.master.left_frame.ent_data.get()
So self reference to the instance of the class which we had bound to self.right_frame in our class named Application.
The class Application is also the master of self/right_frame.
The Application has the attribute left_frame which we did by self.left_frame in the Application class.
self.left_frame was bound to a reference of an instance from the class LeftFrame were we have defined the attribute ent_data which is an tk.Entry that has the method get.
I know it seems confusing in the beginning and you may need to read this text more then onnce, but this is how it works. It is more or less a straight way to go.
import tkinter as tk
#-----------
class LeftFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.ent_data = tk.Entry(self, width=8, bg='#3de',fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class RightFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
self.transf_button = tk.Button(self, text="Transfer to...",
bg='#489', fg='#dff',
command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.master.left_frame.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.left_frame = LeftFrame(self)
self.right_frame = RightFrame(self)
self.left_frame.pack(side='left', fill='both', expand=1)
self.right_frame.pack(side='right', fill='both', expand=1)
#-----------------------------
app = Application()
app.mainloop()
EDIT:
import tkinter as tk
#-----------
class LeftFrame(object):
def __init__(self, frame):
self.f = frame
self.ent_data = tk.Entry(self.f, width=8, bg='#3de',fg='#dff')
self.ent_data.grid(column=0, row=1)
#-----------
class RightFrame(object):
def __init__(self, frame):
self.f = frame
self.transf_button = tk.Button(self.f, text="Transfer to...",
bg='#489', fg='#dff',
command=self.transferTo)
self.transf_button.grid(column=0, row=1)
def transferTo(self):
self.ent_data_st = self.f.master.for_left_frame.ent_data.get()
print('Transfer to...', self.ent_data_st)
#-----------
class Application(tk.Tk):
def __init__(self):
super().__init__()
frame1 = tk.Frame(self)
frame2 = tk.Frame(self)
self.for_left_frame = LeftFrame(frame1)
self.for_right_frame = RightFrame(frame2)
frame1.pack()
frame2.pack()
#-----------------------------
app = Application()
app.mainloop()
Actually, i want to create a GUI to receive data via serial port, but i got a problem is that the button will be pressed continually till the whole data collation to be finished.
In order to make people to understand the situation what i encountered easily, i change the code in the bellow
from Tkinter import *
class GUIDemo(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
# self.datarcv()
def createWidgets(self):
self.inputText = Label(self)
self.inputText["text"] = "Input:"
self.inputText.grid(row=0, column=0)
self.inputField = Entry(self)
self.inputField["width"] = 50
self.inputField.grid(row=0, column=1, columnspan=6)
self.regist = Button(self, command=self.datasave)
self.regist["text"] = "button"
self.regist.grid(row=2, column=0)
def datasave(self):
a=0
while a<100000000:
a+=1
print a
if __name__ == '__main__':
root = Tk()
app = GUIDemo(master=root)
app.mainloop()
You'll need to add a thread
from Tkinter import *
import threading
class GUIDemo(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
# self.datarcv()
def createWidgets(self):
self.inputText = Label(self)
self.inputText["text"] = "Input:"
self.inputText.grid(row=0, column=0)
self.inputField = Entry(self)
self.inputField["width"] = 50
self.inputField.grid(row=0, column=1, columnspan=6)
self.regist = Button(self, command=self.datasave_thread)
self.regist["text"] = "button"
self.regist.grid(row=2, column=0)
def datasave_thread(self):
self.thread = threading.Thread(None, self.datasave, None, (), {})
self.thread.start()
def datasave(self):
a=0
while a<100000000:
a+=1
print a
if __name__ == '__main__':
root = Tk()
app = GUIDemo(master=root)
app.mainloop()
I wrote a tkinter application that had widgets displayed on two frames, similar to this example, which successfully runs.
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Example")
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()
#(The labels are examples, but the rest of the code is identical in structure).
labelA = ttk.Label(frame1, text = "This is on Frame One")
labelA.grid(column=1, row=1)
labelB = ttk.Label(frame2, text = "This is on Frame Two")
labelB.grid(column=1, row=1)
root.mainloop()
I decided that I should try to restructure the program to use a class (which I'm admittedly not very familiar with). However, I'm unsure what I should do to allow the widgets to appear on different frames (everything else works okay). For instance, the following produces a "TypeError: init() takes from 1 to 2 positional arguments but 3 were given." So presumably I'd need to initialise with an extra argument, but I'm not sure how the notebook would be worked into that, or if that's even the approach I should be taking. (The program will run if the "frame1" and "frame2" arguments are removed from the labels, it will, however, display the same thing on both frames).
from tkinter import *
from tkinter import ttk
class MainApplication(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.labelA = ttk.Label(self, frame1, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
self.labelB = ttk.Label(self, frame2, text = "This is on Frame Two")
self.labelB.grid(column=1, row=1)
root = Tk()
root.title("Example")
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
notebook.add(frame1, text="Frame One")
notebook.add(frame2, text="Frame Two")
notebook.pack()
MainApplication(root).pack()
root.mainloop()
I'm interested in a solution, but I'm also interested in learning what the class is doing differently compared to the standalone widgets.
This would be one way to generalize the application as a class. You want to eliminate the repeated code.
from tkinter import *
from tkinter import ttk
class Notebook:
def __init__(self,title):
self.root = Tk()
self.root.title(title)
self.notebook = ttk.Notebook(self.root)
def add_tab(self,title,text):
frame = ttk.Frame(self.notebook)
self.notebook.add(frame,text=title)
label = ttk.Label(frame,text=text)
label.grid(column=1,row=1)
self.notebook.pack()
def run(self):
self.root.mainloop()
nb = Notebook('Example')
nb.add_tab('Frame One','This is on Frame One')
nb.add_tab('Frame Two','This is on Frame Two')
nb.run()
I suggest you split the different Frames within notebook into separate files.
I used from tab2 import * because I wanted this to remain in the namespace without adding the file/class prefix ie. I didn't want to write: tab1.Tab1(notebook)
main.py
import tkinter as tk
from tkinter import ttk
from tab1 import *
from tab2 import *
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
notebook = ttk.Notebook(parent)
Tab1frame = Tab1(notebook)
Tab2frame = Tab2(notebook)
notebook.add(Typ1frame, text='TAB1')
notebook.add(Typ2frame, text='TAB2')
notebook.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
tab1.py
import tkinter as tk
from tkinter import ttk
class Typ14(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
shell_frame=tk.LabelFrame(self, text="Sample Label Frame", padx=5,pady=5)
shell_frame.grid(row=0,column=0,padx=5,pady=5)
To ensure that your code is clear and purposeful, you should create a class for your main application, and a class for each tab. You don't need to separate tab classes into separate files, but if you're working with other developers it would be in your best interest.
The code below shows your code reformatted to have 3 classes: 1 for the main app (MainApplication), and 2 for each tab (Frame1 and Frame2). In addition, I've imported tkinter as tk for referential clarity.
import tkinter as tk
from tkinter import ttk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.title("Example")
self.geometry('300x300')
self.notebook = ttk.Notebook(self)
self.Frame1 = Frame1(self.notebook)
self.Frame2 = Frame2(self.notebook)
self.notebook.add(self.Frame1, text='Frame1')
self.notebook.add(self.Frame2, text='Frame2')
self.notebook.pack()
class Frame1(ttk.Frame):
def __init__(self, container):
super().__init__()
self.labelA = ttk.Label(self, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
class Frame2(ttk.Frame):
def __init__(self, container):
super().__init__()
self.labelB = ttk.Label(self, text = "This is on Frame Two")
self.labelB.grid(column=1, row=1)
if __name__ == '__main__':
app = MainApplication()
app.mainloop()
As you can imagine, this will allow you to create additional classes to add frames to your tab classes. The code below shows an alteration to class Frame1 above, and the addition of class Frame1FrameA which does just this.
class Frame1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.labelA = ttk.Label(self, text = "This is on Frame One")
self.labelA.grid(column=1, row=1)
self.frame = Frame1FrameA(self)
self.frame.grid(row=1, columnspan=2)
class Frame1FrameA(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.LabelA = ttk.Label(self, text="LabelA in FrameA in tab Frame1")
self.LabelA.grid(column=0, row=0)
self.LabelB = ttk.Label(self, text="LabelB in FrameA in tab Frame1")
self.LabelB.grid(column=1, row=0)
I am trying to create multiple windows using tkinter , but i am having no success so far ... When i create a child window and put a button on it , the button is created in the parent window!
from tkinter import *
class Login_screen(Frame):
def __init__(self,master):
Frame.__init__(self, master)
self.grid()
self.button1 = Button(text = "Open",command = lambda: self.open_login())
self.button1.grid()
def open_login(self):
self.root2 = Toplevel()
self.root2.geometry("400x200")
self.app2 = Main_screen(self.root2)
class Main_screen(Frame):
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.button = Button(text = "Close",command = lambda: self.close_windows())
self.button.grid()
def close_windows(self):
self.grid_forget()
root = Tk()
root.geometry("800x600")
app = Login_screen(root)
root.mainloop()
You need to supply the Button() with the master argument:
self.button = Button(master = self, text = "Close",command = lambda: self.close_windows())
master is the first arg to a widget so it can also be done via: Button(self, text=...)
This is good practice and you should get in the habit of always explicitly providing master, otherwise Tk defaults this arg to None and will place it on the root window.
I am trying to create a program in tkinter which allows me to open an initial window then to keep it throughout all classes used. For example, if I was to create a button in a window then when I click this button, it would exuecute a method that destroys the widget, and then executes a new class that builds a new screen within the same window, such as text opposed to a button.
from tkinter import *
class Window1:
def __init__(self, master):
self.master = master
self.label = Button(self.master, text = "Example", command = self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
## Code to execute next class
class Window2:
def __init__(self, master):
self.master = master
self.label = Label(self.master, text = "Example")
self.label.pack()
def main():
root = Tk()
run = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
I understand this is less practical, but I am curious. Cheers.
Tk() creates main window and variable root gives you access to this window. You can use root as argument for Window2 and you will have access to main window inside Window2
from tkinter import *
class Window1:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Button(self.master, text="Example", command=self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
# use `root` with another class
self.another = Window2(self.master)
class Window2:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Label(self.master, text="Example")
self.label.pack()
root = Tk()
run = Window1(root)
root.mainloop()
--
Probably nobody use another class to create Label in place of Button ;)
--
EDIT: In this example using names Window1 and Windows2 is misleading because there is only one window and two classes which use this window. I would rather use names FirstOwner, SecondOwner
Everything is implemented in one Tk class and in this case there always is only one window.
from tkinter import *
from tkinter import ttk
class MainWindow():
def __init__(self, mainWidget):
self.main_frame = ttk.Frame(mainWidget, width=300, height=150, padding=(0, 0, 0, 0))
self.main_frame.grid(row=0, column=0)
self.some_kind_of_controler = 0
self.main_gui()
def main_gui(self):
root.title('My Window')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=0, column=0)
self.main_label_2 = ttk.Label(self.main_frame, text='Object_2')
self.main_label_2.grid(row=1, column=0)
self.main_label_3 = ttk.Label(self.main_frame, text='Object_3')
self.main_label_3.grid(row=2, column=0)
self.setings_button = ttk.Button(self.main_frame, text='Setings')
self.setings_button.grid(row=0, column=1)
self.setings_button.bind('<Button-1>', self.setings_gui)
self.gui_elements = [self.main_label_1,
self.main_label_2,
self.main_label_3,
self.setings_button]
def setings_gui(self, event):
self.gui_elements_remove(self.gui_elements)
root.title('Setings')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=2, column=0)
self.main_menu_button = ttk.Button(self.main_frame, text='Main menu')
self.main_menu_button.grid(row=0, column=1)
self.main_menu_button.bind('<Button-1>', self.back_to_main)
self.some_kind_of_controler = 1
self.gui_elements = [self.main_label_1,
self.main_menu_button]
def back_to_main(self, event):
if self.some_kind_of_controler == 1:
self.gui_elements_remove(self.gui_elements)
else:
pass
self.main_gui()
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def main():
global root
root = Tk()
root.geometry('300x150+50+50')
window = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()