Reputation: 1017
I would like to know how to save a programs current settings so that it remains the same, unless specified otherwise, on program restart or computer restart. For example, windows default program sticky notes, where it saves the text, so that it can be used even after the computer is shut down.
Is there some sort of module that you can import? My program is basically a task list program, where you can add stuff to a list, and tick it off using wxPython check boxes. Is there any possible way to keep it's state even after program exit?
It would be appreciated if someone could show me an example with my code, and don't worry, I won't just simply copy it and be done with it. It will be considered a learning experience for me, so that I may use it in the future. Thanks.
Here is my program:
import wx, sys,os
mylist = []
class test(wx.Frame):
def __init__(self, parent, id):
self.count = 1
#Frame
wx.Frame.__init__(self,parent,id,'List',size = (200,500))
#Panel
self.panel = wx.Panel(self)
item = wx.TextEntryDialog(None, "List Title")
if item.ShowModal() == wx.ID_OK:
print 'here'
answer = item.GetValue()
mylist.append(answer)
print mylist
windtitle = wx.StaticText(self.panel, -1, answer, (10,10))
windtitle.SetForegroundColour("blue")
addButton = wx.Button(self.panel, label = "+ Add", pos=(40,450), size = (60,-1))
finishButton = wx.Button(self.panel, label = "Finish", pos=(110,450), size = (60,-1))
self.Bind(wx.EVT_BUTTON, self.addtomenu, addButton)
self.Bind(wx.EVT_BUTTON, self.finish, finishButton)
def finish(self, event):
self.Destroy()
sys.exit()
def addtomenu(self,event):
newitem = wx.TextEntryDialog(None, "New Item")
if newitem.ShowModal() == wx.ID_OK:
count = len(mylist)+1
print count
yaxis = 20*count
if count == 21:
wx.StaticText(self.panel, -1, "List To Full", (10, yaxis))
else:
answer = newitem.GetValue()
mylist.append(answer)
print mylist
self.Bind(wx.EVT_CLOSE, self.closewindow)
wx.CheckBox(self.panel, -1, answer, (10,yaxis), size = (200,-1))
def closewindow(self, event):
self.Destroy()
if __name__ == "__main__":
app=wx.PySimpleApp() #Blood
frame = test(parent=None, id = -1) #Skin
frame.Show()
app.MainLoop() #Heart
Upvotes: 4
Views: 4981
Reputation: 2034
Just a short notice. I wondered if there is a built-in solutiuon - after all, most of us faces this problem sooner or later.
Turns out, there is a thing called PersistenceManager that can be used to simply save and resore the state.
Note, I did not use it just came across.
Upvotes: 0
Reputation: 879739
Here is an example of how you might save the state of the program in a JSON file. You already have a finish
method which is called when the program exits or when the Finish button is closed. We can use it now to also call a save
method which saves the state to a JSON file.
def finish(self, event):
self.save()
self.Destroy()
sys.exit()
def save(self):
windtitle = self.windtitle.GetLabelText()
checkboxes = [{'checked': child.IsChecked(),
'label': child.GetLabel()}
for child in self.panel.GetChildren()
if isinstance(child, wx.CheckBox)]
data = {
'windtitle':windtitle,
'checkboxes':checkboxes,
}
with open(CONFIGFILE, 'w') as f:
json.dump(data, f)
And here is how you could read the JSON data to reconstitute the GUI:
def load(self):
if os.path.exists(CONFIGFILE):
with open(CONFIGFILE, 'r') as f:
data = json.load(f)
title = data['windtitle']
self.windtitle = wx.StaticText(self.panel, -1, title)
self.vbox.Add(self.windtitle)
for checkbox in data['checkboxes']:
label = checkbox['label']
cb = wx.CheckBox(
self.panel, -1, checkbox['label'])
self.vbox.Add(cb)
cb.SetValue(checkbox['checked'])
else:
self.create_windtitle()
self.create_buttons()
For example:
import wx, sys, os
import json
CONFIGFILE = os.path.expanduser('~/tasklist.json')
class test(wx.Frame):
def __init__(self, parent, id):
frame = wx.Frame.__init__(self, parent, id, 'List', size = (200,500))
self.panel = wx.Panel(self)
self.panelbox = wx.BoxSizer(wx.VERTICAL)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.load()
self.panelbox.Add(self.vbox)
self.panelbox.Add(self.buttonbox)
self.panel.SetSizer(self.panelbox)
self.panelbox.Fit(self)
self.Bind(wx.EVT_BUTTON, self.addtomenu, self.addButton)
self.Bind(wx.EVT_BUTTON, self.finish, self.finishButton)
self.Bind(wx.EVT_CLOSE, self.finish)
def create_buttons(self):
self.buttonbox = wx.BoxSizer(wx.VERTICAL)
self.addButton = wx.Button(
self.panel, label = "+ Add")
self.finishButton = wx.Button(
self.panel, label = "Finish")
self.buttonbox.Add(self.addButton)
self.buttonbox.Add(self.finishButton)
def create_windtitle(self):
item = wx.TextEntryDialog(None, "List Title")
if item.ShowModal() == wx.ID_OK:
answer = item.GetValue()
self.windtitle = wx.StaticText(self.panel, -1, answer)
self.windtitle.SetForegroundColour("blue")
def addtomenu(self, event):
newitem = wx.TextEntryDialog(None, "New Item")
if newitem.ShowModal() == wx.ID_OK:
if len(self.mylist) > 5:
wx.StaticText(self.panel, -1, "List To Full")
else:
answer = newitem.GetValue()
cb = wx.CheckBox(self.panel, -1, answer)
self.vbox.Add(cb)
self.panelbox.Fit(self)
def finish(self, event):
self.save()
self.Destroy()
sys.exit()
@property
def mylist(self):
return [ child.GetLabel()
for child in self.panel.GetChildren()
if isinstance(child, wx.CheckBox) ]
def save(self):
windtitle = self.windtitle.GetLabelText()
checkboxes = [{'checked': child.IsChecked(),
'label': child.GetLabel()}
for child in self.panel.GetChildren()
if isinstance(child, wx.CheckBox)]
data = {
'windtitle':windtitle,
'checkboxes':checkboxes,
}
with open(CONFIGFILE, 'w') as f:
json.dump(data, f)
def load(self):
if os.path.exists(CONFIGFILE):
with open(CONFIGFILE, 'r') as f:
data = json.load(f)
title = data['windtitle']
self.windtitle = wx.StaticText(self.panel, -1, title)
self.vbox.Add(self.windtitle)
for checkbox in data['checkboxes']:
label = checkbox['label']
cb = wx.CheckBox(
self.panel, -1, checkbox['label'])
self.vbox.Add(cb)
cb.SetValue(checkbox['checked'])
else:
self.create_windtitle()
self.create_buttons()
if __name__ == "__main__":
app = wx.PySimpleApp() #Blood
frame = test(parent = None, id = -1) #Skin
frame.Show()
app.MainLoop() #Heart
By the way, do not use explicit positions for placing widgets in your GUI. That road leads to madness. If you use positions (e.g. pos = (10,yaxis)
), as your GUI grows, it becomes more and more difficult to modify your layout. Every element's position becomes dependent on some other element's position and it soon becomes unmanageable.
Every GUI framework provides some saner way to achieve nice looking layouts. I'm not very familiar with wxpython
, but it seems to use BoxSizers
. The layout I used above is very rudimentary. I'm sure a much nicer layout can be achieved with some study of wxpython layout design patterns.
Sometimes I needed to find out what all the attributes and methods of a widget are. For example, I didn't know how to ask a Panel what Checkboxes it contains. I found it using this function:
def describe(obj):
for key in dir(obj):
try:
val = getattr(obj, key)
except AttributeError:
continue
if callable(val):
help(val)
else:
print('{k} => {v}'.format(k = key, v = val))
print('-'*80)
describe(self.panel)
This is the function I had in utils_debug.
Upvotes: 4
Reputation: 34698
Your probably gonna want to store the state of the buttons in a data collection like a list or a dict, then you can marshal or serialize the data
e.g.
my_button_states = {"button1":True}
import json
with open("states.json", 'w') as output:
json.dump(my_button_states, output)
Later on just re-load the json back into the program and set the buttons by looping over the dict
e.g.
with open("states.json") as fp:
my_button_states = json.loads(fp)
# inside your frame or something.... idk where you put them.
for button, state in my_button_states.iteritems():
setattr(self, button, state)
You can use json
or some people might suggest pickle
but json
might be the better choice for this.
Using your code you probably want to do something like
json.dump(mylist, output)
In my example of course
Upvotes: 1