I have a button that can create another buttons, but when I close the program, the buttons I just created disappear.
I know how to save things in a file using pickle, but how should I write the code so that it saves the buttons and creates them again when I open the program.
code:
def CreateButton():
global name,clicked,started
started=True
name = str(input("Write a Student Name..."))
print('variable changed...')
clicked=True
def update():
global MainWindow,changed,clicked,name
#global clicked
if clicked and started:
#name = input("Write a Student Name: ")
button_new=needed_but(MainWindow=MainWindow,color="#E6E6E6",text=name)
clicked=False
buttonred=False
MainWindow.after(1*1000,update)
class needed_but:
def __init__(self,MainWindow,color,text):
console = Button(MainWindow, text=text,bg=color, command=self.changecolor)
console.pack()
self.instance=console
def changecolor(self):
buttonred,buttongreen,buttonblue=get_color()
global clicked,misses_dict
#clicked=True
#global buttoncolor
if buttonred == True:
self.instance.configure(bg = "#ff0000")
dun = self.instance.cget('text')
print(dun)
if dun in misses_dict:
misses_dict[('%s' %(dun))] += 1
else:
misses_dict[('%s' %(dun))] = 1
pickle.dump(dictionary, open("%s\SI\SIA.dat" %(path), 'wb'))
print(misses_dict)
buttonred = False
elif buttongreen == True:
self.instance.configure(bg = "#00ff00")
elif buttonblue == True:
self.instance.configure(bg = "#2e9afe")
how should I write the code so that it saves the buttons and creates them again when I open the program.
You can't. The objects are actually objects within an embedded tcl interpreter. There's no way to save the state of the interpreter.
You'll have to write code to save all of the information about the widgets in some sort of format where you can read it back and recreate the widgets.
Related
I'm currently trying to create program that allows a user to view pictures of a shirt the user selects combined with different pants.
The program contains a directory filled with pictures of shirts, aswell as one of pants.
The user selects a shirt from a DropDown-menu, and the program then generates, saves, and displays the all possible combinations created using the shirt.
To accomplish this I'm using Tkinter & PIL (Pillow).
Here's my problem: When the user edits the DropDown-menu, and selects another shirt, I want the program to generate new images using THAT shirt, and replace the old images currently displayed.
I've read answers to similar questions, and some suggest a setter and getter, to detect and call a function when a variables' value is changed. I'm not quite sure I understand how it works, and definetly not how to implement it into my code. The function I want to call is inside a nested loop. Does that make any difference in this context?
Here is the code. The generateImage()function is a placeholder for the many rows of code that acutally generate the images.
To detect when the selected option from the DropDown-menu is changed, I use variable.trace.
shirtcolors = ["blue", "red", "green"]
def ChosenImage(*args):
chosen_image = variable.get()
print("value is: " + chosen_image)
selectedimage = ""
if chosen_image == "blue":
selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0840.jpg"
elif chosen_image == "red":
selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0850.jpg"
elif chosen_image == "green":
selectedimage = "C:/User/Desktop/OutfitGenerator/shirts/IMG_0860.jpg"
return selectedimage
DropdownList = tk.OptionMenu(frame, variable, *shirtcolors)
DropdownList.pack()
variable.trace("w", callback=ChosenImage)
shirtslist = [ChosenImage()]
pantsdirectory= "C:/User/Desktop/OutfitGenerator/pants"
pantslist = [os.path.abspath(os.path.join(pantsdirecotry, h)) for h in os.listdir(pantsdirecotry)]
for i in shirtslist:
for j in pantslist:
def generateImage(file1, file2):
My problem is that i can't figure out how to make the program run the code below the variable.trace line again. When a callback is sent to ChosenImage I want it to also continue the rest of the code, now using the new selectedimage value. However the continuation of the code cannot happen until the callback has reached the ChosenImage function, and it has changed its value.
My problem is that i can't figure out how to make the program run the
code below the variable.trace line again. When a callback is sent to
ChosenImage I want it to also continue the rest of the code, now using
the new selectedimage value.
This is the wrong way of thinking. Your callback is the thing that should generate the combinations/images - your callback shouldn't return anything, it should only have side-effects:
import tkinter as tk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Window")
self.geometry("256x64")
self.resizable(width=False, height=False)
self.shirt_colors = ["blue", "red", "green"]
self.pants_colors = ["brown", "black"]
self.drop_down_var = tk.StringVar(self)
self.menu = tk.OptionMenu(self, self.drop_down_var, *self.shirt_colors)
self.menu.pack()
self.drop_down_var.trace("w", callback=self.on_change)
def on_change(self, *args):
# Everytime the selection changes, generate combinations
shirt_color = self.drop_down_var.get()
for pant_color in self.pants_colors:
print("{} shirt with {} pants".format(shirt_color, pant_color))
def main():
Application().mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
I have designed an application which have two buttons i.e CAL and SAV.
Accordingly that I have two functions but the issue is sometimes production line Operator by mistake presses SAV button. So that attribute error arises and program stuck.
How to overcome this issue? Please guide me.
Here is my code:
class ADS1x15:
"""Base functionality for ADS1x15 analog to digital converters."""
class ADS1115(ADS1x15):
"""Class for the ADS1115 16 bit ADC."""
class AnalogIn:
"""AnalogIn Mock Implementation for ADC Reads."""
import RPi.GPIO as GPIO
import tkinter as tk
GPIO.setmode(GPIO.BCM)
GPIO.setup(12,GPIO.IN) #Save Button
GPIO.setup(5,GPIO.IN) #Cal Button
root=tk.Tk()
root.geometry("1000x600")
file = open("/home/pi/data_log.txt", "r")
f = file.read().split(',')
rangeh = int(f[3])
offset = int(f[4])
fullScale = int(f[5])
chan=AnalogIn(ads,P0,P1)
def cal(channel):
global Dsel,cal_c,rangeh,offset,fullScale,chan
cal_c = cal_c + 1
if cal_c == 1:
root.L1 = tk.Label(root,text="Put Zero Weight and Press CAL btn",fg="brown",font="bold")
root.L1.pack()
root.L1.place(x=1,y=1)
elif cal_c == 2:
root.L1.destroy()
offset = chan.value
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2 = tk.Label(root,text="Put Full Weight and Press SAV btn",fg="brown",font="bold")
root.L2.pack()
root.L2.place(x=1,y=1)
def sav(channel):
global rangeh,offset,fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh)+","+str(offset)+","+str(fullScale))
file.flush()
root.L2.destroy()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5,GPIO.RISING,callback=cal,bouncetime=1000)
GPIO.add_event_detect(12,GPIO.RISING,callback=sav,bouncetime=1000)
root.after(500,update)
root.mainloop()
This error generated due to root.L2.destroy() this line.
Can I block or disable this sav function, so that without call of cal function, it shouldn't execute?
A brute force solution would be to check whether root has an L2 attribute or not
from tkinter import messagebox
def sav(channel):
if hasattr(root, 'L2'):
global rangeh, offset, fullScale
file = open("/home/pi/data_log.txt", "w")
if os.stat("/home/pi/data_log.txt").st_size == 0:
file.write("rangeh,offset,Full_Scale,\n")
file.write(str(rangeh) + "," + str(offset) + "," + str(fullScale))
file.flush()
root.L2.destroy()
else:
messagebox.showinfo('Unable to save', 'No data was generated yet')
A more elegant approach would be to disable the save button on startup and only enable it after the cal function has been executed.
I am not very familiar with Raspberry Pi implementations, so this is only a rough sketch on how to achieve the button disabling:
By the looks of it, the buttons are "wired in" via the GPIO.add_event_detect functions.
So i would remove the sav-callback from the main script and dynamically add it after the cal script, something like that:
# [...] beginning of your script [...]
def cal(channel):
# [...] original body of cal function [...]
activate_save_button()
def activate_save_button():
GPIO.add_event_detect(12, GPIO.RISING, callback=sav, bouncetime=1000)
def deactivate_save_button():
GPIO.remove_event_detect(12)
def sav(channel):
# [...] original body of sav function [...]
# remove save button functionality after saving
deactivate_save_button()
def update():
""" function for continuous show value in every 500ms in tkinter window"""
GPIO.add_event_detect(5, GPIO.RISING, callback=cal, bouncetime=1000)
# line with callback=sav is deleted here
root.after(500, update)
root.mainloop()
The title is not very easy to understand, I know, so let me explain it here.
Basically if I do
if variable.get() == "Select Website":
print("ok")
it will print out "ok", but if I change it from "Select Website" to "Fareham" as well as the option in the drop down box to "Fareham" it will not notice it changed. If I want it to notice it changed I would need to do a while loop, but that would stop the script running in the first place.
How can I make the script print out "ok" if variable is changed to "Fareham"?
Current Code:
import tkinter
sites = [
"Fareham",
"Hants",
"Southampton",
"Eastleigh",
"Havant",
"Gosport",
]
win = tkinter.Tk()
win.geometry("500x500")
variable = tkinter.StringVar(win)
variable.set("Select Website")
drop = tkinter.OptionMenu(win, variable, *sites)
drop.pack()
if variable.get() == "Fareham":
print("ok")
win.mainloop()
You can do this by associating a callback funtion to the drop down menu:
import tkinter
def your_callback(*args):
if args[0] == "Fareham":
print("ok")
sites = [
"Fareham",
"Hants",
"Southampton",
"Eastleigh",
"Havant",
"Gosport",
]
win = tkinter.Tk()
win.geometry("500x500")
variable = tkinter.StringVar(win)
variable.set("Select Website")
drop = tkinter.OptionMenu(win, variable, *sites, command = your_callback)
drop.pack()
win.mainloop()
Here's where some important info can be found:
http://effbot.org/tkinterbook/variable.htm
By setting an observer to the variable variable, it will check it everytime it changes.
def check(*args):
if variable.get() == 'Fareham':
print 'ok'
variable.trace(
'w', # 'w' checks when a variable is written (aka changed)
check # this is the function it should call when the variable is changed
)
Just put the code in place of your current if statement and it will work like a charm.
I am serializing my GUI to save the information.But the problem is that it is only saving the last value entered not all of them.
So now the problem is that when I click on the + button, the row gets incremented but only the last name entered is saved. I want to save all of them
def increment(self):
current_row=1
MoreButton=Button(self.listFrame,text="+",command=entry_1(self))
MoreButton.grid(column=1,row=0)
def entry_1(self):
self.entryName=Entry(self.listFrame)
self.entryName.grid(column=1,row=current_row,sticky="EW")
self.entryName.get()
nameLabel=Label(self.listFrame,text="NAME")
nameLabel.grid(column=0,row=current_row)
save_button=Button(self.listFrame,text="save",command=save_data(self))
save_button.grid(column=2,row=0)
current_row=current_row+1
def save_data(self):
data={
"Name":self.entryName.get(),
}
with open("test.json","wb") as f:
dill.dump(data,f)
def load_data(self):
with open("test.json","rb") as f:
data=dill.load(f)
Sorry for the past incorrect answer. I think the problem is that your entry_1 function creates entry boxes with the same name. This means when you try getting the text in it, it gets the text of the last one only. Here is the full code I created that creates entry's with different names. It is in a class but I don't know how you want them set out.
from Tkinter import *
import dill
import sys
boxes=[]
no_of_boxes=0
root=Tk()
current_row=1
current_box=0
data={}
class Main(object):
#this is used every time a new entry is created
def save_data(self):
global current_box
global current_row
global no_of_boxes
###this it the key. It creates the entries
###in a list so that we can access them
###without the name
boxes.append(Entry(root))
boxes[-1].grid(column=1, row=current_row)
###
try:
data["Name"+str(current_box)]=boxes[-2].get()
except:
data["Name"+str(current_box)]=boxes[-1].get()
current_row+=1
current_box+=1
no_of_boxes+=1
print no_of_boxes
print boxes
print data
with open("test.json","wb") as f:
dill.dump(data,f)
#to save when closing
def save_close(self):
global current_box
global current_row
boxes.append(Entry(root))
boxes[-1].grid(column=1, row=current_row)
data["Name"+str(current_box)]=boxes[-2].get()
with open("test.json","wb") as f:
dill.dump(data,f)
print no_of_boxes
print boxes
print data
sys.exit()
def load_data(self):
with open("test.json","rb") as f:
data=dill.load(f)
def entry_1(self):
global current_row
global no_of_boxes
nameLabel=Label(root,text="NAME")
nameLabel.grid(column=0,row=current_row)
self.save_data()
app=Main()
MoreButton=Button(root,text="+",command=app.entry_1)
MoreButton.grid(column=1,row=0)
CloseButton=Button(root, text="Close and save", command=app.save_close)
CloseButton.grid(column=2, row=0)
root.mainloop()
The saved data is saved as "Name" plus current_box in data so to get it go data["Name1"] for entry box 1 and so on.
I hope this is a better answer than my last one!
P.S if you still have problems with other parts of this code, feel free to contact me on my website.
Ok. I'm designing a small text-based RPG, however, I need to have the player be able to save the game. I've succeeded in doing this through using the pickle module, but I'm trying to get the player to be able to get back to their previous point in the storyline through using this variable I call 'storypointe'. Basically it would work like this:
if storypointe == 0:
#Story, story, stuff happens here...
storypointe += 1
if storypointe == 1:
#More story, more story, more stuff happens here....
I would then pickle the variable storypointe, and when the game is loaded (meaning using pickle.load to get player stats and storypointe from whatever file I pickled it into), and IDEALLY it would just start from whichever code block storypointe corresponds to. The actual code is way too much work for the writer and (perhaps) the readers, so I've written the following code that simulates the same environment and replicates the same problem.
storypointe = 0
jump = 0
spin = 0
dive = 0
roar = 0
savefile = "C:\Users\Sammu\The Folder\databin.txt"
import pickle, sys
def save ():
with open(savefile, 'w') as savebin:
actions = [jump, spin, dive, roar, storypointe]
pickle.dump (actions, savebin)
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
print actions2
jump = actions2[0]
spin = actions2[1]
dive = actions2[2]
roar = actions2[3]
storypointe = actions2[4]
#Begin the code#
gameIO = raw_input ('Would you like to load previous game?\n>>> ')
if gameIO in ['yes', 'load', 'load game', 'Yes', 'Load', 'Load game']:
load ()
if storypointe == 0:
action = raw_input ('Would you like to jump, spin, dive or roar?\n>>> ')
if action in ['jump', 'Jump']:
jump += 1
print jump
if action in ['spin', 'Spin']:
spin += 1
print spin
if action in ['dive', 'Dive']:
dive += 1
print dive
if action in ['roar', 'Roar']:
roar += 1
print roar
storypointe += 1
if storypointe == 1:
print "\n\nYou have progressed to the next stage"
save ()
So if storypointe is equal to actions2[4], then that must mean it should be equal to 1. But the problem here is that it's always following the first code block, starting from
action = raw_input ('#yadayadayada')
instead of:
print "You have progressed to the next stage"
I think you're confused about Python scoping.
Here you make a new variable at module level:
storypointe = 0
[...]
But here:
def load ():
with open(savefile, 'r') as loadbin:
actions2 = pickle.load (loadbin)
[...]
storypointe = actions2[4]
you simply make a new local name "storypointe" in the function load. It doesn't affect what storypointe is at the module level. I would store your variables in a class or a dict instead, which would also prevent having to do the actions2[i] stuff.
Rather than express your narrative as a bunch of if statements consider it as a state machine, if you express your story-line as a tree then you can easily store routes through the game as references to the next node in the tree, you can also store references (unique) to each node, allowing for easy saving and loading of positions.
See for example
class Node(object):
def __init__(self, parent, children=None):
self.parent = parent
self.children = children or {}
story = {}
story['a'] = Node(None)
story['b'] = Node(a)
story['a'].children['b'] = story['b']