Python3, difficulty with classes and tkinter - python-3.x

First of all to kick it off,
I'm not great at programming,
I have difficulty with understanding most basics,
I always try doing my uterly best to solve things like this myself.
I'm trying to create a simple gui that makes json files. Everything works fine. Fine in the sense that I'm able to create the files.
Now I wanted to get my code cleaned up and to the next level. I have added tabs to the tkinter screen and that is where the troubles starts. Because when I'm, on a differend tab, the function doesn't get the current selected items, so I added buttons to save that list and then move to different tab.
I have a function(Save_List_t), which looks at the selected items from the listbox(a_lsb1) and saves them to a list(choice_list_t). This function runs when I press button(a_button).
After doing that I got a problem, I don't want to use "global" but I need the list in a other function(Mitre_Gen_Techs) to generate the files. This function runs when I press a button on the third tab.(c.button1)
To tackel this problem, I saw a post where someone uses a class to fix it. However even after reading to the documentation about classes I still don't truely get it.
Now I'm stuck and get the error. Which I don't find that strange, it makes sense to me why it gives the error but what am I doing wrong or how do I solve this issue.
The error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\thans\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
TypeError: Save_List_t() missing 1 required positional argument: 'self'
The code I wrote:
from tkinter import *
from attackcti import attack_client
from mitretemplategen import *
from tkinter import ttk
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mitre ATT&Ck
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ac = attack_client()
groups = ac.get_groups()
groups = ac.remove_revoked(groups)
techs = ac.get_enterprise_techniques()
techs = ac.remove_revoked(techs)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Tkinter screen setup
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
root = Tk()
root.title("Mitre Att&ck")
root.minsize(900, 800)
root.wm_iconbitmap('')
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Functions / classes
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Screen(object):
def __init__(self):
self.choice_list_t = []
self.choice_list_g = []
def Save_List_t(self):
for item in a_lsb2.curselection():
self.choice_list_t.append(a_lsb2.get(item))
print(self.choice_list_t)
def Save_List_g(self):
choice_list_g = []
for item in b_lsb1.curselection():
self.choice_list_g.append(b_lsb1.get(item))
print(self.choice_list_g)
def Mitre_Gen_Techs(self):
# Gen the json file
# mitre_gen_techs(self.choice_list_t, techs)
#testing class
print(self.choice_list_t)
def Mitre_Gen_Groups(self):
# Gen the json file
# mitre_gen_groups(self.choice_list_g, groups)
#testing class
print(self.choice_list_g)
def main():
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# First Tkinter tab
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
rows = 0
while rows < 50:
root.rowconfigure(rows, weight=1)
root.columnconfigure(rows, weight=1)
rows += 1
# Notebook creating tabs
nb = ttk.Notebook(root)
nb.grid(row=1, column=0, columnspan=50, rowspan=50, sticky='NESW')
# Create the differend tabs on the notebook
tab_one = Frame(nb)
nb.add(tab_one, text='APG')
tab_two = Frame(nb)
nb.add(tab_two, text='Actors')
tab_gen = Frame(nb)
nb.add(tab_gen, text='test')
# =-=- First Tab -=-=
# List box 1
a_lsb1 = Listbox(tab_one, height=30, width=30, selectmode=MULTIPLE)
# List with techs
a_lsb2 = Listbox(tab_one, height=30, width=30, selectmode=MULTIPLE)
for t in techs:
a_lsb2.insert(END, t['name'])
# Save list, to later use in Screen.Mitre_Gen_Techs
a_button = Button(tab_one, text="Save selected", command=Screen.Save_List_t)
# =-=- Second Tab -=-=
# List with TA's
b_lsb1 = Listbox(tab_two, height=30, width=30, selectmode=MULTIPLE)
for g in groups:
b_lsb1.insert(END, g['name'])
# Save list, to later use in Screen.Mitre_Gen_Groups
b_button = Button(tab_two, text="Save selected", command=Screen.Save_List_g)
# =-=- Third tab -=-=
c_button = Button(tab_gen, text="Print group json", command=Screen.Mitre_Gen_Groups)
c_button1 = Button(tab_gen, text="Print techs json", command=Screen.Mitre_Gen_Techs)
# Placing the items on the grid
a_lsb1.grid(row=1, column=1)
a_lsb2.grid(row=1, column=2)
b_lsb1.grid(row=1, column=1)
a_button.grid(row=2, column=1)
b_button.grid(row=2, column=1)
c_button.grid(row=2, column=1)
c_button1.grid(row=2, column=2)
root.mainloop()
# If main file then run: main()
if __name__ == "__main__":
main()
The application:
Image

I found someone who explained what was wrong.
Credits to Scriptman ( ^ , ^ ) /
simply adding:
sc = Screen()
And changing:
Button(tab_apg, text="Save selected", command=sc.Save_List_t)
Resolved the issue.

Related

Integrating class into my existing code or not? Tkinter, python 3 [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have this GUI application that I have been obsessing about for over a week now and I think it is slightly harder than what I can manage with the limited programming/python knowledge I have gained so far - however, I just can't stop thinking about it and it is driving me insane that I can't figure it out.
I have created a GUI with Tkinter which is a Todo-list application. However, every task in the application must have some information associated with it.
So let's say we create a task called "Homework".
I want to associate some attributes with Homework.
So, among others, some attributes would be "Impact" which represents the impact of completing the task on a scale from 0-10 (e.g. 10: I will fail my class if I don't complete my Homework task.), and deadline - which is self-explanatory. Those are just 2 of the attributes I want (there will be more though).
So as far as I have come to understand, this would be a great time to utilize classes.
If I understand it correctly, I would have to create a class Task: and then set the attributes for every instance of Task to something.
I created the GUI before I became familiar with classes and some of the attribute information I want to associate with a given task can already be specified in the GUI (without any functionality) after creating a task, but I don't know how to use that information and connect it to the given task so that it has the functionality that I want.
I want to be able to enter a task, specify some attributes associated with that task, and then I want to be able to 'summon' that task and its' associated attributes for some simple math and/or sorting.
I only want the user to see that task itself - the calculations/sorting will just happen behind the scenes.
My question:
Should I do this with classes or is there a more beginner-level approach to this which would be easier to implement into my already existing code?
I really, really want to figure out how to go about this so any qualified pointers or in the right direction or examples, explanations, etc. will be truly appreciated.
This is my code:
from tkcalendar import *
import tkinter.messagebox # Import the messagebox module
import pickle # Module to save to .dat
import tkinter as tk
TASKS_FILE = "tasks.dat" ##Where the task strings are saved
task_list = [] ##Don't know what I am doing yet
impact_number = [] ##Don't know if this could be useful
root = tk.Tk() # ?????
root.title('SmaToDo') # Name of the program/window
# ROOT WINDOW
# Gets the requested values of the height and widht.
windowWidth = root.winfo_reqwidth()
windowHeight = root.winfo_reqheight()
# Gets both half the screen width/height and window width/height
positionRight = int(root.winfo_screenwidth()/2 - windowWidth/2)
positionDown = int(root.winfo_screenheight()/2 - windowHeight/2)
# Positions the window in the center of the page.
root.geometry("+{}+{}".format(positionRight, positionDown))
def new_task():
def add_task():
global task
global impact_window
task = entry_task.get() # we get the task from entry_task and we get the input from the entry_task type-field with .get()
if task != '': # If textbox inputfield is NOT empty do this:
listbox_tasks.insert(tkinter.END, task)
entry_task.delete(0, tkinter.END) #
task_window.destroy()
else:
tkinter.messagebox.showwarning(title='Whoops', message='You must enter a task')
task_window.destroy()
task_window = tk.Toplevel(root)
task_window.title('Add a new task')
task_label = tk.Label(task_window, text = 'Title your task concisely:', justify='center')
task_label.pack()
# Entry for tasks in new window
entry_task = tk.Entry(task_window, width=50, justify='center')
entry_task.pack()
# Add task button in new window
button_add_task = tk.Button(task_window, text='Add task', width=42, command=lambda: [add_task(), impact()])
button_add_task.pack()
def impact():
global impact_next_button
global impact_window
global impact_label
global impact_drop
global options
global select
impact_window = Toplevel(root)
impact_window.title('Impact')
options = StringVar()
options.trace_add
task_label = tk.Label(impact_window, text=str(task), font='bold')
task_label.pack()
impact_label = tk.Label(impact_window, text = 'Specify below the impact of completing this task \n (10 being the highest possible impact)', justify='center')
impact_label.pack()
impact_drop = OptionMenu(impact_window, options, '0','1','2','3','4','5','6','7','8','9','10')
impact_drop.pack()
impact_next_button = tk.Button(impact_window, text='Next', command=connectivity)
impact_next_button.pack(side=tkinter.RIGHT)
def select():
global impact_next_button
global impact_window
global impact_label
global impact_drop
global options
impact_number.append(str(task) + options.trace_add('write', lambda *args: print(str(task)+' '+ 'impact' + ' ' + options.get())))
options.set(options.get())
def connectivity():
impact_window.destroy()
clicked = StringVar()
clicked.set('Select')
global connectivity_next_button
global connectivity_window
global connectivity_label
global connectivity_drop
connectivity_window = Toplevel(root)
connectivity_window.title('Connectivity')
task_label = tk.Label(connectivity_window, text=str(task), font='bold')
task_label.pack()
connectivity_label = tk.Label(connectivity_window, text = 'Specify below the connectivity this task has to other tasks\n (e.g. tasks you can not complete unless you have completed this task)', justify='center')
connectivity_label.pack()
# var1 = IntVar()
Checkbutton(connectivity_window, text="Each task from list must be 'checkable' in this window").pack() # variable=var1).pack()
connectivity_next_button = tk.Button(connectivity_window, text='Next', command=lambda: [Proximity(), select()])
connectivity_next_button.pack(side=tkinter.RIGHT)
def Proximity():
connectivity_window.destroy()
global proximity_button
global proximity_window
global proximity_label
global proximity_drop
global cal
global proximity_output_date
proximity_window = Toplevel(root)
proximity_window.title('Proxmity')
task_label = tk.Label(proximity_window, text=str(task), font='bold')
task_label.pack()
proximity_label = tk.Label(proximity_window, text = 'Specify a deadline for when this task must be completed', justify='center')
proximity_label.pack()
cal = Calendar(proximity_window, selectmode='day', year=2021, month=4, day=27)
cal.pack()
def get_date():
proximity_output_date.config(text=cal.get_date())
proximity_date_button = tk.Button(proximity_window, text='Pick date', command=get_date)
proximity_date_button.pack()
proximity_output_date = tk.Label(proximity_window, text='')
proximity_output_date.pack()
proximity_button = tk.Button(proximity_window, text='Next', command=manageability)
proximity_button.pack(side=tkinter.RIGHT)
def manageability():
print('Deadline:'+' '+cal.get_date())
proximity_window.destroy()
clicked = StringVar()
clicked.set('Select')
global manageability_next_button
global manageability_window
global manageability_label
global manageability_drop
manageability_window = Toplevel(root)
manageability_window.title('Manageability')
task_label = tk.Label(manageability_window, text=str(task), font='bold')
task_label.pack()
manageability_label = tk.Label(manageability_window, text = 'Specify how difficult this task is to complete \n (0 being extremely difficult and 10 being extremely easy)', justify='center')
manageability_label.pack()
manageability_drop = OptionMenu(manageability_window, clicked, '0','1','2','3','4','5','6','7','8','9','10')
manageability_drop.pack()
manageability_next_button = tk.Button(manageability_window, text='Next', command=urgency)
manageability_next_button.pack(side=tkinter.RIGHT)
def urgency():
pass
def delete_task():
try:
task_index = listbox_tasks.curselection()[0]
listbox_tasks.delete(task_index)
except:
tkinter.messagebox.showwarning(title='Oops', message='You must select a task to delete')
def save_tasks():
tasks = listbox_tasks.get(0, listbox_tasks.size())
pickle.dump(tasks, open('tasks.dat', 'wb'))
# Create UI
your_tasks_label = tk.Label(root, text='THESE ARE YOUR TASKS:', font=('Roboto',10, 'bold'), justify='center')
your_tasks_label.pack()
frame_tasks = tkinter.Frame(root)
frame_tasks.pack()
scrollbar_tasks = tkinter.Scrollbar(frame_tasks)
scrollbar_tasks.pack(side=tkinter.RIGHT, fill=tkinter.Y)
listbox_tasks = tkinter.Listbox(frame_tasks, height=10, width=50, font=('Roboto',10), justify='center') # tkinter.Listbox(where it should go, height=x, width=xx)
listbox_tasks.pack()
listbox_tasks.config(yscrollcommand=scrollbar_tasks.set)
scrollbar_tasks.config(command=listbox_tasks.yview)
try:
tasks = pickle.load(open('tasks.dat', 'rb'))
listbox_tasks.delete(0, tkinter.END)
for task in tasks:
listbox_tasks.insert(tkinter.END, task)
except:
tkinter.messagebox.showwarning(title='Phew', message='You have no tasks')
# Add task button
button_new_task = tkinter.Button(root, text='New task', width=42, command=new_task)
button_new_task.pack()
button_delete_task = tkinter.Button(root, text='Delete task', width=42, command=delete_task)
button_delete_task.pack()
button_save_tasks = tkinter.Button(root, text='Save tasks', width=42, command=save_tasks)
button_save_tasks.pack()
root.mainloop() # Runs the program - must be at the very buttom of the code
Some people on here have already helped me out a tremendous amount which I truly appreciate.
So I made some sample code:
from tkinter import Tk, Frame, Button, Entry, Label, Canvas, Scrollbar, OptionMenu, Toplevel, StringVar
import pickle
class Task:
def __init__(self, name, type_, importance):
self.name = name
self.type = type_
self.importance = importance
try:
with open('saved_tasks.dat', 'rb') as file:
task_list = pickle.load(file)
except FileNotFoundError and EOFError:
with open('saved_tasks.dat', 'w') as file:
pass
print('Could not locate the file or the file was empty. A new file was created.')
task_list = []
task_types = ['Misc', 'Math', 'Science', 'Economics', 'Biology', 'Homework']
def show_tasks():
for widget in task_frame.winfo_children():
widget.destroy()
for task in task_list:
Label(task_frame, text=f'{task.name} | Type: {task.type} | Importance: {task.importance}').pack(fill='x')
def open_add_task():
win = Toplevel(root)
win.focus_force()
Label(win, text='Write task name').grid(column=0, row=0)
Label(win, text='Choose type of task').grid(column=1, row=0)
Label(win, text='Choose importance of task').grid(column=2, row=0)
entry = Entry(win)
entry.grid(column=0, row=1, sticky='ew')
type_var = StringVar(value=task_types[0])
OptionMenu(win, type_var, *task_types).grid(column=1, row=1, sticky='nsew')
imp_var = StringVar(value=1)
OptionMenu(win, imp_var, *range(1, 10+1)).grid(column=2, row=1, sticky='nsew')
def add_task():
task_list.append(Task(entry.get(), type_var.get(), imp_var.get()))
show_tasks()
Button(win, text='Add Task', command=add_task).grid(column=0, row=2, columnspan=3, sticky='nsew')
def sort_tasks():
type_ = sort_type.get()
importance = sort_imp.get()
order = asc_desc_var.get()
for widget in task_frame.winfo_children():
widget.destroy()
for task in task_list if order == 'Ascending' else task_list[::-1]:
if type_ == 'All' and importance == 'Any':
Label(task_frame, text=f'{task.name} | Type: {task.type} | Importance: {task.importance}').pack(fill='x')
elif type_ == 'All':
if importance == task.importance:
Label(task_frame, text=f'{task.name} | Type: {task.type} | Importance: {task.importance}').pack(fill='x')
elif type_ != 'All':
if type_ == task.type and importance == 'Any':
Label(task_frame, text=f'{task.name} | Type: {task.type} | Importance: {task.importance}').pack(fill='x')
elif type_ == task.type and importance == task.importance:
Label(task_frame, text=f'{task.name} | Type: {task.type} | Importance: {task.importance}').pack(fill='x')
root = Tk()
btn_frame = Frame(root)
btn_frame.pack(fill='x')
sort_type = StringVar(value='All')
OptionMenu(btn_frame, sort_type, 'All', *task_types).grid(column=0, row=0, sticky='nsew')
sort_imp = StringVar(value='Any')
OptionMenu(btn_frame, sort_imp,'Any', *range(1, 10+1)).grid(column=1, row=0, sticky='nsew')
asc_desc_var = StringVar(value='Ascending')
OptionMenu(btn_frame, asc_desc_var, 'Ascending', 'Descending').grid(column=2, row=0, sticky='nsew')
Button(btn_frame, text='Sort', command=sort_tasks).grid(column=0, row=1, columnspan=3, sticky='nsew')
Button(btn_frame, text='Add New', command=open_add_task).grid(column=3, row=0, rowspan=2, sticky='nsew')
task_frame_main = Frame(root)
task_frame_main.pack()
task_frame = Frame()
canvas = Canvas(task_frame_main)
canvas.pack(side='left', expand=True, fill='both')
canvas.create_window((0, 0), window=task_frame, tag='task_frame', anchor='nw')
scrollbar = Scrollbar(task_frame_main, orient='vertical', command=canvas.yview)
scrollbar.pack(side='right', fill='y')
canvas.config(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.config(scrollregion=canvas.bbox('task_frame')))
show_tasks()
root.mainloop()
try:
with open('saved_tasks.dat', 'wb') as file:
pickle.dump(task_list, file)
print('File saved.')
except Exception as e:
print(f'Exception was raised:\n{e}')
The main part here is the Task class, as You can see it is pretty small and that was the whole point, it doesn't have to be anything large. And the task_list can be easily serialized using pickle and easily loadable and it will keep those classes with their given attributes.
If You have any other questions, ask.
Explaining everything (or most at least):
First import everything You need and I like to do it like this when working with tkinter.
Then I defined task_list which will be used to store added tasks, and this can and probably should be replace by a file so You would read a file and then append the saved tasks to this list or just use json or pickle to import the file as a list which makes it easier. Anyways, then I just created a list to store all the types of tasks, it can be modified and again this is not as necessary but the list can be loaded from a file for example if there is an option to add another type (which requires quite some coding) it would be great to save them to a file so the info is saved.
Now about class Task:
this class doesn't have a lot of attributes, in fact just three to store basic stuff like name of the task, type of the task and how important it is.
There is not much to this but to mention it is possible to use for example pickle module and apply it to the task_list (which will store instances of Task) and it will serialize this object while keeping its attributes so when loading from pickle it will still be possible to access the object the same way.
Now moving on to the show_tasks() function.
So this is a general function for just showing everything that is appended to the task_list a.k.a. all tasks. This function puts Labels on the frame specified below but first it deletes everything off that frame because otherwise it will append all the items each time which means that it will grow exponentaly (You can test if You want by removing the first loop and adding a few tasks).
Next is open_add_task() function which is just for adding tasks.
It starts by creating another window Toplevel and puts focus on it. (sometimes I like to bind such windows so that when user clicks out of the window and this window loses focus close it so that there are not multiple such windows laying around but that is up to You).
Then I created a few labels that help users indicate what is asked from them.
Then I placed entry so that user can type in the task name and then so to avoid user typing error I gave them a choice to choose which category and importance they want so that they don't mistype something.
Then I defined a function (add_task()) for collecting data from user inputs, creating the class instance and appending it to the task_list. And it finishes with creating a Button that calls this function.
Onto sort_tasks.
This function is linked with widgets on root so there is the reference. so first get what the user has inputed and store in variables for easier reuse. Again user input is made easier by just giving choices. Again all widgets are cleared and then comes the sorting/logical part which probably can be improved and I didn't add the option to sort by importance number (so how high is it) so it sorts by time added (again there is no exact time but just order in the list is the time added). The logic could be improved but it mostly does what it is supposed to do (it was late and I couldn't properly think) but yeah there is not much to explain just step through it yourself.
Next bit is the main window which is not inside any functions as to not use global. first I initiated Tk() which is the basic and at the end there is .mainloop() used - the basic stuff. Then there is a lot of frames used to help organize the widgets and then all the widgets are added in this case I'm talking about the widgets that contribute to sort_tasks() function. So added all the choice menus and the button to execute sort_tasks. And a button to call open_add_task().
Next bit is the sort of main frame where all the tasks are shown. There is canvas which is important, because a Frame cannot be scrolled itself, whereas canvas can be scrolled so essentially what happens, is that a frame gets added to the canvas and the scrollregion is set to the Frame size. Then added the scrollbar (will add link to this).
And that is pretty much it.
EDIT:
Added functionality for saving and reading file so that tasks can be saved and loaded. For some reason the file has to be loaded after Task because apparently otherwise it throws an error which I don't completely understand but I dealt with it by moving class above the whole thing. I also found an issue where when new tasks are added and they exceed the visible frame limit, scrollbar does not work, but then again opening the file again it works fine, so that could be dealt with. Also as You can see I added save function after mainloop() so that whenever user closes the window it saves tasks to file. (added to sources)
Sources:
General tkinter info about widgets (I mostly use this)
Very good documentation of tkinter
Info about OptionMenu (1st source does not have this and 2nd source is a bit limited)
"Scrolling Frame" (there are other sources too)
Pickle module (as it is noticeable it is not entirely safe but...)

Missing Argument / issues opening GUI program on macOS Big Sur

This is my first GUI program and I am having some major issues. I really need some help. First, I cannot get the program to open on my computer (mac). When running in Idle IDE I get this error message: import Tkinter
ModuleNotFoundError: No module named 'Tkinter'.
I have 3.9 installed which I thought had a GUI interface.
When debugging in VS Code i get this error message # line 44:
Exception has occurred: TypeError
init() takes at least 4 arguments (3 given)
I think I have 4
I'm not sure where to begin with these issues. From my research it appears that there is an issue running GUI programs on macs updated higher then 11.1.
Code is below
# Create a Tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# Tkinter imported
import Tkinter
# Global variable used
temp_val = 'Celsius'
#Set value for drop down menu
def store_temp (set_temp):
global temp_val
temp_Val = set_temp
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = Tkinter.Tk()
# create a title for window
self.main_window.title('Temperature Converter')
# create three frames
self.top_frame = Tkinter.Frame()
self.option_frame = Tkinter.Frame()
self.mid_frame = Tkinter.Frame()
self.bottom_frame = Tkinter.Frame()
#create widget for top frame
self.prompt_label = Tkinter.Label(self.top_frame, text= 'Enter a temperature in Celsius: ')
#pack top frame
self.prompt_label.pack(side='left')
# create str variable obj to hold empty string variable
self.inputNumber = Tkinter.StringVar()
self.var = Tkinter.StringVar()
# create widget for option drop down menu
self.entry = Tkinter.Entry(self.option_frame, textvariable=self.inputNumber )
self.dropDownList = ['Celsius','Fahrenheit']
self.drop_down = Tkinter.OptionMenu(self.option_frame, value=self.var , values=self.dropDownList, command=store_temp)
self.var.set(dropDownList[0])
# option widgets packed
self.entry.pack(side='right')
self.dropDownList.pack(side='left')
#create widget for middle frame
self.result_label = Tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.call_convert = (call_convert , result_label, inputNumber)
self.convert_button = Tkinter.Button(self.bottom_frame, text='Convert', command=self.call_convert)
self.quit_button= Tkinter.Button(self.bottom_frame, text= 'Quit', command= self.main_window.destroy)
#pack the buttons
self.convert_button.pack(side='left')
self.quit_button.pack(side='left')
#pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
#Enter the tkinter main loop
Tkinter.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if temp_Val == 'Celsius':
f = float((float(temp)* 9/5)+32)
self.result_label.config(text='The temperature in Fahrenhiet is:')
if temp_Val == 'Fahrenheit':
c = float((float(temp)-32) * 5 / 9)
self.result_label.config(text='The temperature in Celsius is:')
if __name__ == '__main__':
temp_converter = TemperatureConverter()
There were a lot of bugs in your code. I fixed all of them (I think). I had to guess where you wanted to put the label with the results. I also had to fix all of the indentations. This is the working code:
# Create a tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# tkinter imported
import tkinter
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = tkinter.Tk()
# create a title for window
self.main_window.title("Temperature Converter")
# create three frames
self.top_frame = tkinter.Frame(self.main_window)
self.option_frame = tkinter.Frame(self.main_window)
self.mid_frame = tkinter.Frame(self.main_window)
self.bottom_frame = tkinter.Frame(self.main_window)
# create widget for top frame
self.prompt_label = tkinter.Label(self.top_frame, text="Enter a temperature in Celsius:")
# pack top frame
self.prompt_label.pack(side="left")
# create str variable obj to hold empty string variable
self.inputNumber = tkinter.StringVar(self.main_window)
self.var = tkinter.StringVar()
# create widget for option drop down menu
self.entry = tkinter.Entry(self.option_frame, textvariable=self.inputNumber)
self.dropDownList = ["Celsius", "Fahrenheit"]
self.drop_down = tkinter.OptionMenu(self.option_frame, self.var, *self.dropDownList)
self.var.set(self.dropDownList[0])
# option widgets packed
self.entry.pack(side="right")
self.drop_down.pack(side="left")
# create widget for middle frame
self.result_label = tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.convert_button = tkinter.Button(self.bottom_frame, text="Convert", command=self.call_convert)
self.quit_button= tkinter.Button(self.bottom_frame, text= "Quit", command=self.main_window.destroy)
# pack the buttons
self.convert_button.pack(side="left")
self.quit_button.pack(side="left")
# pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
# It is better to call `<tkinter.Tk>.mainloop()`
self.main_window.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if self.var.get() == "Celsius":
f = float((float(self.entry.get())* 9/5)+32)
self.result_label.config(text="The temperature in Fahrenhiet is: "+str(f))
if self.var.get() == "Fahrenheit":
c = float((float(self.entry.get())-32) * 5 / 9)
self.result_label.config(text="The temperature in Celsius is: "+str(c))
self.result_label.pack(side="bottom")
if __name__ == "__main__":
temp_converter = TemperatureConverter()
Look at what I did for the OptionMenu and look at how I fixed you call_convert function. If you have any specific questions, tell me and I will try to answer them.
By the way I don't think any of the errors you were getting were caoused by your OS. Also I suggest that next time you use import tkinter as tk as it will make it way easier to write code.

Returning variable from a function

I have a main function page and i linked the next page with a button
when i click the button it executes a function in the main page but.
i want to use the result from the function in another file as a variable
########main.py
class Main(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master)
mainFrame.pack()
topFrame= Frame(mainFrame,width=1050,height =50, bg="#f8f8f8",padx =20, relief =SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP,fill = X)
self.btnselfolder= Button(topFrame, text="Select Folder", compound=LEFT,
font="arial 12 bold", command=self.selectFolder)
self.btnselfolder.pack(side=LEFT)
def selectFolder(self):
print("folder")
return folder
################# selectfolder page
class Page2(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry("450x300")
self.title("page2")
self.resizable(False,False)
self.topFrame = Frame(self, width=350,height=150, bg="white")
self.topFrame.pack(fill=X)
# call the function from main.py and it will give me the same output folder
y = selectFolder()
Since the selectFolder method is not static, you'll have to access it using instance. Like this:
main = Main()
folder = main.selectFolder()
folder should hold the value you returned.
I'm afraid when i just copied and pasted your code into my IDLE directly, it immediately presented me with naming errors. Not even a Tkinter window popped up. So to clear things up I would go about making a Tkinter window like the following. Also bare in mind I don't really use or know how to integrate classes with Tkinter itself, but I quickly learned how to by your mistakes :)
# import tkinter from the libraries
from tkinter import *
import os
# the Zen of Python
#import this
# declare the tkinter window variable
# scroll to bottom to see mainloop
root = Tk()
########main.py
# folder is not defined as a variable
# but i recommend looking into
# using system of which i imported
# for you. You can research how to
# use it yourself for good practice
# i am sorry if i am wrong about this
# but my impression is you're trying to
# use the OS to select a folder from a
# directory. So the 'selectFolder' function
# should not be declared as a method within the class
# therefore rendering the Page2 class useless.
def selectFolder():
print("folder")
# return will result in an error
# because it has not been declared as a variable
# return folder
class Main():
# all instances instantiated (created)
# within the __init__ method
# must be declared in the parentheses
def __init__(self, topFrame, btnselfolder):
# instantiate the topFrame object
self.topFrame = topFrame
# instantiate the btnselfolder
self.btnselfolder = btnselfolder
# pro tip - when having multiple
# arguments within an object, then
# to keep it clean and easy to read
# space out the code like i have for
# you. You should also read the "Zen of Python"
# which is at the top as 'import this'
# however i have commented it out for you
topFrame = Frame(root,
width=1050,
height = 50,
bg = "#f8f8f8",
padx = 20,
relief = SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP, fill = X)
btnselfolder = Button(topFrame,
text = "Select Folder",
compound=LEFT,
font = "arial 12 bold",
command = selectFolder).pack()
# mainloop() method will keep the window open
# so that it doesn't appear for a millisecond
# and dissapear :)
root.mainloop()
# I hope this has been a big help
# Thanks for posting this question! :-)
# Enjoy your day, good luck and be safe in Lockdown!
Thanks again for the question, I thoroughly enjoyed solving it or at least giving you some guidence! :)

Bug appears when writing file from tkinter module

import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.create_widgets()
def create_widgets(self):
self.hi_there = tk.Button(self)
self.hi_there["text"] = "Done!\n(click me)"
self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
self.entrythingy = tk.Entry()
self.entrythingy2 = tk.Entry()
self.entrythingy.pack()
self.entrythingy2.pack()
# here is the application variable
self.contents = tk.StringVar()
self.contents2 = tk.StringVar()
# set it to some value
self.contents.set("stdio")
self.contents2.set("script name")
# tell the entry widget to watch this variable
self.entrythingy["textvariable"] = self.contents
self.entrythingy2["textvariable"] = self.contents2
self.text = tk.Text()
self.text.pack()
# and here we get a callback when the user hits return.
# we will have the program print out the value of the
# application variable when the user hits return
self.entrythingy.bind('<Key-Return>',
self.print_contents)
self.quit = tk.Button(self, text="QUIT", fg="red",
command=root.destroy)
self.quit.pack(side="bottom")
def say_hi(self):
#print("hi there, everyone!")
self.fn = self.contents2.get()
self.body = self.text.get(1.0, tk.END).split('\n')
#print('Self.body:\n',self.body)
self.libs = self.contents.get().split(' ')
self.make_c()
def make_c(self):
lib_text = ''
for i in self.libs:
lib_text += "#include <lib.h>\n".replace('lib', i)
body_text = "int main() {\n\t"+"\n\t".join(self.body)+"return 0\n}"
print(lib_text+body_text)
with open(self.fn+'.c', 'w+') as f:
f.write(lib_text+body_text)
print('File written!')
from subprocess import call
call(['gcc',self.fn+'.c', '-o', self.fn])
def print_contents(self, event):
print("hi. contents of entry is now ---->",
self.contents.get())
#self.contents.set("")
#def
root = tk.Tk()
app = Application(master=root)
app.mainloop()
Those are the my code, which tries to make a c file and convert it. The problem is, when I convert it once, it is working fine, but when I change the content of the text box, the file doesn't change, and I don't understand why. I am sure that I put in the new file content, because it prints before it writes. Also, it appears that when I try to write files independent from tkinter, it works just the way I want it to.
I think there is some mechanism that I am not aware of in TK, or there is a bug. Please help me out, thanks.
I solved it. It doesn't compile again due to the error in it when I added return 0, without semicolon. So, when I click the executable file, it shows the old program. I added the semicolon, and now it is fine. Thx everyone!

tkinter listbox insert error "invalid command name ".50054760.50055432"

I have a database of objects and you can view the items in the database in a listbox and there's a button to remove an item and to create an item. Creating an item opens a dialog window for the item class and then the item's data is stored in the database. I have reproduced the problem with a very simple duplicate of my set-up (see code below).
Every time I add a new item, the addition is successful (it's there the next time I open up the database dialog), but the listbox doesn't insert the item, and when I close the database dialog I get the following error:
Exception in Tkinter callback Traceback (most recent call last):
File "C:\Python33\lib\tkinter__init__.py", line 1442, in call
return self.func(*args) File "", line 21, in addRecord File "C:\Python33\lib\tkinter__init__.py", line 2604, in insert
self.tk.call((self._w, 'insert', index) + elements)
_tkinter.TclError: invalid command name ".50054760.50055432"
The same problem doesn't come up if I just try to create the object and populate its values without invoking its inputs GUI (which is necessary for the process of inserting things into my database). I've seen a similar error in another thread (sorry, but I can't seem to find it again), where the problem was with multithreading. I'm not aware of any threading that I'm doing and don't want to download yet another package to handle tkinter threading. Any ideas? Workarounds? I'm using Python v3.3 and 64-bit Windows 7, if that helps.
Here's my simplified database code:
import tkinter
import traceback
# Test =========================================================================
class Test:
def __init__(self):
"""A database of objects' IDs and values."""
self.data = {1: 'a', 2: 'b', 3: 'c'}
#---------------------------------------------------------------------------
def addRecord(self):
"""Opens up a new item for editing and saves that ability to the
database."""
print('hi0')
newItem = OtherObject()
newItem.create(self.root)
print('hi1')
self.data[newItem.ID] = newItem.value
print('hi2')
self.listbox.insert(tkinter.END, self.formatItem(newItem.ID))
print('hi3')
#---------------------------------------------------------------------------
def delRecord(self):
"""Removes selected item from the database."""
try:
index = self.listbox.curselection()[0]
selection = self.listbox.get(index)
except:
return
ID = int(selection.split(':')[0])
self.data.pop(ID)
self.listbox.delete(index)
#---------------------------------------------------------------------------
def dataframe(self, master):
"""Assembles a tkinter frame with a scrollbar to view database objects.
(Returns Frame, Scrollbar widget, Listbox widget)
master: (Tk or Toplevel) tkinter master widget."""
frame = tkinter.Frame(master)
# scrollbar
scrollbar = tkinter.Scrollbar(frame)
scrollbar.pack(side=tkinter.LEFT, fill=tkinter.Y)
# listbox
listbox = tkinter.Listbox(frame, yscrollcommand=scrollbar.set)
listbox.pack(side=tkinter.LEFT, fill=tkinter.BOTH)
# fill listbox
for ID in self.data:
listbox.insert(tkinter.END, self.formatItem(ID))
return (frame, listbox, scrollbar)
#---------------------------------------------------------------------------
def destroyLB(self, e):
for line in traceback.format_stack():
print(line.strip())
#---------------------------------------------------------------------------
def formatItem(self, ID):
"""Creates a nice string representation of an item in the database."""
return '{0}:{1}'.format(ID, self.data[ID])
#---------------------------------------------------------------------------
def listboxSelect(self, e):
"""Manages events when the selection changes in the database interface.
e: (Event) tkinter event."""
try:
selection = self.listbox.get(self.listbox.curselection()[0])
except:
return
# set description label
ID = int(selection.split(':')[0])
self.lblstr.set(self.data[ID])
#---------------------------------------------------------------------------
def view(self):
"""Displays database interface."""
self.root = tkinter.Tk()
# listbox frame
self.frame, self.listbox, self.scrollbar = self.dataframe(self.root)
self.frame.grid(column=0, row=0)
self.listbox.bind('<<ListboxSelect>>', self.listboxSelect)
self.listbox.bind('<Destroy>', self.destroyLB)
# record display frame
self.lblstr = tkinter.StringVar()
self.lbl = tkinter.Label(self.root, textvariable=self.lblstr)
self.lbl.grid(column=1, row=0, sticky=tkinter.N)
# buttons frame
self.frame_btn = tkinter.Frame(self.root)
self.frame_btn.grid(row=1, columnspan=2, sticky=tkinter.E+tkinter.W)
# 'create new' button
self.btn_new = tkinter.Button(
self.frame_btn, text='+', command=self.addRecord)
self.btn_new.grid(row=0, column=0)
# 'delete record' button
self.btn_del = tkinter.Button(
self.frame_btn, text='-', command=self.delRecord)
self.btn_del.grid(row=0, column=1)
# display
self.root.mainloop()
# Test =========================================================================
# OtherObject ==================================================================
class OtherObject:
"""An object with an ID and value."""
def __init__ (self):
self.ID = 0
self.value = ''
#---------------------------------------------------------------------------
def create(self, master=None):
"""open a dialog for the user to entry a new object ID and value.
master: (Tk or Toplevel) tkinter master widget."""
self.stuff = tkinter.Toplevel(master)
# ID
tkinter.Label(self.stuff, text='ID: ').grid(row=0, column=0)
self.IDvar = tkinter.StringVar(self.stuff)
self.IDvar.set(self.ID)
IDwidget = tkinter.Entry(self.stuff, textvariable=self.IDvar)
IDwidget.grid(row=0, column=1)
# value
tkinter.Label(self.stuff, text='Value: ').grid(row=1, column=0)
self.valueVar = tkinter.StringVar(self.stuff)
self.valueVar.set(self.value)
valueWidget = tkinter.Entry(self.stuff, textvariable=self.valueVar)
valueWidget.grid(row=1, column=1)
# OK button
tkinter.Button(self.stuff, text='OK', command=self.OK).grid(row=2)
self.stuff.mainloop()
#---------------------------------------------------------------------------
def OK(self):
try: self.ID = int(self.IDvar.get())
except: self.ID = 0
self.value = self.valueVar.get()
self.stuff.destroy()
# OtherObject ==================================================================
Thanks in advance
You are creating more than one instance of Tk. Tkinter is not designed to work like that and you will get unexpected behavior. You need to refactor your code so that you create an instance of Tk only once. If you need multiple windows, create instances of Toplevel.
... time passes ... the code in the question gets updated ...
In the updated version of your question you now are creating one instance of Tk, and then instances of Toplevel. This is good. However, you are also calling mainloop more than once which is a problem. Worse, you're redefining self.root which no doubt is part of the problem. You must call mainloop exactly once over the entirety of your program.
Bryan Oakley guided me to the exact problem that I was encountering:
(1) I created a second Tk object in my OtherObject class.
(2) I called mainloop in my OtherObject class.
Only one Tk object should exist and mainloop should only ever be called once, no matter how many windows are to be displayed.

Resources