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! :)
Related
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...)
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.
1) What is my goal:
I’m creating an application that should read data every 60s from ModBusServer, append those data to Graphs and then when the app is closed save the data to excel file.
Site note:
The process of reading data from ModBusServer and appending them to graphs should start after a start button is pressed.
And end after stop button is pressed OR when ModBusServer sends a request to stop.
2) What I have so far:
I created the GUI without any major problems as a class “GUI_komora”.
Everything there works just fine.
3) What is the problem:
But now I’m lost on how to approach the “read data every 60 seconds”, and overall how to control the application.
I did some research on threading but still I’m confused how to implement this to my application.
I learned how to make functions run simultaneously in this tutorial.
And also how to call a function every few seconds using this question.
But none of them helped me to learn how to control the overall flow of the application.
If you could redirect me somewhere or tell me about a better approach I would be really glad.
Some of my code:
from tkinter import *
from GUI_komora import GUI
root = Tk()
my_gui = GUI(root) #my GUI class instance
#main loop
root.mainloop()
"""
How do I achieve something like this???
whenToEnd = False
while whenToEnd:
if step == "Inicialzation":
#inicializace the app
if step == "ReadData":
#read data every 60 seconds and append them to graphs
if step == "EndApp"
#save data to excel file and exit app
whenToEnd = True
"""
Here is an example of a loop that takes a decision (every 60 sec in your case) and pushes the outcome of the decision to tkinter GUI: https://github.com/shorisrip/PixelescoPy/blob/master/base.py
Parts:
main thread - starts tkinter window
control thread - reads some data and decides what to show in GUI
GUI class - has a method "add_image" which takes input an image and displays on GUI.(add_data_to_graph maybe in your case). This method is called everytime by the control thread.
Snippets:
def worker(guiObj, thread_dict):
# read some data and make decision here
time.sleep(60)
data_to_show = <outcome of the decision>
while some_logic:
pictureObj = Picture(chosen, timer)
pictureObj.set_control_thread_obj(thread_dict["control_thread"])
guiObj.set_picture_obj(pictureObj)
pictureObj.display(guiObj)
# do post display tasks
guiObj.quit_window()
# Keep GUI on main thread and everything else on thread
guiObj = GuiWindow()
thread_list = []
thread_dict = {}
thread_for_image_control = threading.Thread(target=worker, args=(guiObj,
thread_dict))
thread_dict["control_thread"] = thread_for_image_control
thread_list.append(thread_for_image_control)
thread_for_image_control.start()
guiObj.run_window_on_loop()
# thread_for_image_control.join()
Code for Picture class:
class Picture:
def __init__(self, path, timer):
self.path = path
self.timer = timer
self.control_thread_obj = None
def set_control_thread_obj(self, thread_obj):
self.control_thread_obj = thread_obj
def display(self, guiObj):
image_path = self.path
guiObj.add_image(image_path)
time.sleep(self.timer)
Code for GUI class
class GuiWindow():
def __init__(self):
self.picture_obj = None
self.root = Tk()
self.image_label = None
self.image = None
self.folder_path = None
self.timer = None
self.root.protocol("WM_DELETE_WINDOW", self.exit_button)
def add_image(self, image_path):
resized_img = self.resize(image_path)
image_obj = ImageTk.PhotoImage(resized_img)
image_label = Label(self.root, image=image_obj,
height=resized_img.height,
width=resized_img.width)
self.image = image_obj # DO NOT REMOVE - Garbage collector error
if self.image_label is not None:
self.remove_image()
image_label.grid(row=0, column=0, columnspan=3)
self.image_label = image_label
Here based on my control loop thread I am changing image (in your case graph data) of the tkinter GUI.
Does this help?
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!
I’m very new to python. I’m working on a ‘proof of concept’ piece of code; using PiCamera on a Raspberry Pi running Jessie.
I’ve based my code on a tutorial code from: https://pythonprogramming.net/tkinter-adding-text-images/
Once you hit the button to show the image, the code starts PiCamera and starts to get capture_continuous, passes it to a stream, applies crosshairs to it.
It works mostly well… but after a bit over two minutes, the disk drive lights up and it starts to slow drastically. Once I get the program to break, everything is fine. I’ve looked at a couple of logs, but I can’t for the life of me find out what cache is overflowing or why. I’ve tried a bunch of different ways and tried to leave those in as comments. I suspected it had something to do with having to clear the image in tkinter, but even that doesn’t seem to work and makes the video flash unevenly.
Any help would be great! I’ve started to explore using opencv instead. Still installing that.
Thanks!
The code:
# Simple enough, just import everything from tkinter.
from tkinter import *
import picamera
import picamera.array
import time
import threading
import io
import numpy as np
from PIL import Image, ImageTk
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Create an array representing a 1280x720 image of
# a cross through the center of the display. The shape of
# the array must be of the form (height, width, color)
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
self.init_window()
#Creation of init_window
def init_window(self):
# changing the title of our master widget
self.master.title("GUI")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a menu instance
menu = Menu(self.master)
self.master.config(menu=menu)
# create the file object)
file = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
file.add_command(label="Exit", command=self.client_exit)
#added "file" to our menu
menu.add_cascade(label="File", menu=file)
# create the file object)
edit = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
edit.add_command(label="Show Img", command=self.showImg)
edit.add_command(label="Show Text", command=self.showText)
#added "file" to our menu
menu.add_cascade(label="Edit", menu=edit)
self.trim_running_bool = False
def showImg(self):
self.trim_running_bool = True
trim_thrd_thread = threading.Thread(target=self._cam_thread_def)
trim_thrd_thread.start()
self.update_idletasks()
def _cam_thread_def(self):
img_stream = io.BytesIO()
frame_count = 0
with picamera.PiCamera() as camera:
camera.resolution = (400, 300)
## while True: ### tried it this way too
for xxx in range(0,900):
img_stream = io.BytesIO()
frame_count = frame_count + 1
print(frame_count," ", xxx)
if self.trim_running_bool == False:
print("break")
break
camera.capture(img_stream, 'jpeg', use_video_port=True)
img_stream.seek(0)
img_load = Image.open(img_stream)
for xl_line in range(0,196,4):
img_load.putpixel((xl_line, 149), (xl_line, 0, 0))
xll=xl_line+2
img_load.putpixel((xl_line, 150), (xl_line, xl_line, xl_line))
img_load.putpixel((xl_line, 151), (xl_line, 0, 0))
(xl_line)
for xr_line in range(208,400,4):
clr = 400 - xr_line
img_load.putpixel((xr_line, 149), (clr, 0, 0))
img_load.putpixel((xr_line, 150), (clr, clr, clr))
img_load.putpixel((xr_line, 151), (clr, 0, 0))
(xr_line)
for yt_line in range(0,146,4):
clrt = int(yt_line * 1.7)
img_load.putpixel((199, yt_line), (clrt, 0, 0))
img_load.putpixel((200, yt_line), (clrt, clrt, clrt))
img_load.putpixel((201, yt_line), (clrt, 0, 0))
(yt_line)
for yb_line in range(158,300,4):
clrb = int((300 - yb_line) * 1.7)
img_load.putpixel((199, yb_line), (clrb, 0, 0))
img_load.putpixel((200, yb_line), (clrb, clrb, clrb))
img_load.putpixel((201, yb_line), (clrb, 0, 0))
(yb_line)
img_render = ImageTk.PhotoImage(img_load)
# labels can be text or images
img = Label(self, image=img_render)
img.image = img_render
img.place(x=0, y=0)
self.update_idletasks()
img_stream.seek(0)
img_stream.truncate(0)
# tried these:
## img_stream.flush()
## print("flushed ", img_stream)
## print("2nd ",img_stream)
## del img_load
##
##
## rawCapture.truncate(0)
##
## rawCapture.seek(0)
## rawCapture.truncate(0)
## del render
## img.image = None
## foregnd_image = None
(xxx)
pass
def showText(self):
text = Label(self, text="Hey there good lookin!")
text.pack()
def client_exit(self):
self.trim_running_bool = False
exit()
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("400x300")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
Each time through your loop you are creating a new image object and a new label, as well as some other objects. That is a memory leak, since you never destroy the old image or old label.
Generally speaking, you should create exactly one label, then use the_label.configure(image=the_image) every time through the loop. With that, you don't need to create new labels or call place on it.
Even better, since a label automatically updates when the associated image changes, you only need to change the the bits that are in the image object itself and the label should update automatically.
The simplest solution is to move image creation to a function so that all of those objects you are creating are local objects that can get automatically garbage collected when the function returns.
The first step is to create a single label and single image in your main thread:
class Window(Frame):
def __init__(self, master=None):
...
self.image = PhotoImage(width=400, height=300)
self.label = Label(self, image=self.image)
...
Next, create a function that copies new data into the image. Unfortunately, tkinter's implementation of the copy method doesn't support the full power of the underlying image object. A workaround is described here: http://tkinter.unpythonic.net/wiki/PhotoImage#Copy_a_SubImage.
Note: the workaround example is a general purpose workaround that uses more arguments than we need. In the following example we can omit many of the arguments. The documentation for the underlying tk photo object copy method is here: http://tcl.tk/man/tcl8.5/TkCmd/photo.htm#M17
The implementation would look something like this (I'm guessing; I don't have a good way to test it):
def new_image(self):
# all your code to create the new image goes here...
...
img_render = ImageTk.PhotoImage(img_load)
# copy the new image bits to the existing image object
self.tk.call(self.image, 'copy', img_render)
Finally, your loop to update the image would be much simpler:
while True:
self.new_image()
# presumeably there's some sort of sleep here so you're
# not updating the image faster than the camera can
# capture it.
I don't know how fast new_image can run. If it can run in 200ms or less you don't even need threads. Instead, you can use after to run that function periodically.
Note: I haven't worked much with Tkinter photo images in a long time, and I have no good way to test this. Use this as a guide, rather than as a definitive solution.