-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpycxsimulator.py
More file actions
294 lines (242 loc) · 11.8 KB
/
Copy pathpycxsimulator.py
File metadata and controls
294 lines (242 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
## "pycxsimulator.py"
## Realtime Simulation GUI for PyCX
##
## Developed by:
## Chun Wong
## email@chunwong.net
##
## Revised by:
## Hiroki Sayama
## sayama@binghamton.edu
##
## Copyright 2012 Chun Wong & Hiroki Sayama
##
## Simulation control & GUI extensions
## Copyright 2013 Przemyslaw Szufel & Bogumil Kaminski
## {pszufe, bkamins}@sgh.waw.pl
##
## Fixing errors due to "the grid and pack problem" by:
## Toshihiro Tanizawa
## tanizawa@ee.kochi-ct.ac.jp
## began at 2016-06-15(Wed) 17:10:17
## fixed grid() and pack() problem on 2016-06-21(Tue) 18:29:40
##
## The following two lines should be placed at the beginning of your simulator code:
##
## import matplotlib
## matplotlib.use('TkAgg')
from tkinter import *
import pylab as PL
from ttk import Notebook
class GUI:
## GUI variables
titleText = 'PyCX Simulator' # window title
timeInterval = 0 # refresh time in milliseconds
running = False
modelFigure = None
stepSize = 1
currentStep = 0
# Constructor
def __init__(self, title='PyCX Simulator', interval=0, stepSize=1, parameterSetters=[]):
self.titleText = title
self.timeInterval = interval
self.stepSize = stepSize
self.parameterSetters = parameterSetters
self.varEntries = {}
self.statusStr = ""
self.initGUI()
# Initialization
def initGUI(self):
#create root window
self.rootWindow = Tk()
self.statusText = StringVar(value=self.statusStr) # at this point, statusStr = ""
self.setStatusStr("Simulation not yet started")
self.rootWindow.wm_title(self.titleText) # titleText = 'PyCX Simulator'
self.rootWindow.protocol('WM_DELETE_WINDOW', self.quitGUI)
self.rootWindow.geometry('450x300')
self.rootWindow.columnconfigure(0, weight=1)
self.rootWindow.rowconfigure(0, weight=1)
self.notebook = Notebook(self.rootWindow)
# self.notebook.grid(row=0,column=0,padx=2,pady=2,sticky='nswe') # commented out by toshi on 2016-06-21(Tue) 18:30:25
self.notebook.pack(side=TOP, padx=2, pady=2)
self.frameRun = Frame()
self.frameSettings = Frame()
self.frameParameters = Frame()
self.frameInformation = Frame()
self.notebook.add(self.frameRun,text="Run")
self.notebook.add(self.frameSettings,text="Settings")
self.notebook.add(self.frameParameters,text="Parameters")
self.notebook.add(self.frameInformation,text="Info")
self.notebook.pack(expand=NO, fill=BOTH, padx=5, pady=5 ,side=TOP)
# self.notebook.grid(row=0, column=0, padx=5, pady=5, sticky='nswe') # commented out by toshi on 2016-06-21(Tue) 18:31:02
self.status = Label(self.rootWindow, width=40,height=3, relief=SUNKEN, bd=1, textvariable=self.statusText)
# self.status.grid(row=1,column=0,padx=5,pady=5,sticky='nswe') # commented out by toshi on 2016-06-21(Tue) 18:31:17
self.status.pack(side=TOP, fill=X, padx=5, pady=5, expand=NO)
# -----------------------------------
# frameRun
# -----------------------------------
# buttonRun
self.runPauseString = StringVar()
self.runPauseString.set("Run")
self.buttonRun = Button(self.frameRun,width=30,height=2,textvariable=self.runPauseString,command=self.runEvent)
self.buttonRun.pack(side=TOP, padx=5, pady=5)
self.showHelp(self.buttonRun,"Runs the simulation (or pauses the running simulation)")
# buttonStep
self.buttonStep = Button(self.frameRun,width=30,height=2,text='Step Once',command=self.stepOnce)
self.buttonStep.pack(side=TOP, padx=5, pady=5)
self.showHelp(self.buttonStep,"Steps the simulation only once")
# buttonReset
self.buttonReset = Button(self.frameRun,width=30,height=2,text='Reset',command=self.resetModel)
self.buttonReset.pack(side=TOP, padx=5, pady=5)
self.showHelp(self.buttonReset,"Resets the simulation")
# -----------------------------------
# frameSettings
# -----------------------------------
can = Canvas(self.frameSettings)
lab = Label(can, width=25,height=1,text="Step size ", justify=LEFT, anchor=W,takefocus=0)
lab.pack(side='left')
self.stepScale = Scale(can,from_=1, to=50, resolution=1,command=self.changeStepSize,orient=HORIZONTAL, width=25,length=150)
self.stepScale.set(self.stepSize)
self.showHelp(self.stepScale,"Skips model redraw during every [n] simulation steps\nResults in a faster model run.")
self.stepScale.pack(side='left')
can.pack(side='top')
can = Canvas(self.frameSettings)
lab = Label(can, width=25,height=1,text="Step visualization delay in ms ", justify=LEFT, anchor=W,takefocus=0)
lab.pack(side='left')
self.stepDelay = Scale(can,from_=0, to=max(2000,self.timeInterval),
resolution=10,command=self.changeStepDelay,orient=HORIZONTAL, width=25,length=150)
self.stepDelay.set(self.timeInterval)
self.showHelp(self.stepDelay,"The visualization of each step is delays by the given number of milliseconds.")
self.stepDelay.pack(side='left')
can.pack(side='top')
# --------------------------------------------
# frameInformation
# --------------------------------------------
scrollInfo = Scrollbar(self.frameInformation)
self.textInformation = Text(self.frameInformation, width=45,height=13,bg='lightgray',wrap=WORD,font=("Courier",10))
scrollInfo.pack(side=RIGHT, fill=Y)
self.textInformation.pack(side=LEFT,fill=BOTH,expand=YES)
scrollInfo.config(command=self.textInformation.yview)
self.textInformation.config(yscrollcommand=scrollInfo.set)
# --------------------------------------------
# ParameterSetters
# --------------------------------------------
for variableSetter in self.parameterSetters:
can = Canvas(self.frameParameters)
lab = Label(can, width=25,height=1,text=variableSetter.__name__+" ",anchor=W,takefocus=0)
lab.pack(side='left')
ent = Entry(can, width=11)
ent.insert(0, str(variableSetter()))
if variableSetter.__doc__ != None and len(variableSetter.__doc__) > 0:
self.showHelp(ent,variableSetter.__doc__.strip())
ent.pack(side='left')
can.pack(side='top')
self.varEntries[variableSetter]=ent
if len(self.parameterSetters) > 0:
self.buttonSaveParameters = Button(self.frameParameters,width=50,height=1,
command=self.saveParametersCmd,text="Save parameters to the running model",state=DISABLED)
self.showHelp(self.buttonSaveParameters,
"Saves the parameter values.\nNot all values may take effect on a running model\nA model reset might be required.")
self.buttonSaveParameters.pack(side='top',padx=5,pady=5)
self.buttonSaveParametersAndReset = Button(self.frameParameters,width=50,height=1,
command=self.saveParametersAndResetCmd,text="Save parameters to the model and reset the model")
self.showHelp(self.buttonSaveParametersAndReset,"Saves the given parameter values and resets the model")
self.buttonSaveParametersAndReset.pack(side='top',padx=5,pady=5)
# <<<<< Init >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def setStatusStr(self,newStatus):
self.statusStr = newStatus
self.statusText.set(self.statusStr)
# model control functions for changing parameters
def changeStepSize(self,val):
self.stepSize = int(val)
def changeStepDelay(self,val):
self.timeInterval= int(val)
def saveParametersCmd(self):
for variableSetter in self.parameterSetters:
variableSetter(float(self.varEntries[variableSetter].get()))
self.setStatusStr("New parameter values have been set")
def saveParametersAndResetCmd(self):
self.saveParametersCmd()
self.resetModel()
# <<<< runEvent >>>>>
# This event is envoked when "Run" button is clicked.
def runEvent(self):
self.running = not self.running
if self.running:
self.rootWindow.after(self.timeInterval,self.stepModel)
self.runPauseString.set("Pause")
self.buttonStep.configure(state=DISABLED)
self.buttonReset.configure(state=DISABLED)
if len(self.parameterSetters) > 0:
self.buttonSaveParameters.configure(state=NORMAL)
self.buttonSaveParametersAndReset.configure(state=DISABLED)
else:
self.runPauseString.set("Continue Run")
self.buttonStep.configure(state=NORMAL)
self.buttonReset.configure(state=NORMAL)
if len(self.parameterSetters) > 0:
self.buttonSaveParameters.configure(state=NORMAL)
self.buttonSaveParametersAndReset.configure(state=NORMAL)
def stepModel(self):
if self.running:
self.modelImpFunc()
self.currentStep += 1
self.setStatusStr("Step "+str(self.currentStep))
self.status.configure(foreground='black')
if (self.currentStep) % self.stepSize == 0:
self.drawModel()
self.rootWindow.after(int(self.timeInterval*1.0/self.stepSize),self.stepModel)
def stepOnce(self):
self.running = False
self.runPauseString.set("Continue Run")
self.modelImpFunc()
self.currentStep += 1
self.setStatusStr("Step "+str(self.currentStep))
self.drawModel()
if len(self.parameterSetters) > 0:
self.buttonSaveParameters.configure(state=NORMAL)
def resetModel(self):
self.running = False
self.runPauseString.set("Run")
self.modelInitFunc()
self.modelPopulate() #popluate after init
self.currentStep = 0;
self.setStatusStr("Model has been reset")
self.drawModel()
def drawModel(self):
PL.ion() # bug fix by Alex Hill in 2013
if self.modelFigure == None or self.modelFigure.canvas.manager.window == None:
self.modelFigure = PL.figure()
self.modelDrawFunc()
self.modelFigure.canvas.manager.window.update()
PL.show() # bug fix by Hiroki Sayama in 2016
def start(self,func=[]):
if len(func)==4:
self.modelInitFunc = func[0]
self.modelPopulate = func[1]
self.modelDrawFunc = func[2]
self.modelImpFunc = func[3]
if (self.modelImpFunc.__doc__ != None and len(self.modelImpFunc.__doc__)>0):
self.showHelp(self.buttonStep,self.modelImpFunc.__doc__.strip())
if (self.modelInitFunc.__doc__ != None and len(self.modelInitFunc.__doc__)>0):
self.textInformation.config(state=NORMAL)
self.textInformation.delete(1.0, END)
self.textInformation.insert(END, self.modelInitFunc.__doc__.strip())
self.textInformation.config(state=DISABLED)
self.modelInitFunc()
self.modelPopulate() #populate the grid
self.drawModel()
self.rootWindow.mainloop()
def quitGUI(self):
PL.close('all')
self.rootWindow.quit()
self.rootWindow.destroy()
def showHelp(self, widget,text):
def setText(self):
self.statusText.set(text)
self.status.configure(foreground='blue')
def showHelpLeave(self):
self.statusText.set(self.statusStr)
self.status.configure(foreground='black')
widget.bind("<Enter>", lambda e : setText(self))
widget.bind("<Leave>", lambda e : showHelpLeave(self))