Tkinter DateEntry not allowing selection - python

I am using a Tkinter DateEntry field to get a "from" and "to" date for a SQL query. I recetnly redid my project, but copied a lot of code over. In my old project, the date entry works fine, however in my new project whenever I try to select a day for any DateEntry fields, it autofills and does not allow me to use the drop-down window.
This link is an example of how it looks in my working project:
https://www.plus2net.com/python/tkinter-DateEntry.php
This is the working code:
date_joined_to = DateEntry(container, selectmode="day", year=datetime.now().year,
month=datetime.now().month, day=datetime.now().day,
date_pattern="yyyy-mm-dd", foreground="black",
headersforeground="black", selectforeground="black",
width=10)
date_joined_to.grid(row=4, column=1, sticky="W")
date_joined_to.delete(0, "end")
date_joined_to._top_cal.overrideredirect(False)
This is my code in my new project where the problem occurs:
test_date_to = DateEntry(self.centersearch, selectmode="day", year=datetime.now().year,
month=datetime.now().month,
day=datetime.now().day, date_pattern="yyyy-mm-dd",
foreground="black", headersforeground="black",
selectforeground="black", width=10)
test_date_to.grid(row=9, column=1, sticky="E")
test_date_to.delete(0, "end")
test_date_to._top_cal.overrideredirect(False)
I tried removing all keyword arguments to see if those caused it, but the issue persisted.
From my searches it does appear that many people have encountered this before.

I can't answer as I've seen the same problem and got here looking for an answer. Here is code that shows it. In my case the version that works looks the same but is not in separate classes but instead in one large script file. But for real world problems that's poor coding practice. When I moved it out into classes It no longer works.
This version works
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkcalendar import DateEntry
from datetime import datetime
current_day = datetime.now().day
current_month = datetime.now().month
current_year = datetime.now().year
root = tk.Tk()
root.title("Date Demo Works")
# root.minsize(750, 400)
root.geometry("1024x768+0+0")
root.grid()
demo_edit_screen = tk.Toplevel(root)
demo_edit_screen.geometry("1024x768+0+0")
demo_edit_screen.title("Demo Working")
demo_edit_screen.iconify()
demo_edit_screen.withdraw()
def DoNothing():
tk.messagebox.showinfo(message='This is an info box that Does Nothing')
print("testing do nothing dialog")
class DemoEdit():
def __init__(self):
leftsidebar = tk.Frame(demo_edit_screen, borderwidth=2, relief="raised", width=40)
leftsidebar.grid(row=0, column=0, rowspan=11, sticky=("NSEW"))
gohome = ttk.Button(leftsidebar, text="Home", command=DoNothing())
gohome.grid(row=0, column=0)
centersearch = tk.Frame(demo_edit_screen, width=850)
centersearch.grid(row=0, column=1, sticky="W")
test_date_label = tk.Label(centersearch, text='Test Date Range', anchor="e")
test_date_label.grid(row=7, column=0, columnspan=2, padx=4)
test_date_from_label = tk.Label(centersearch, text='From', anchor="w")
test_date_from_label.grid(row=8, column=0, padx=4)
test_date_to_label = tk.Label(centersearch, text='To', anchor="w")
test_date_to_label.grid(row=8, column=1, padx=4)
test_date_from = DateEntry(centersearch, selectmode="day"
, year=datetime.now().year, month=datetime.now().month, day=datetime.now().day
, date_pattern="yyyy-mm-dd"
, foreground="black", headersforeground="black", selectforeground="black", width=10)
test_date_from.grid(row=9, column=0, sticky="W")
test_date_from.delete(0, "end")
test_date_from._top_cal.overrideredirect(False)
test_date_to = DateEntry(centersearch, selectmode="day"
, year=datetime.now().year, month=datetime.now().month, day=datetime.now().day
, date_pattern="yyyy-mm-dd"
, foreground="black", headersforeground="black", selectforeground="black", width=10)
test_date_to.grid(row=9, column=1, sticky="E")
test_date_to.delete(0, "end")
test_date_to._top_cal.overrideredirect(False)
def show_demoedit(self):
root.withdraw()
demo_edit_screen.update()
demo_edit_screen.deiconify()
leftsidebar = tk.Frame(root, borderwidth=2, relief="raised", height=768)
leftsidebar.grid(row=0, column=0, rowspan=8, sticky=("NSEW"))
show_screen = ttk.Button(leftsidebar, text="Show Calendar Screen", command=DemoEdit.show_demoedit(root))
show_screen.grid(row=3, column=0)
demo_edit = DemoEdit()
root.mainloop()
This version does not
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkcalendar import DateEntry
from datetime import datetime
def DoNothing():
tk.messagebox.showinfo(message='This is an info box that Does Nothing')
print("testing do nothing dialog")
class TopScreen(tk.Frame):
def __init__(self, parent, controller, label_text="sample", *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.controller = controller
self.parent = parent
# Create the choose action sidebar that will go to specific screens
self.leftsidebar = tk.Frame(self, borderwidth=2, relief="raised", height=768)
self.leftsidebar.grid(row=0, column=0, rowspan=8, sticky=("NSEW"))
self.home_label = tk.Label(self.leftsidebar, text=label_text)
self.home_label.grid(row=0, column=0)
class CSVReportScreen(TopScreen):
def __init__(self, parent, controller):
super().__init__(parent, controller, label_text="CSV Report")
self.controller = controller
self.parent = parent
create_csv_files_button = ttk.Button(self.leftsidebar, text="Create .CSV Files", command=DoNothing())
create_csv_files_button.grid(row=3, column=0, sticky="NSEW")
# Top center search section
self.centersearch = tk.Frame(self, width=850)
self.centersearch.grid(row=0, column=1, sticky="W")
test_date_label = tk.Label(self.centersearch, text='Test Date Range', anchor="e")
test_date_label.grid(row=7, column=0, columnspan=2, padx=4)
test_date_from_label = tk.Label(self.centersearch, text='From', anchor="w")
test_date_from_label.grid(row=8, column=0, padx=4)
test_date_to_label = tk.Label(self.centersearch, text='To', anchor="w")
test_date_to_label.grid(row=8, column=1, padx=4)
test_date_from = DateEntry(self.centersearch, selectmode="day"
, year=datetime.now().year, month=datetime.now().month, day=datetime.now().day
, date_pattern="yyyy-mm-dd"
, foreground="black", headersforeground="black", selectforeground="black", width=10)
test_date_from.grid(row=9, column=0, sticky="W")
test_date_from.delete(0, "end")
test_date_from._top_cal.overrideredirect(False)
test_date_to = DateEntry(self.centersearch, selectmode="day"
, year=datetime.now().year, month=datetime.now().month, day=datetime.now().day
, date_pattern="yyyy-mm-dd"
, foreground="black", headersforeground="black", selectforeground="black", width=10)
test_date_to.grid(row=9, column=1, sticky="E")
test_date_to.delete(0, "end")
test_date_to._top_cal.overrideredirect(False)
class TestApp(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 = dict()
# Add each separate screen here. The name is the class for that screen
trakker_classes = (CSVReportScreen,TopScreen)
for F in trakker_classes:
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("CSVReportScreen")
def show_frame(self, page_name):
# Show a frame for the given page name
frame = self.frames[page_name]
frame.tkraise()
if __name__ == "__main__":
app = TestApp()
app.mainloop()
DateEntry is a part of tkcalendar. So I have the same question.
OK, I've just spent all day trying all sorts of permutations:
I'm running on MacOS Catalina 10.15.7 (19H1824)
I am using PyCharm 2021.03.3 (Professional Edition)
Build #Py-213.7172.26, built on March 15, 2022
I am using virtual environments and have tried the following versions with these results:
Python 3.8.3
tkinter 8.6.8
Cannot get the DateEntry dropdown calendar to appear. Doesn't matter whether the _top_cal.overrideredirect(False) is set or not same behavior.
Python version 3.8.10 (v3.8.10:3d8993a744, May 3 2021, 08:55:58)
tkinter 8.6.8
DateEntry in the documentation demo code works when the _top_cal.overrideredirect(False) is used.
My DateEntry is not working from within a class.
Python 3.9.8 Universal Version
tkinter 8.6.11
DateEntry dropdown requires double clicks to display. Doesn't matter whether the _top_cal.overrideredirect(False) is set or not same behavior. Same in both documentation demo code and my code.
Python 3.9.12 Intel
tkinter 8.6.8
Cannot get the DateEntry dropdown calendar to appear. Doesn't matter whether the _top_cal.overrideredirect(False) is set or not same behavior. Same in both documentation demo code and my code.
Python 3.10.4 Universal Version
tkinter 8.6.121
DateEntry dropdown requires double clicks to display. Doesn't matter whether the _top_cal.overrideredirect(False) is set or not same behavior. Same in both documentation demo code and my code.
A couple of things I learned along the way.
The Intel Python installers all seem to include tkinter 8.6.8
The Universal installers seem to grab whatever was the latest for that version of Python.
I was not able to test all the options because for lots of the intervening ones there are no installers. I was not able to install from the source code.
I don't know where to go from here.

Related

Using Tkinter filedialog within a frame and accessing the output globally

I am building my first GUI using tkinter and have come up against some problems. To make the code more modular, I am using an object-oriented approach, as seen in the code below. The basic idea is that I have defined classes for the DataFrame, MetaFrame and SaveFrame, which are all instantiated within the OptionsFrame, which then is instantiated within the MainWindow.
import tkinter as tk
from tkinter import ttk
class DataFrame(ttk.Frame):
def __init__(self, main, *args, **kwargs):
super().__init__(main, *args, **kwargs)
# data frame elements
self.data_label = ttk.Label(self, text="Add Data:")
self.labelled_tweets_label = ttk.Label(self, text="Labelled-Tweets: ")
self.labelled_tweets_button = ttk.Button(self, text="Browse")
self.places_label = ttk.Label(self, text="Places: ")
self.places_button = ttk.Button(self, text="Browse")
self.plots_label = ttk.Label(self, text="Plots Path: ")
self.plots_button = ttk.Button(self, text="Browse")
self.submit_button = ttk.Button(self, text="Submit")
# data frame layout
self.data_label.grid(row=0, column=0, columnspan=2, pady=10)
self.labelled_tweets_label.grid(row=1, column=0)
self.labelled_tweets_button.grid(row=1, column=1)
self.places_label.grid(row=2, column=0)
self.places_button.grid(row=2, column=1)
self.plots_label.grid(row=3, column=0)
self.plots_button.grid(row=3, column=1)
self.submit_button.grid(row=4, column=0, columnspan=2, pady=10)
class MetaFrame(ttk.Frame):
...
class SaveFrame(ttk.Frame):
...
class OptionsFrame(ttk.Frame):
def __init__(self, main, *args, **kwargs):
super().__init__(main, *args, **kwargs)
# options frame components
self.data_frame = DataFrame(self)
self.horiz1 = ttk.Separator(self, orient="horizontal")
self.meta_frame = MetaFrame(self)
self.horiz2 = ttk.Separator(self, orient="horizontal")
self.save_frame = SaveFrame(self)
# options frame layout
self.data_frame.grid(row=0, column=0)
self.horiz1.grid(row=1, column=0, sticky="ew", pady=30)
self.meta_frame.grid(row=2, column=0)
self.horiz2.grid(row=3, column=0, sticky="ew", pady=30)
self.save_frame.grid(row=4, column=0, sticky="s")
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.geometry("800x600")
self.resizable(False, False)
# configuration
self.columnconfigure(index=0, weight=1)
self.columnconfigure(index=1, weight=2)
# main frames
self.options_frame = OptionsFrame(self, width=400, height=600, borderwidth=1)
self.vert = ttk.Separator(self, orient="vertical")
# main layout
self.options_frame.grid(row=0, column=0)
self.vert.grid(row=0, column=1, sticky="ns")
def main():
root = MainWindow()
root.mainloop()
The layout can be seen in the following image.
This is the basic layout I want within the OptionsFrame. My confusion lies with creating filedialog methods for the three file browsing buttons within the DataFrame. I understand how to use the filedialog class to return the path to a given file, but then this value is restricted to be in the scope of the DataFrame.
I have a back-end that is already developed which requires these file paths, so ideally I would like to access them from the main() function. How is this possible?
Thanks

StringVar().set() Not Adjusting StringVar

I'm a beginner learning Python and mucking around with the tkinter GUI stuff. I'm trying to make a very basic beginner project that allows a user to type something into a text box and click a button, whereupon that input is added to a label in another part of the window.
However, I'm running into an issue where the StringVar that I'm using as an output isn't being updated by the .set() command.
def __init__(self):
self.window = Tk()
self.window.title("Terminal Writer 9000!")
self.terminalString = StringVar()
self.terminalString.set("This is an example message.")
self.allcontent = ttk.Frame(self.window)
self.allcontent.grid(row=0, column=0, sticky="nwse")
self.mainframe = ttk.Frame(self.allcontent)
self.mainframe.grid(row=0, column=0, sticky = "nwse", columnspan=4, rowspan=5)
self.terminal = ttk.Label(self.mainframe, textvariable=self.terminalString, padding=10, relief="sunken")
self.terminal.grid(row=0, column=0, rowspan=5, columnspan=2, sticky="nwse")
# GUI setup for Buttons and Entry box omitted...
play = TerminalWriterApp()
play.window.mainloop()
However, the area used by the terminal Label is blank, even though it should display "This is an example message." While troubleshooting, I made this, which is basically a complete copy/paste of the functional elements of my original code:
from tkinter import *
from tkinter import ttk
window = Tk()
strvar = StringVar()
strvar.set("Test 2")
allcontent = ttk.Frame(window)
allcontent.grid(row=0, column=0, sticky="nwse")
mainframe = ttk.Frame(allcontent)
mainframe.grid(row=0, column=0, sticky="nwse", columnspan=4, rowspan=5)
text = Label(mainframe, text="Test 1")
text.grid(row=0, column=0, sticky="nwse")
text2 = Label(mainframe, textvariable=strvar)
text2.grid(row=1, column=0, sticky="nwse")
window.mainloop()
This code functions as intended, displaying a window with "Test 1" and "Test 2" on separate lines.
Does anyone know why the set() method wouldn't work in this context? (Also, feel free to get mad at my horrible code - I need to learn good habits somehow!)
For some reasons, the label appears when the app takes focus (when you click on it); maybe it is because of the stack nested frames, IDK.
You could use focus_force to constrain the OS to give focus to your app immediately.
from tkinter import *
from tkinter import ttk
class TerminalWriterApp:
def __init__(self):
self.window = Tk()
self.window.title("Terminal Writer 9000!")
self.terminalString = StringVar()
self.terminalString.set("This is an example message.")
self.allcontent = ttk.Frame(self.window)
self.allcontent.grid(row=0, column=0, sticky="nwse")
self.mainframe = ttk.Frame(self.allcontent)
self.mainframe.grid(row=0, column=0, sticky = "nwse", columnspan=4, rowspan=5)
self.terminal = ttk.Label(self.mainframe, textvariable=self.terminalString, padding=10, relief="sunken")
self.terminal.grid(row=0, column=0, rowspan=5, columnspan=2, sticky="nwse")
self.terminal.focus_force()
play = TerminalWriterApp()
play.window.mainloop()

Tkinter: Code stops and window doesn't show up

I am working on a very basic interface on Python with Tkinter, that displays two input boxes and a button to login. I try to do it by creating different frames and change the frame when the user is logged. It was working nearly fine but then the code started to execute itself not entirely sometimes and entirely but without the Tkinter window. I looked into it and saw nothing shocking but I am not an expert so I am looking for help.
This is the code to run my class that implement Tkinter window:
print 1
app = Skeleton("HomePage")
print 2
app.mainloop()
print 3
The skeleton Class that implement the Tkinter window:
class Skeleton(Tk):
def __init__(self, f,*args, **kwags):
Tk.__init__(self,*args, **kwags)
self.title(f)
container = Frame(self, width=512, height=512)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frameName = {"home","upload","retrieve","deconnected"}
self.frames["HomePage"] = HomePage(parent= container, controller=self)
self.frames["HomePage"].grid(row=0, column=0, sticky="nsew")
print 321
self.show_frame("HomePage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
print "Je vais te montrer mon frame"
frame = self.frames[page_name]
frame.tkraise()
And the code of the Home Page frame:
class HomePage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.parent = parent
self.controller = controller
#print ("Construction de la page dáccueil")
#LABEL
self.username = Label(self, text="Username:")
self.username.grid(row =0,column =0)
self.username.pack()
#ENTRY
self.username_txb = Entry( self)
self.username_txb.focus_set()
self.username_txb.grid(row =0,column =1)
self.username_txb.pack(side=LEFT)
#LABEL
self.pass_lbl = Label(self, text="Password:")
self.pass_lbl.grid(row =0,column =2)
#ENTRY
self.password_txb = Entry( self, text="Password", show = "*")
self.password_txb.grid(row =0,column =3)
self.password_txb.pack(side=LEFT)
#LOGIN BUTTON
self.login_btn = Button(self, text="Login", command=lambda: controller.show_frame("UploadPage"))
self.login_btn.grid(row =0,column =4)
self.login_btn.pack(side=LEFT)
self.info_pane = PanedWindow()
self.info_pane.grid(row =1,column =0)
self.info_pane.pack(fill="none", expand=True, side=BOTTOM)
self.info_lbl = Label(self, text="More information about access:", fg="blue", cursor="hand2")
self.contact_lbl = Label(self, text="Contact us", fg="blue", cursor="hand2")
self.contact_lbl.grid(row =2,column =0)
self.contact_lbl.pack()
self.contact_lbl.bind("<Button-1>", self.callback)
print ("123Construction de la page dáccueil")
#self.parent.update()
def callback(self, event):
pass
def connect(self,controller ):
login = self.username_txb.get()
pwd = self.password_txb.get()
if(login == "a" and pwd == "a"):
print "Valid account"
self.controller.show_frame("UploadPage")
#UploadPage frame is implemented
The output everytime I execute the code is as following:
1
123Construction de la page dáccueil
Thank you in advance for the help. Hope this will help other people.
First lets address your use of pack() and grid().
Due to how tkinter is set up you cannot use both pack() and grid() on the same widget in a frame or window at one time.
You may use for example pack() to pack the main frame and grid() on the widgets inside that frame but you cannot use both in side the frame.
If one of your issues is where each widget is located and if it is expanding with the window you can manage all that inside of grid() so we can just use grid() here as its what I prefer when writing up a GUI.
Next we need to look at your call to show_frame as you are attempting to show a frame that does not exist in self.frames in the code you have presented us.
I have created a new class so your program can be tested with this line of code:
self.controller.show_frame("UploadPage")
The new class just makes a basic frame with a label in it showing that the frame does rise properly with tkrise().
I did some general clean up as your show_frame method was taking unnecessary steps to raise the frame, your method of importing tkinter is not the best option and some other quality corrections.
Instead of using:
frame = self.frames[page_name]
frame.tkraise()
We can simplify this method with just one line like this:
self.frames[page_name].tkraise()
I have also changed how you are importing tkinter as importing with * can sometimes cause problems if you inadvertently override build in methods. The best option is to import tkinter like this:
import tkinter as tk
Take a look at the below code and let me know if you have any questions. It should provide the info you need to allow the HomePage frame and UploadPage frame to work as intended.
import tkinter as tk
class Skeleton(tk.Tk):
def __init__(self, f,*args, **kwags):
tk.Tk.__init__(self,*args, **kwags)
self.title(f)
self.container = tk.Frame(self, width=512, height=512)
self.container.grid(row=0, column=0, sticky="nsew")
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames["HomePage"] = HomePage(parent=self.container, controller=self)
self.frames["HomePage"].grid(row=0, column=0, sticky="nsew")
self.frames["UploadPage"] = UploadPage(parent=self.container)
self.frames["UploadPage"].grid(row=0, column=0, sticky="nsew")
self.show_frame("HomePage")
def show_frame(self, page_name):
self.frames[page_name].tkraise()
class HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.parent = parent
self.controller = controller
self.username = tk.Label(self, text="Username:")
self.username.grid(row =0,column =0)
self.username_txb = tk.Entry(self)
self.username_txb.focus_set()
self.username_txb.grid(row=0, column=1)
self.pass_lbl = tk.Label(self, text="Password:")
self.pass_lbl.grid(row =0,column =2)
self.password_txb = tk.Entry(self, text="Password", show="*")
self.password_txb.grid(row =0,column =3)
self.login_btn = tk.Button(self, text="Login", command=self.connect)
self.login_btn.grid(row=0, column=4)
self.info_pane = tk.PanedWindow()
self.info_pane.grid(row=1, column=0)
self.info_lbl = tk.Label(self, text="More information about access:", fg="blue", cursor="hand2")
self.contact_lbl = tk.Label(self, text="Contact us", fg="blue", cursor="hand2")
self.contact_lbl.grid(row=2, column=0)
self.contact_lbl.bind("<Button-1>", self.callback)
def callback(self, event):
pass
# webbrowser.open_new("https://www.tno.nl/nl/")
# I do not have the import for this webbrowser so I disabled it for testing.
def connect(self):
login = self.username_txb.get()
pwd = self.password_txb.get()
if(login == "a" and pwd == "a"):
self.controller.show_frame("UploadPage")
class UploadPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
tk.Label(self, text="This upload frame is a test to see if your code is working").grid(row=0, column=0)
if __name__ == "__main__":
app = Skeleton("HomePage")
app.mainloop()

Tkinter OptionMenu widget is not displaying values

At the moment I am working on a project using Python 3.6 Tkinter. At the moment, I am trying to make a user access rights OptionMenu for me to put either "User" or "Admin". I have tried various methods, but cannot seem to either fix it myself or to find helpful documentation online.
The problem isn't in making the OptionMenu and displaying it, nor is it that the value of the StringVar variable isn't changing. The problem is that the text inside of the OptionMenu isn't changing when any new option is selected.
class UsersDetailsEditPage(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
self.title("Edit User Details")
self.option_add("*Font", 'TkDefaultFont')
self.noteBook = ttk.Notebook(self)
for i in range(len(users)):
self.noteBook.add(self.getUserViewFrame(users[i]), text=users[i][2])
self.noteBook.pack()
self.resizable(width=False, height=False)
def getUserViewFrame(self, user):
frame = Frame(self)
frame.grid_rowconfigure(1, weight=1)
frame.grid_columnconfigure(1, weight=1)
Label(frame, text="User's name:").grid(row=0, column=0, sticky=W)
nameText = Text(frame, height=1, width=20)
nameText.insert("1.0", user[2])
nameText.edit_reset()
nameText.grid(row=0, column=1, sticky=E)
Label(frame, text="Username:").grid(row=1, column=0, sticky=W)
usernameText = Text(frame, height=1, width=20)
usernameText.insert("1.0", user[0])
usernameText.edit_reset()
usernameText.grid(row=1, column=1, sticky=E)
Label(frame, text="Password:").grid(row=2, column=0, sticky=W)
passwordText = Text(frame, height=1, width=20)
passwordText.insert("1.0", user[1])
passwordText.edit_reset()
passwordText.grid(row=2, column=1, sticky=E)
# the constructor syntax is:
# OptionMenu(master, variable, *values)
Label(frame, text="User Access:").grid(row=3, column=0, sticky=W)
self.options = StringVar()
self.options.set("User")
self.userAccessDrop = ttk.OptionMenu(frame, self.options, "User", *("User", "Admin"))
self.userAccessDrop.config(width=10)
self.userAccessDrop.grid(row=3, column=1, sticky=E)
return frame
This is the output of the code
I have all the library imports that are needed (I think):
from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk
import csv
import os
If anyone can work out how to make this work it would be much appreciated.
Thanks
I tried your code and it works for me. I can only guess that this is a garbage collection problem. Try assigning the option menu to frame rather than self, so that it doesn't get overwritten.
frame.options = StringVar()
frame.options.set("User")
frame.userAccessDrop = ttk.OptionMenu(frame, frame.options, "User", *("User", "Admin"))
frame.userAccessDrop.config(width=10)
frame.userAccessDrop.grid(row=3, column=1, sticky=E)
You really should rewrite this so that you subclass a Frame to make the user instances.
Edit: for example:
import tkinter as tk
from tkinter import ttk
class UserFrame(tk.Frame):
def __init__(self, master=None, data=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
data = data[2], data[0], data[1] # rearrange data
labels = ("User's name:", "Username:", "Password:")
for row, (label, value) in enumerate(zip(labels, data)):
lbl = tk.Label(self, text=label)
lbl.grid(row=row, column=0, sticky=tk.W)
ent = tk.Entry(self)
ent.insert(0, value)
ent.grid(row=row, column=1, sticky=tk.E)
lbl = tk.Label(self, text="User Access:")
lbl.grid(row=3, column=0, sticky=tk.W)
self.options = tk.StringVar(self, "User")
self.userAccessDrop = ttk.OptionMenu(self,
self.options,
"User", # starting value
"User", "Admin", # options
)
self.userAccessDrop.config(width=10)
self.userAccessDrop.grid(row=len(labels), column=1, sticky=tk.E)
class UsersDetailsEditPage(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Edit User Details")
self.option_add("*Font", 'TkDefaultFont')
self.noteBook = ttk.Notebook(self)
for user in users:
self.noteBook.add(UserFrame(self, user), text=user[2])
self.noteBook.pack()
self.resizable(width=False, height=False)
It's really not functionally any different from what you have, just neater code (which actually makes a huge difference in coding time). Note I also got rid of the evil wildcard import and condensed your code a bit. Remember, if you are copying and pasting code blocks you are doing the computer's job. I also moved you to Entry widgets, which are a single line Text widget. Also, try to avoid the "convenience" initializing and laying out a widget on the same line. It's ugly and it leads to bugs.
I used 'self' in for the "self.options" and "self.userAccessDrop", but since we subclassed the Frame, "self" now refers to the Frame instance. In other words it's the same as my above block where I used "frame.options".

Using tkinter as a start menu for another python program

Hello I'm new to tkinter and coding. I am creating a project called a google bike. I found an instructable on it and I wanted it to look nicer by adding a start menu. It uses python and I am using tkinter to create a start page of some sort for it. I am trying to create a button where I would just press it in order to launch the program. This is the code I used to make the tkinter to make it look nice and sort of like a start menu.
import Tkinter as tk
from Tkinter import *
import ttk
def OpenServer():
execfile("C:/users/Broadway Jewelry/Downloads/server/server2/server.py")
class Camacho(tk.Tk):
def __init__(app):
tk.Tk.__init__(app)
container = tk.Frame(app, width=800, height=500)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
app.frames = {}
for F in (StartPage, Instructionspage, OptionsPage, DemoPage):
frame = F(container, app)
app.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
app.show_frame(StartPage)
def show_frame(app, cont):
frame = app.frames[cont]
frame.tkraise()
class StartPage(Frame):
def __init__(app, parent, controller):
Frame.__init__(app, parent, background='black', width=800, height=500)
earth = tk.PhotoImage(file="C:/Users/Broadway Jewelry/Desktop/Bluestamp/earth.gif")
BGlabel = tk.Label(app,image=earth)
BGlabel.image = earth
BGlabel.place(x=300,y=0,width=400,height=400)
StartProgram = Button(app, text="Start Google Bike",
bd=10, command=OpenServer)
StartProgram.place(x=100,y=75,width=200,height=44)
Instructions = Button (app, text="Instructions", bd=10,
command=lambda: controller.show_frame(Instructionspage))
Instructions.place(x=100,y=150,width=200,height=44)
Options = Button (app, text="Options/Setup", bd=10,
command=lambda: controller.show_frame(OptionsPage))
Options.place(x=100,y=225,width=200,height=44)
Quit = Button (app, text="Exit Program", command=quit, bd=10)
Quit.place(x=100,y=300,width=200,height=44)
class Instructionspage(Frame):
def __init__(app, parent, controller):
Frame.__init__(app, parent, background='black', width=800, height=500)
label = tk.Label(app, text="Instructions\n \nPedal=go forward\npress button=reverse\nbutton side to steer",
font=(200), background='black', fg='white')
label.place(x=300,y=0,width=400,height=400)
StartProgram = Button(app, text="Start Google Bike", bd=10, command=OpenServer)
StartProgram.place(x=100,y=225,width=200,height=44)
GoBack = Button (app, text="Go Back", bd=10,
command=lambda: controller.show_frame(StartPage))
GoBack.place(x=100,y=300,width=200,height=44)
class OptionsPage (Frame):
def __init__(app, parent, controller):
Frame.__init__(app, parent, background='black', width=800, height=500)
GoBack = Button (app, text="Go Back", width=50, bd=10,
command=lambda: controller.show_frame(StartPage))
GoBack.place(x=100,y=300,width=200,height=44)
StartProgram = Button(app, text="Start Google Bike", bd=10, command=OpenServer)
StartProgram.place(x=100,y=225,width=200,height=44)
ShowDemo = Button (app, text="Show Demo Screen", bd=10,
command=lambda: controller.show_frame(DemoPage))
ShowDemo.place(x=100,y=150,width=200,height=44)
class DemoPage (Frame):
def __init__(app, parent, controller):
Frame.__init__(app, parent, background='black', width=800, height=500)
earth = tk.PhotoImage(file="C:/Users/Broadway Jewelry/Desktop/Bluestamp/Google-Bike.gif")
BGlabel = tk.Label(app,image=earth)
BGlabel.image = earth
BGlabel.place(x=300,y=0,width=400,height=400)
GoBack = Button (app, text="Go Back", width=50, bd=10,
command=lambda: controller.show_frame(OptionsPage))
GoBack.place(x=100,y=300,width=200,height=44)
app = Camacho()
app.mainloop()
I am thinking of finding some way to close the tkinter window and just have the python running the google bike. If anyone can help thank you very much
Just add a single line to OpenServer (which seems to be the function that runs the real program):
def OpenServer():
execfile("C:/users/Broadway Jewelry/Downloads/server/server2/server.py")
app.destroy()
I would also recommend that, within any given class, you refer to an instance of that class as the standard self rather than app. As it is, it's giving the impression that the Camacho object you instantiate at the end of the program is being passed around and referred to by its app name. However, that is not actually the case: in Camacho, app could refer to any Camacho object, even if you did something like test = Camacho(). Within Camacho, that test object would refer to itself as app. In the other classes, it's even more misleading, as the Camacho object named app is actually not app in there at all, but rather controller.
Confused? Even I find it messy - that's why it's better to use self, and avoid using the same apparent variable name in multiple scopes.

Categories