set expiration date for python application - python

Hello dear programmers,
I have generated an EXE with pyinstaller. However, I would like this EXE to be valid for only 6 months so that users have to re-download a improved EXE with updates from a download-center. Is there any way to add this in the program code?
Unfortunately I can't include a standard update function because the users often don't have internet.
I am curious about your suggestions. Thanks a lot guys! I've attached a small snippet of the program below.
import tkinter as tk
from tkinter import ttk
class Win1:
def __init__(self, master):
self.master = master
self.topFrame = tk.Frame(self.master)
self.topFrame.grid(row=0, column=0, sticky='news', ipady = 5)
self.B_GapFrame = tk.Frame(self.master)
self.master.resizable(False, False)
self.gapType = tk.StringVar(self.master)
self.choiceGap = ['RBC, TUR']
self.gapType.set('') # set the default option
self.ctngMenu = tk.OptionMenu(self.topFrame, self.gapType, *self.choiceGap, command=self.choose_gap_handle)
self.ctngMenu.config(width=40)
self.LabelGap = tk.Label(self.topFrame, text="Select TYPE")
self.LabelGap.grid(row = 3, column = 0,sticky = "W")
self.ctngMenu.grid(row = 3, column =2,sticky = "W", columnspan = 3)
self.frameVar = tk.StringVar(self.master)
self.choiceFrame = "Test"
self.frameVar.set('') # set the default option
self.frameMenu = ttk.Combobox(self.topFrame, values= self.choiceFrame, state = "readonly", justify = "center", textvariable = self.frameVar, width = 12)
self.frameMenu.grid(row = 1, column =2,sticky = "W", pady = 7, columnspan = 3)
self.LabelFrame = tk.Label(self.topFrame, text="Select TUR ")
self.LabelFrame.grid(row = 1, column = 0,sticky = "W",pady =7)
def choose_gap_handle(self, selected_Gap):
if selected_Gap == 'RBC, TUR':
self.B_GapFrame.tkraise()
self.B_GapFrame.grid(row=2, column=0, sticky='news')
root = tk.Tk()
root.geometry("+50+50")
app = Win1(root)
root.mainloop()

It all depends how 'cruel' you want to be.
One option might be to add a hard coded date in your code like
import datetime
RLS_DATE = datetime.date(2021, 02, 24)
and then before your main code section something like:
if datetime.date.today() - RLS_DATE > 7 * 30: # about 7 months
# as #TheLizzard suggested delete the executable.
# You might do this only if running under
# py2exe to avoid deleting your own source code
if getattr(sys, 'frozen', False):
os.remove(sys.argv[0])
elif datetime.date.today() - RLS_DATE > 6 * 30: # about 6 months
# write your own coded, that shows just a popup and lets the user
# continue using the app.
If you don't want to hardcode the release date, you could write a smarter script to create your executable and add some data for py2exe, that contains the date at which you compiled the executable.
What I mean with being cruel.
Either just show a popup, that the user should update,
or add a popup and block the user interface for x seconds.
The delay might increase the longer the user doesn't update
or just exit the program
or even delete the program
Of course all these options are not safe.
This is just for standard users.
If somebody insists to run your program, he can modify the generated executable to ignore the date.

You can save your app first launch time to local file and then compare it with current time.
import os
import time
import tempfile
import tkinter as tk
EXPIRATION_TIME_SEC = 100
class App(object):
def __init__(self):
config = os.path.join(tempfile.gettempdir(), 'config.txt')
self.start_time = time.time()
if os.path.isfile(config):
with open(config, 'r') as f:
self.start_time = int(float(f.read()))
else:
with open(config, 'w') as f:
f.write(f'{self.start_time}')
self.root = tk.Tk()
self.label = tk.Label(self.root)
self.label.pack()
self.update_label()
self.root.mainloop()
def update_label(self):
if time.time() - self.start_time > EXPIRATION_TIME_SEC:
self.root.quit()
self.label.configure(text=f'Expired in: {int(self.start_time+EXPIRATION_TIME_SEC-time.time())} sec.')
self.root.after(1000, self.update_label)
App()
Output:

Related

Collecting variables from a GUI

I have a simple GUI where the user selects a file, which becomes a variable for my main code. Here, my variable output should be the database path (gui_db_path) which the user inputs. When I run this code, called gui_test.py, the variable is printable, and prints to the console.
class GUI:
def __init__(self, window):
# 'StringVar()' is used to get the instance of input field
self.input_db_text = StringVar()
window.title("HyPep 1.0")
window.geometry("700x700")
ttk.Label(window, text='Database sequences .csv:').grid(row=1,column=0)
ttk.Button(window, text = "Browse", command = lambda: self.set_path_database_field()).grid(row = 1,column=2, ipadx=5, ipady=0)
ttk.Entry(window, textvariable = self.input_db_text, width = 70).grid( row = 1, column = 1, ipadx=1, ipady=1)
ttk.Button(window, text = "Analyze").grid(row = 10,column=1, ipadx=5, ipady=15)
def set_path_database_field(self):
self.path_db = askopenfilename()
self.input_db_text.set(self.path_db)
def get_database_path(self):
""" Function provides the database full file path."""
return self.path_db
if __name__ == '__main__':
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
print(gui.path_db, '\n', gui.path_qu)
gui_db_path = gui.path_db
print(gui_db_path)
My issue is that I need to retrieve this variable for use in another file, user_input.py, but is no longer callable. My code for user_input.py is:
from gui_test import gui_db_path
print(gui_db_path)
Instead of printing to the console in this instance, I get:
ImportError: cannot import name 'gui_db_path' from 'gui_test'
I'm sure there is a simple solution that I am missing, can anyone shed some light?
...
Update: much closer, need to expand the solution:
How would I go about expanding this to retrieve multiple paths? I have been trying this:
gui_test.py:
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
def get_qu_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_query_path()
user_input.py:
from gui_test import get_db_path
from gui_test import get_qu_path
gui_db_path = get_db_path()
gui_qu_path = get_qu_path()
Note that the code inside if __name__ == '__main__' block will not be executed when the file is imported. You need to put those code inside a function instead and returns the path at the end of the function:
gui_test.py
...
def get_db_path():
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
return gui.get_database_path()
Then import this function inside user_input.py:
from gui_test import get_db_path
gui_db_path = get_db_path()
print(gui_db_path)

tkinter - I'm trying to disable the forward / backward button but it doesn't seem to work

I'm trying to build an image viewer that loads the images from a folder. It should have forward / backward and quit buttons. It seems to work just fine with one issue:
So I just take the image paths with this:
def get_files_from_folder(path, allowed_extensions):
found_paths = []
for (dir_path, _, file_names) in os.walk(path):
for file_name in file_names:
for allowed_extension in allowed_extensions:
if file_name.lower().endswith(allowed_extension.lower()):
found_paths.append(os.path.join(dir_path, file_name))
break
return found_paths
I have the tkinter UI for the image viewer:
class UI:
def __init__(self, icon_path, images_folder_path):
self.current_index = 0
self.root = tk.Tk()
self.root.title('Images')
self.root.iconbitmap(icon_path)
self.button_quit = tk.Button(self.root, text = 'Quit', padx = 60, command = self.root.quit)
self.button_forward = tk.Button(self.root, text = '>>', command = self.forward)
self.button_backward = tk.Button(self.root, text = '<<', command = self.backward)
self.button_quit.grid(row = 1, column = 1)
self.button_forward.grid(row = 1, column = 2)
self.button_backward.grid(row = 1, column = 0)
self.images_paths = get_files_from_folder(images_folder_path, ['.jpg', '.png'])
self.tk_images = []
print(get_files_from_folder)
for image_path in self.images_paths:
self.tk_images.append(ImageTk.PhotoImage(Image.open(image_path)))
self.current_image = tk.Label(image = self.tk_images[0])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
self.root.mainloop()
And for some reason, here when I'm using the tk.DISABLED, it just won't disable it
def backward(self):
if self.current_index == 0:
self.button_backward = self.button_backward = tk.Button(self.root, text = '<<', command = self.backward, state = tk.DISABLED)
self.current_image.grid_forget()
self.current_index -= 1
self.current_image = tk.Label(image = self.tk_images[self.current_index])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
same for the forward:
def forward(self):
self.current_image.grid_forget()
if self.current_index == len(self.tk_images)-1:
self.button_forward = self.button_forward = tk.Button(self.root, text = '>>', command = self.forward, state = tk.DISABLED)
else:
self.button_forward.state = tk.ACTIVE
self.current_index += 1
self.current_image = tk.Label(image = self.tk_images[self.current_index])
self.current_image.grid(column = 0, row = 0, columnspan = 3)
There's at least a couple of things wrong with your current code regarding the forward and backward Buttons commands. As for disabling the Buttons, that can be done by calling their config() method — not by creating a new one or assigning a new value to the existing ones state attribute (i.e. self.button_forward.state = tk.ACTIVE).
Similarly it's not a good practice to continually create new tk.Label widgets every time one of the buttons is pressed and the image on it changes. It's better to change the image= option of the existing Label. Doing this also often simplifies what needs to be done.
In addition your logic for clamping the self.current_index was flawed and would allow it to get out of range and IndexErrors to occur. This is more complicated than I anticipated, but I think I figured out a good way to handle it, namely by putting all the logic a private method _change_current_index() and calling it from the forward and backward callback functions.
Lastly, user #acw1668 commented that it loading all images into memory at program start might be two slow. To avoid that, I replaced the list of all the loaded images you had (self.tk_images) with calls to a another new private method named _open_image() which caches its results by having the functools.lru_cache decorator applied to it. This makes the method remember what values it has already returned for a limited number of indices thereby restricting the number of them in memory at any one time.
Note I also reformatted the code to better conform to the PEP 8 - Style Guide for Python Code guidelines to make it more readable.
from functools import lru_cache
import tkinter as tk
import os
from PIL import Image, ImageTk
class UI:
IMG_CACHE_SIZE = 10
def __init__(self, icon_path, images_folder_path):
self.current_index = 0
self.root = tk.Tk()
self.root.title('Images')
# self.root.iconbitmap(icon_path)
self.button_quit = tk.Button(self.root, text='Quit', padx=60, command=self.root.quit)
self.button_forward = tk.Button(self.root, text='>>', command=self.forward)
self.button_backward = tk.Button(self.root, text='<<', command=self.backward)
self.button_quit.grid(row=1, column=1)
self.button_forward.grid(row=1, column=2)
self.button_backward.grid(row=1, column=0)
self.image_paths = get_files_from_folder(images_folder_path, ['.jpg', '.png'])
self.current_image = tk.Label(image=self._open_image(0))
self.current_image.grid(column=0, row=0, columnspan=3)
self._change_current_index() # Initializes fwd and bkd button states.
self.root.mainloop()
#lru_cache(maxsize=IMG_CACHE_SIZE)
def _open_image(self, i):
image_path = self.image_paths[i]
return ImageTk.PhotoImage(Image.open(image_path))
def backward(self):
self._change_current_index(-1)
def forward(self):
self._change_current_index(1)
def _change_current_index(self, delta=0):
self.current_index += delta
# Update state of forward and backward buttons based on new index value.
bkd_state = (tk.DISABLED if self.current_index == 0 else tk.ACTIVE)
self.button_backward.config(state=bkd_state)
fwd_state = (tk.DISABLED if self.current_index == len(self.image_paths)-1 else tk.ACTIVE)
self.button_forward.config(state=fwd_state)
# Update image on Label.
self.current_image.config(image=self._open_image(self.current_index))
def get_files_from_folder(path, allowed_extensions):
found_paths = []
for (dir_path, _, file_names) in os.walk(path):
for file_name in file_names:
for allowed_extension in allowed_extensions:
if file_name.lower().endswith(allowed_extension.lower()):
found_paths.append(os.path.join(dir_path, file_name))
break
return found_paths
if __name__ == '__main__':
UI('.', './images_test')

Python tkinter Pop Up Progress Bar

I have created a simple program to download from a private azure container and rename a series of .jpg files listed in a csv file. I'm still learning python so I am sure the code is a bit on the rough side! That said, the code works fine and the files download correctly. However, I would like to display a pop up progress bar showing the current progress. I have looked at a number of examples but I'm not sure how best to approach this. Could anyone offer some pointers on the best way? Thanks.
from tkinter import messagebox
import urllib.request
import csv
from datetime import datetime, timedelta
from azure.storage.blob import BlockBlobService
from azure.storage.blob.models import BlobPermissions
from azure.storage.blob.sharedaccesssignature import BlobSharedAccessSignature
account_name = '***'
account_key = '***'
top_level_container_name = '***'
blob_service = BlockBlobService(account_name, account_key)
blob_shared = BlobSharedAccessSignature(account_name, account_key)
root = Tk()
root.withdraw()
csvDir = filedialog.askopenfilename(initialdir="/", title="SELECT CSV FILE", filetypes=(("CSV files", "*.csv"), ("all files", "*.*")))
imageDir = filedialog.askdirectory()
with open(csvDir) as images:
images = csv.reader(images)
img_count = 1
for image in images:
sas = blob_shared.generate_blob(container_name=top_level_container_name, blob_name=image[0], permission=BlobPermissions.READ, start=datetime.now(), expiry=datetime.now() + timedelta(hours=1))
sas_url = 'https://' + account_name + '.blob.core.windows.net' + '/' + top_level_container_name + '/' + image[0] + '?' + sas
print(sas_url)
urllib.request.urlretrieve(sas_url, imageDir + "/{}.jpg".format(image[1]))
img_count += 1
messagebox.showinfo("Complete", "Print images have been downloaded")
root.mainloop()
The Base Code
The base code that this project uses as its baseline is Shichao's Blog Post, Progress/speed indicator for urlretrieve() in Python and my version of it is simply a tkinter wrapper is big props to Shichao for the amazing Blog Post.
The Tkinter Code
If you merely wish to see the code without the breakdown, you simply see it and copy and paste the given example:
import time
import urllib.request
import tkinter as tk
import tkinter.ttk as ttk
class download_toplevel:
def __init__(self, master):
self.master = master
self.download_button = tk.Button(self.master, text="Download", command=self.download)
self.download_button.grid(row=0, column=0)
self.root = tk.Toplevel(self.master)
self.progress_bar = ttk.Progressbar(self.root, orient="horizontal",
length=200, mode="determinate")
self.progress_bar.grid(row=0, column=0)
self.progress_bar.grid_rowconfigure(0, weight=1)
self.progress_bar.grid_columnconfigure(0, weight=1)
self.progress_bar["maximum"] = 100
self.root.withdraw()
# See https://blog.shichao.io/2012/10/04/progress_speed_indicator_for_urlretrieve_in_python.html
def reporthook(self, count, block_size, total_size):
print(count, block_size, total_size)
if count == 0:
self.start_time = time.time()
return
duration = time.time() - self.start_time
progress_size = int(count * block_size)
speed = int(progress_size / (1024 * duration))
percent = min(int(count*block_size*100/total_size), 100)
print(percent)
self.progress_bar["value"] = percent
self.root.update()
def save(self, url, filename):
urllib.request.urlretrieve(url, filename, self.reporthook)
def download(self):
self.progress_bar["value"] = 0
self.root.deiconify()
self.save("https://files02.tchspt.com/storage2/temp/discord-0.0.9.deb", "discord-0.0.9.deb")
self.root.withdraw()
def main():
root = tk.Tk()
#root.withdraw()
downloader = download_toplevel(root)
root.mainloop()
if __name__ == '__main__':
main()
The Breakdown
The imports
import time
import urllib.request
import tkinter as tk
import tkinter.ttk as ttk
The import of tkinter.ttk is important as the Progress Bar widget is not found in the default tkinter module.
The main loop
def main():
root = tk.Tk()
downloader = download_toplevel(root)
root.mainloop()
if __name__ == '__main__':
main()
The line if __name__ == '__main__' means if the code is run directly without being imported by another python program execute the following statement, so on you running $ python3 main.py, the code will run the main function.
The main function works by creating the main window root, and passing the window into a variable within the class download_toplevel. This is a neater way of writing python code and is more versatile when working with tkinter as it simplifies the code when looking at it in the future.
The Download Button
self.master = master
self.download_button = tk.Button(self.master, text="Download", command=self.download)
self.download_button.grid(row=0, column=0)
This code adds a button labelled download bound to the command download
The Toplevel Window
self.root = tk.Toplevel(self.master)
This creates a toplevel window that appears on top of the master window
The Progress Bar
self.progress_bar = ttk.Progressbar(self.root, orient="horizontal",
length=200, mode="determinate")
self.progress_bar.grid(row=0, column=0)
self.progress_bar.grid_rowconfigure(0, weight=1)
self.progress_bar.grid_columnconfigure(0, weight=1)
self.progress_bar["maximum"] = 100
This creates a ttk.Progressbar onto the Toplevel window, oriented in the x plane, and is 200px long. The grid_*configure(0, weight=1) means that the progress bar should grow to fit the given space, to make sure this happens you should also include , sticky='nsew' in the grid command. self.progress_bar["maximum"] = 100 means that the max value is 100%.
Withdraw
self.root.withdraw()
This withdraws the Toplevel window until it is actually required, i.e. when the download button is pressed.
The Report Hook
def reporthook(self, count, block_size, total_size):
print(count, block_size, total_size)
if count == 0:
self.start_time = time.time()
return
duration = time.time() - self.start_time
progress_size = int(count * block_size)
speed = int(progress_size / (1024 * duration))
percent = min(int(count*block_size*100/total_size), 100)
This code is taken directly from Shichao's Blog Post, and simply works out the percentage completed.
self.progress_bar["value"] = percent
self.root.update()
The Progress Bar is then set to the current percentage and the Toplevel is updated, otherwise it remains as a black window (tested on Linux).
The save function
def save(self, url, filename):
urllib.request.urlretrieve(url, filename, self.reporthook)
This bit of code has been straight up taken from the Blog Post and simply downloads the file and on each block being downloaded and written the progress is sent to the self.reporthook function.
The Download Function
def download(self):
self.progress_bar["value"] = 0
self.root.deiconify()
self.save("https://files02.tchspt.com/storage2/temp/discord-0.0.9.deb", "discord-0.0.9.deb")
self.root.withdraw()
The download function resets the Progress Bar to 0%, and then the root window is raised (outside of the withdraw). Then the save function is run, you may wish to rewrite the self.save call to read self.after(500, [the_function]) so that your main window is still updated as this program is run. Then the Toplevel window is withdrawn.
Hope this helps,
James

How to retrieve file name after choosing in dialog box using Python Tkinter

Please advise on how to retrieve a file's full path into a variable after i pick one using tkinter
The whole idea of my GUI is to:
1. Have few buttions
2. Have address bar with file's full address
Once user clicks the button and picks the file >> file's path is displayed in the address bar as well as stored in a separate variable for future usage later in code
I've done some testing, but when checking for retrieved value - I get None.
def file_picker():
"""Pick enova .xlsx file"""
path = filedialog.askopenfilename(filetypes=(('Excel Documents', '*.xlsx'), ('All Files', '*.*')))
return path
file_button = tkinter.Button(root, text='Users File', width=20, height=3,
bg='white', command=custom_functions.file_picker).place(x=30, y=50)
Apart form that I found another code snippet, but this simply captures line onto the GUI interface, not saving file path in any variable though:
def browsefunc():
filename = filedialog.askopenfilename()
pathlabel.config(text=filename)
print(pathlabel)
browsebutton = tkinter.Button(root, text="Browse", command=browsefunc).pack()
pathlabel = tkinter.Label(root).pack()
Expected result: https://imgur.com/a/NbiOPzG - unfortunatelly I cannot post images yet so uploaded one onto imgur
To capture the full path of a file using Tkinter you can do the following. The output of your full file path will be displayed in the "Entry" field / your address bar as per your requirement in your original post.
Update
import tkinter
from tkinter import ttk, StringVar
from tkinter.filedialog import askopenfilename
class GUI:
def __init__(self, window):
# 'StringVar()' is used to get the instance of input field
self.input_text = StringVar()
self.input_text1 = StringVar()
self.path = ''
self.path1 = ''
window.title("Request Notifier")
window.resizable(0, 0) # this prevents from resizing the window
window.geometry("700x300")
ttk.Button(window, text = "Users File", command = lambda: self.set_path_users_field()).grid(row = 0, ipadx=5, ipady=15) # this is placed in 0 0
ttk.Entry(window, textvariable = self.input_text, width = 70).grid( row = 0, column = 1, ipadx=1, ipady=1) # this is placed in 0 1
ttk.Button(window, text = "Enova File", command = lambda: self.set_path_Enova_field()).grid(row = 1, ipadx=5, ipady=15) # this is placed in 0 0
ttk.Entry(window, textvariable = self.input_text1, width = 70).grid( row = 1, column = 1, ipadx=1, ipady=1) # this is placed in 0 1
ttk.Button(window, text = "Send Notifications").grid(row = 2, ipadx=5, ipady=15) # this is placed in 0 0
def set_path_users_field(self):
self.path = askopenfilename()
self.input_text.set(self.path)
def set_path_Enova_field(self):
self.path1 = askopenfilename()
self.input_text1.set(self.path1)
def get_user_path(self):
""" Function provides the Users full file path."""
return self.path
def get_enova_path1(self):
"""Function provides the Enova full file path."""
return self.path1
if __name__ == '__main__':
window = tkinter.Tk()
gui = GUI(window)
window.mainloop()
# Extracting the full file path for re-use. Two ways to accomplish this task is below.
print(gui.path, '\n', gui.path1)
print(gui.get_user_path(), '\n', gui.get_enova_path1())
Note: I added a comment to point you to where the full file path is stored, in my example it's 'path' & 'path1'.

Best way of storing data in this scenario using text files?

I am creating a software for a driving school but I'm limited to what plugins are available to me. I also have to use text files for the databases rather than being able to use SQL or the like. I can't seem to figure out this next stage.
This is the scenario:
Each instructor has a unique login, and they can choose to book a lesson onto the calendar/timetable, or just simply view it. The issue is that I'm unsure how to store the data without creating a very large number of text files, because originally my idea was that each instructor would be able to view their own individual calendar/timetable (linked together via their unique login) however because of how the calendar works, each week would require it's own text file and then that number of text files would be multiplied by the total number of instructors.
The code for the calendar and timetable is(not perfectly working but the main principles are there):
import sys
import re
from tkinter import *
import tkinter.messagebox as box
import tkinter.font as Font
import time
import calendar
#from tkinter import font
from tkinter import ttk
def Calendar():
def mainTimetable():
#root1 = Tk()
#frame = Frame(root1)
days = ["","Mon","Tue","Wed","Thur","Fri","Sat","Sun"]
hours = ["","08:00","09:00","10:00","11:00","12:00","13:00","14:00","15:00",
"16:00","17:00","18:00"]
class WrappedButtons:
def __init__(self, master, **kwargs):
self.button = Button(master,font=('Courier'),fg='#333333',bg='#CCCCCC', **kwargs)
self.name = ''
def nameToggle(self):
if not self.name:
self.name = nameEntry.get()
else:
self.name = ''
self.button.config(text=self.name)
def subTimetable():
global nameEntry
popup = Toplevel()
#popup.title('Timetable')
for x in range(12):
Label(popup,font=('Courier'),fg='#333333',bg='#CCCCCC', text=hours[x]).grid(row=x,column=0)
for x in range(8):
Label(popup,font=('Courier'),fg='#333333',bg='#CCCCCC',text=days[x]).grid(row=0,column=x)
if x < 7: # this avoids an extra column of buttons
for y in range(11):
temp = WrappedButtons(popup,width=20)
temp.button.config(command=temp.nameToggle)
temp.button.grid(row=y+1,column=x+1)
Label(popup,text = "Enter the name of pupil and click the slot you wish to fill/remove: ")\
.grid(row=13,column=0,columnspan=4)
nameEntry = Entry(popup,width=40)
nameEntry.grid(row=13,column=4,columnspan=2)
backButton = Button(popup,font=('Courier'),fg='#333333',bg='#CCCCCC',text='Back',command= popup.destroy)#lambda: [f() for f in[popup.destroy,ownermenu]]).grid(row=13,column=6) # Add functionality to return to user page
submitButton = Button(popup,text='Submit Data').grid(row=13,column=7)
menubar = Menu(popup)
filemenu = Menu(menubar,tearoff=0)
filemenu.add_command(label='Home',command=lambda:[f() for f in [popup.destroy,home1]])
filemenu.add_separator()
filemenu.add_command(label='Exit',command=popup.destroy)
menubar.add_cascade(label='Page',menu=filemenu)
popup.config(menu=menubar)
popup.configure(bg='#333333')
#popup.overrideredirect(True)
#popup.geometry("{0}x{1}+0+0".format(popup.winfo_screenwidth(), popup.winfo_screenheight()))
subTimetable()
#root.mainloop()
root = Tk()
root.title('Calendar')
root.configure(bg='#333333')
permanentFrame,newFrame = Frame(root), Frame(root)
permanentFrame.pack(side='top')
newFrame.pack(side='top')
months = {1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31}
lbMonths = Label(permanentFrame,text='Months: ',fg='#333333',bg='#CCCCCC')
lbMonths.pack()
dropMonths = ttk.Combobox(permanentFrame, values=list(months.keys()))
dropMonths.pack()
class ButtonsWrap:
def __init__(self,master,month,day, **kwargs):
self.button = Button(master,**kwargs)
self.monthInd = month-1
self.dayInd = day-1
self.toplevel = False
self.thing = None
def popClose(self):
self.thing.destroy()
self.toplevel = False
self.thing = None
def popup(self):
if not self.toplevel:
self.thing = Toplevel()
self.toplevel = True
#Label(self.thing, command=mainTimetable).pack()
self.popClose
mainTimetable()
self.thing.protocol("WM_DELETE_WINDOW",self.popClose) #Add back in when 'back' functionality is complete
buttons = []
def makeCalendar(blank):
curMonth = int(dropMonths.get())
for i in buttons:
if i.thing:
i.thing.destroy()
for i in newFrame.slaves():
i.destroy()
rows = [Frame(newFrame) for i in range(5)]
[i.pack() for i in rows]
curDay = 1
for i in range(35):
if len(rows[0].slaves()) == 7:
rows.pop(0)
temp = ButtonsWrap(rows[0], curMonth,curDay,text='',height=1,width=2)
temp.button.pack(side='left')
buttons.append(temp)
if curDay <= months[curMonth]:
temp.button.config(text=curDay,command=temp.popup)
curDay += 1
dropMonths.bind('<<ComboboxSelected>>', makeCalendar)
menubar = Menu(root)
filemenu = Menu(menubar,tearoff=0)
filemenu.add_command(label='Home',command=lambda:[f() for f in [root.destroy,home1]])
filemenu.add_separator()
filemenu.add_command(label='Exit',command=root.destroy)
menubar.add_cascade(label='Page',menu=filemenu)
root.config(menu=menubar)
root.overrideredirect(True)
root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
root.mainloop()
Calendar()
It works by allowing the instructor to type into the timetable, and then my aim is to save the timetable so that it can be referenced in the future (so it doesn't reset every time it's closed). What would be an efficient way of achieving my goal?
Feel free to ask questions if I've poorly explained the problem, as I feel I may have.

Categories

Resources