I am trying to do the following:
Create a Tkinter App with a 'File' menu.
The File Menu has 2 options, Add and View.
The Add option adds a Frame and then adds a Label widget (Label 1) in the frame.
If I then select the View option form the file menu, it should print out whether or not a Frame widget already exists.
Following is my attempt at it, but I receive the error
AttributeError: 'Test' object has no attribute 'tk'
when I select the View option, can someone please help point out what I am missing here?
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
f=Frame()
f.pack()
lbl1 = Label(f, text="Label 1").grid(row=0, column=0)
def viewlbl(self):
print(Frame.winfo_exists(self))
T=Test()
I replicated your problem. I got the code below to work using Python3.4 on Linux. f needs to become self.f. I named it self.frame. This enables the frame to be accessed outside of the method it is created in.
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
self.frame = Frame(self.gui)
self.frame.pack()
lbl1 = Label(self.frame, text="Label 1")
lbl1.grid(row=0, column=0)
def viewlbl(self):
print('frame exists {}'.format(self.frame.winfo_exists()))
T=Test()
Related
I have 3 modules (small, dont worry).
main_module = it has a combobox and a button. Comobobox list must be update each time a list (in module2) increases in number of names (combo values). Button calls the second window (module2)-->
myapp_second_window.py which has a entry box and another button. We write a name in the entry, push the button...voila..the list increases. In the origina app the list is created automatically when (2) is called.
Now I pass the list to a Pages.variable that is in -->
my_pages_to_connect_modules.
So, when app start I can populate combobox calling (2) to generate a Pages.variable list or populate combobox with json previously written.
The problem? --> how populate combobox while app is running. I mean, we go to (2) create a new name in entry come back to (1) and it is already there.
main_module
import tkinter as tk
from tkinter import*
from tkinter import ttk
import myapp_second_window
from myapp_second_window import SecondClass
root= Tk()
root.geometry("500x500")
root.title('myAPP_Main_Window')
class MainClass:
def __init__(self, parent,myapp_second_window):
self.parent = parent
self.my_widgets1()
def call_second_page (self):
Window2 = tk.Toplevel(root)
Window2.geometry('400x300')
myapp_second_window.SecondClass(Window2)
def my_widgets1(self):
self.field1_value = StringVar()
self.field1 = ttk.Combobox(self.parent, textvariable=self.field1_value)
self.field1['values'] = [1,2] # Pages.variable comes Here
self.field1.grid( row=0, column=0)
self.myButton = tk.Button(self.parent, text = "Call Second module", command = self.call_second_page)
self.myButton.grid(row=2, column=0)
if __name__ == '__main__':
app = MainClass(root, myapp_second_window)
root.mainloop()
myapp_second_window.py
import tkinter as tk
from tkinter import*
from tkinter import ttk
root= Tk()
root.minsize(550,450)
root.maxsize(560,460)
root.title('myAPP_Second_Window')
class SecondClass:
def init(self, parent):
self.parent = parent
self.my_widgets()
self.names = []
def my_widgets(self):
mylabel = Label(self.parent, text='Insert new name in next widget:')
mylabel.grid(column=0, row=0, sticky=W, pady=3)
button1 = tk.Button(self.parent, text="Click to enter Names in list", command=self.addToList)
button1.grid(column=3, row=0, sticky=W, pady=3)
self.name = StringVar()
valueEntry = tk.Entry(self.parent, textvariable= self.name)
valueEntry.grid(row=1, column=0, sticky=W, pady=3)
def addToList(self):
self.names.append(self.name.get())
print('listentries', self.names)
Pages.list_of_names = self.names
my_pages_to_connect_modules.
class Pages():
list_of_names = " "
It`s been challenging to me, every help is welcome. But please dont say just that I must update main window, I need to know how. Thanks to all of you.
I'm trying to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu. When I press new another window opens to ask for a name and displays it in the main window, but I can't get the new window to open and do the rest. I have the TopLevel to open a new window, but I get NameError: name 'TopLevel' is not defined and can't continue from there. What can I do to make it work?
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
from tkinter import filedialog as fd
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = TopLevel()
Label(self, text='Enter Your Name').grid(row=0)
e1 = Entry(self)
e1.grid(row=0, column=1)
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is simply that you're not defining or importing anything named Toplevel. The way you're importing tkinter makes this an easy problem to have.
My recommendation is to remove these statements:
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
... and replace them with these:
import tkinter as tk
from tkinter import ttk
From then on, you have access to almost everything in the tkinter and ttk packages. You simply need to add a tk. or ttk. prefix to everything you use from those packages.
This keeps global namespace pollution at a minimum (ie: you only add two names to the global namespace), and makes your code more self-documenting.
class Example(tk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = tk.Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = tk.Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = tk.TopLevel()
ttk.Label(self, text='Enter Your Name').grid(row=0)
e1 = ttk.Entry(self)
e1.grid(row=0, column=1)
def main():
root = tk.Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
I have created a GUI where the user can select a date from a drop down using the tkcalendar DateEntry widget. I would like to allow the user the option of not selecting a date and leaving this widget blank. However, even if no date is selected the widget returns the current date.
Is there a way to configure the DateEntry to allow for no selection rather than defaulting to the current date if the user does not select a date?
Below is a subset of my code:
import pandas as pd
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
from tkcalendar import DateEntry
class Window(Frame):
def __init__(self, master):
Frame.__init__(self,master)
master.title('Solar Master Project Tracking')
# create canvas for scrollable window
canvas = Canvas(root)
canvas.grid(row=1,column=0, columnspan=2)
# create vertical scrollbar and connect it to the canvas
scrollBar = tk.Scrollbar(root, orient='vertical', command = canvas.yview)
scrollBar.grid(row=1, column=2, sticky = 'ns')
canvas.configure(yscrollcommand=scrollBar.set)
def update_scroll_region(event):
canvas.configure(scrollregion=canvas.bbox("all"))
def _on_mousewheel(event):
canvas.yview_scroll(int(-1*(event.delta/120)), "units")
# create a frame for the widgets in the scrollable canvas
scroll_frame = Frame(canvas)
scroll_frame.bind("<Configure>", update_scroll_region)
canvas.create_window(0,0, anchor='nw', window = scroll_frame)
canvas.bind_all("<MouseWheel>", _on_mousewheel)
# Proposal Date
self.L18 = Label(scroll_frame, text="Proposal Date:",font=('TKDefaultFont', 8, 'bold'))
self.L18.grid(row=21, column=0, sticky=W)
self.prop_date_selection = DateEntry(scroll_frame, width = 25, background = 'LightCyan3',
foreground ='white',borderwidth=2)
self.prop_date_selection.grid(row=21, column=1,sticky=E)
self.prop_date_selection.delete(0,"end")
# SUBMIT INFORMATION
self.button = tk.Button(root, text="Insert / Update Project",font=('TKDefaultFont', 10, 'bold'),
relief=RAISED, command = self.store_user_inputs, bg = "gray80")
self.button.grid(row=25, column = 0, columnspan=8, sticky = 'EW')
# STORE USER INPUT
def store_user_inputs(self):
prop_date_selection = self.prop_date_selection.get_date()
global params
params = [prop_date_selection]
root.destroy()
if __name__ == "__main__":
root = Tk()
Window(root)
root.mainloop()
You can create a class inheriting from tkcalendar.DateEntry and modify the get_date() method to return None when the DateEntry is empty:
import tkcalendar
class DateEntry(tkcalendar.DateEntry):
def get_date(self):
if not self.get():
return None
self._validate_date()
return self.parse_date(self.get())
For those who find this post.
Modifying get_date() didn't change anything in my case. But the following did:
class MyDateEntry(tkcalendar.DateEntry):
def _validate_date(self):
if not self.get():
return True # IMPORTANT!!! Validation must return True/False otherwise it is turned off by tkinter engine
return super()._validate_date()
My understanding is that the in_ keyword argument to pack/grid should allow me to specify the managing widget. I want to pack arbitrary widgets inside a Frame subclass, so I passed the widgets and packed them during intialization, but the widgets didn't appear (although space in the window appears to have been allocated...). If I create the widget internally using master which is root, there is no issue and the widgets are displayed as expected.
The following working example and its output demonstrate the issue:
import tkinter as tk
from tkinter import ttk
class ItemContainerExternal(ttk.Frame):
def __init__(self, master, input_label, input_object):
ttk.Frame.__init__(self, master)
self.label = input_label
self.label.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
self.input_object = input_object
self.input_object.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
def get(self):
return variable.get()
class ItemContainerInternal(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
ttk.Label(master, text='internal').pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
self.input_object = ttk.Entry(master)
self.input_object.pack(side=tk.LEFT, padx=5, pady=3, fill=tk.X, in_=self)
def get(self):
return variable.get()
if __name__ == '__main__':
root = tk.Tk()
inputobj = ttk.Entry(root)
inputlabel = ttk.Label(root, text='external')
ItemContainerExternal(root, inputlabel, inputobj).grid(row=0, column=0)
ItemContainerInternal(root).grid(row=1, column=0)
root.mainloop()
The problem is that you're creating the entry and label before you're creating the frame, so they have a lower stacking order. That means the frame will be on top of the entry and label and thus, obscuring them from view.
A simple fix is to call lift() on the entry and label:
class ItemContainerExternal(tk.Frame):
def __init__(self, master, input_label, input_object):
...
self.input_object.lift()
self.label.lift()
The order in which widgets get created matters. Newer widgets are "on top of" previous widgets.
Call .lower() on the Frame after you create it, assuming it's created after all the widgets that you will pack into it. If not, you'll need to either call .lower() again on the Frame after creating a new widget to go inside it, or you'll have to raise the new widget via .lift() as per Bryan's answer.
I am trying to open a second window in tkinter but it always appears as a tab rather than separately. Code showing the problem is as follows:
import tkinter as tk
class MainWindow:
def __init__(self, master):
self.master = master
frame = tk.Frame(self.master)
button = tk.Button(frame, text = 'New Window', width = 25, command = self.new_window)
button.pack()
frame.pack()
def new_window(self):
newWindow = tk.Toplevel(self.master)
SecondWindow(newWindow)
class SecondWindow:
def __init__(self, master):
frame = tk.Frame(master)
quitButton = tk.Label(frame, text = 'Second Window')
quitButton.pack()
frame.pack()
def main():
root = tk.Tk()
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
When I run this I get the following output:
Clicking the button gives:
(Ignoring the secondary issue of the size) if we expand it we get the following:
I can get a separate window by dragging the tab. How to I code this so that I get the window displaying as a separate window when I click the button ?