Skip to content

Commit 42f10cf

Browse files
committed
add comments + remove backend file + Remove console messages
1 parent e4323c0 commit 42f10cf

File tree

3 files changed

+167
-72
lines changed

3 files changed

+167
-72
lines changed

GUI/backend.py

-15
This file was deleted.

GUI/interface.py

+158-56
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import tkinter.ttk as ttk
44
from tkinter import messagebox, simpledialog
55

6-
import backend
76
import main
87

8+
### Global Variables to store the solution analytics ###
99
algorithm = None
1010
initialState = None
1111
statepointer = cost = counter = depth = 0
@@ -15,6 +15,9 @@
1515

1616
class InterfaceApp:
1717

18+
# =============================================================================================================== #
19+
### Build the GUI ###
20+
1821
def __init__(self, master=None):
1922

2023
self._job = None
@@ -151,36 +154,54 @@ def __init__(self, master=None):
151154

152155
self.gif = [tk.PhotoImage(file='loading.gif', format='gif -index %i' % i) for i in range(10)]
153156

154-
155157
def run(self):
158+
"""
159+
Run the program, display the GUI
160+
"""
156161
app.displayStateOnGrid('000000000')
157162
app.gif_loading.place_forget()
158-
self.updateGrid()
159-
self.mainwindow.after(0, app.update, 0)
163+
self.refreshFrame()
164+
self.mainwindow.after(0, app.refreshGIF, 0)
160165
self.mainwindow.mainloop()
161166

167+
# =============================================================================================================== #
168+
### Widget Methods ###
169+
162170
@staticmethod
163-
def update(ind):
171+
def refreshGIF(ind):
172+
"""
173+
Refreshes the loading gif to show the next frame
174+
"""
164175
frame = app.gif[ind]
165176
ind = (ind + 1) % 10
166177
app.gif_loading.configure(image=frame)
167-
app.appFrame.after(50, app.update, ind)
178+
app.appFrame.after(50, app.refreshGIF, ind)
168179

169180
def prevSequence(self, event=None):
181+
"""
182+
Displays the previous state on the grid
183+
"""
170184
global statepointer
171185
if statepointer > 0:
172186
self.stopFastForward()
173187
statepointer -= 1
174-
self.updateGrid()
188+
self.refreshFrame()
175189

176190
def nextSequence(self, event=None):
191+
"""
192+
Displays the next state on the grid
193+
"""
177194
global statepointer
178195
if statepointer < len(path) - 1:
179196
self.stopFastForward()
180197
statepointer += 1
181-
self.updateGrid()
198+
self.refreshFrame()
182199

183200
def solve(self, event=None):
201+
"""
202+
Function is invoked at pressing the solve button. Solves the puzzle with the given initialState and algorithm
203+
then gives a suitable response to the user
204+
"""
184205
global algorithm, initialState
185206
app.gif_loading.place(x=600, y=125, anchor="s")
186207
if self.readyToSolve():
@@ -189,68 +210,55 @@ def solve(self, event=None):
189210
self.resetGrid()
190211
self.solveState()
191212
if len(path) == 0:
192-
print('<!> Unsolvable State')
193213
messagebox.showinfo('Unsolvable!', 'The state you entered is unsolvable')
194214
self.displaySearchAnalysis(True)
195215
else:
196-
print('<!> Solved!')
197-
self.updateGrid()
216+
self.refreshFrame()
198217
else:
199218
solvingerror = 'Cannot solve.\n' \
200219
'Algorithm in use: ' + str(algorithm) + '\n' \
201220
'Initial State : ' + str(initialState)
202-
print(solvingerror + '\n-')
203221
messagebox.showerror('Cannot Solve', solvingerror)
204222
app.gif_loading.place_forget()
205223

206224
def enterInitialState(self, event=None):
225+
"""
226+
Invoked at pressing enter initial state button. Displays a simple dialog box for the user to enter their
227+
initial state. The state is validated and a suitable response it displayed to the user
228+
"""
207229
global initialState, statepointer
208230
inputState = simpledialog.askstring('Initial State Entry', 'Please enter your initial state')
209231
if inputState is not None:
210-
if backend.validateState(inputState):
232+
if self.validateState(inputState):
211233
initialState = inputState
212234
self.reset()
213235
app.displayStateOnGrid(initialState)
214236
else:
215-
print('Invalid initial state')
216237
messagebox.showerror('Input Error', 'Invalid initial state')
217238

218239
def selectAlgorithm(self, event=None):
240+
"""
241+
Invoked at activating the algorithms combobox. Associates the chosen value to the global variable 'algorithm'
242+
"""
219243
global algorithm
220244
try:
221245
choice = self.algorithmbox.selection_get()
222246
self.reset()
223247
algorithm = choice
224248
except:
225-
print('Invalid algorithm selection')
226-
227-
def showContributors(self, event=None):
228-
messagebox.showinfo('Contributors', "6744 - Adham Mohamed Aly\n"
229-
"6905 - Mohamed Farid Abdelaziz\n"
230-
"7140 - Yousef Ashraf Kotp\n")
231-
232-
def displaySearchAnalysis(self, force_display=False):
233-
if self.solved() or force_display is True:
234-
analytics = 'Analysis of ' + str(algorithm) + \
235-
'\ninitial state = ' + str(initialState)
236-
if force_display:
237-
analytics += '\n< UNSOLVABLE >'
238-
analytics += '\n-------------------------------' \
239-
'\n' + 'Nodes expanded: \n' + str(counter) + \
240-
'\n' + 'Search depth: \n' + str(depth) + \
241-
'\n' + 'Search cost: \n' + str(cost) + \
242-
'\n' + 'Running Time: \n' + str(runtime) + ' s'
243-
else:
244-
analytics = ''
245-
app.analysisbox.configure(text=analytics)
249+
pass
246250

247251
def fastForward(self, event=None):
252+
"""
253+
Invoked at pressing fast-forward button. Displays following states in rapid succession until it reaches the
254+
goal state or until terminated by the stopFastForward() method
255+
"""
248256
global statepointer
249257
self.stopFastForward()
250258
if statepointer < cost:
251259
app.stopbutton.configure(state='enabled')
252260
statepointer += 1
253-
self.updateGrid()
261+
self.refreshFrame()
254262
ms = 100
255263
if 100 < cost <= 1000:
256264
ms = 20
@@ -261,6 +269,10 @@ def fastForward(self, event=None):
261269
self.stopFastForward()
262270

263271
def fastBackward(self, event=None):
272+
"""
273+
Invoked at pressing fast-backward button. Displays previous states in rapid succession until it reaches the
274+
goal state or until terminated by the stopFastForward() method
275+
"""
264276
global statepointer
265277
self.stopFastForward()
266278
if statepointer > 0:
@@ -272,44 +284,96 @@ def fastBackward(self, event=None):
272284
app._job = app.stepCount.after(ms, self.fastBackward)
273285
else:
274286
self.stopFastForward()
275-
self.updateGrid()
287+
self.refreshFrame()
276288

277-
def stopFastForward(self, event=None):
289+
@staticmethod
290+
def stopFastForward(event=None):
291+
"""
292+
Invoked at pressing stop fast-forward/backward button. Stops fast-forward/backward
293+
"""
278294
if app._job is not None:
279295
app.stopbutton.configure(state='disabled')
280296
app.stepCount.after_cancel(app._job)
281297
app._job = None
282298

283299
def resetStepCounter(self, event=None):
300+
"""
301+
Invoked at pressing reset button. Resets the grid to the initial state and the step counter to 0
302+
"""
284303
global statepointer
285304
if statepointer > 0:
286305
self.stopFastForward()
287306
statepointer = 0
288-
self.updateGrid()
307+
self.refreshFrame()
308+
309+
def showContributors(self, event=None):
310+
"""
311+
Invoked at pressing the contributors button. Displays a message box Containing names and IDs of contributors
312+
"""
313+
messagebox.showinfo('Contributors', "6744 - Adham Mohamed Aly\n"
314+
"6905 - Mohamed Farid Abdelaziz\n"
315+
"7140 - Yousef Ashraf Kotp\n")
316+
317+
# =============================================================================================================== #
318+
### Helper Functions ###
319+
320+
def displaySearchAnalysis(self, force_display=False):
321+
"""
322+
Displays the analysis of the search algorithm after execution.
323+
"""
324+
if self.solved() or force_display is True:
325+
analytics = 'Analysis of ' + str(algorithm) + \
326+
'\ninitial state = ' + str(initialState)
327+
if force_display:
328+
analytics += '\n< UNSOLVABLE >'
329+
analytics += '\n-------------------------------' \
330+
'\n' + 'Nodes expanded: \n' + str(counter) + \
331+
'\n' + 'Search depth: \n' + str(depth) + \
332+
'\n' + 'Search cost: \n' + str(cost) + \
333+
'\n' + 'Running Time: \n' + str(runtime) + ' s'
334+
else:
335+
analytics = ''
336+
app.analysisbox.configure(text=analytics)
289337

290338
def displayStateOnGrid(self, state):
291-
if not backend.validateState(state):
339+
"""
340+
Display input state to the grid
341+
:param state: String representation of the required state
342+
"""
343+
if not self.validateState(state):
292344
state = '000000000'
293-
self.cell0.configure(text=backend.adjustDigit(state[0]))
294-
self.cell1.configure(text=backend.adjustDigit(state[1]))
295-
self.cell2.configure(text=backend.adjustDigit(state[2]))
296-
self.cell3.configure(text=backend.adjustDigit(state[3]))
297-
self.cell4.configure(text=backend.adjustDigit(state[4]))
298-
self.cell5.configure(text=backend.adjustDigit(state[5]))
299-
self.cell6.configure(text=backend.adjustDigit(state[6]))
300-
self.cell7.configure(text=backend.adjustDigit(state[7]))
301-
self.cell8.configure(text=backend.adjustDigit(state[8]))
345+
self.cell0.configure(text=self.adjustDigit(state[0]))
346+
self.cell1.configure(text=self.adjustDigit(state[1]))
347+
self.cell2.configure(text=self.adjustDigit(state[2]))
348+
self.cell3.configure(text=self.adjustDigit(state[3]))
349+
self.cell4.configure(text=self.adjustDigit(state[4]))
350+
self.cell5.configure(text=self.adjustDigit(state[5]))
351+
self.cell6.configure(text=self.adjustDigit(state[6]))
352+
self.cell7.configure(text=self.adjustDigit(state[7]))
353+
self.cell8.configure(text=self.adjustDigit(state[8]))
302354

303355
@staticmethod
304356
def readyToSolve():
357+
"""
358+
Checks if current state is ready to be solved by checking if the global variables 'initialState' and
359+
'algorithm' are not None
360+
:return: boolean
361+
"""
305362
return initialState is not None and algorithm is not None
306363

307364
@staticmethod
308365
def solved():
366+
"""
367+
Checks if there is a solution registered in the global variables
368+
:return: boolean
369+
"""
309370
return len(path) > 0
310371

311372
@staticmethod
312373
def solveState():
374+
"""
375+
Solves the puzzle with 'initialState' and the chosen 'algorithm'. Assumes the current state is ready to solve.
376+
"""
313377
global path, cost, counter, depth, runtime
314378
if str(algorithm) == 'BFS':
315379
main.BFS(initialState)
@@ -327,16 +391,20 @@ def solveState():
327391
main.AStarSearch_euclid(initialState)
328392
path, cost, counter, depth, runtime = \
329393
main.euclid_path, main.euclid_cost, main.euclid_counter, round(main.euclid_depth), main.time_euclid
330-
else:
331-
print('Error occurred')
332394

333395
def resetGrid(self):
396+
"""
397+
Resets the grid and step counter to the initial state
398+
"""
334399
global statepointer
335400
statepointer = 0
336-
self.updateGrid()
337-
app.stepCount.configure(text=self.getStepCount())
401+
self.refreshFrame()
402+
app.stepCount.configure(text=self.getStepCountString())
338403

339404
def reset(self):
405+
"""
406+
Resets global variables and the GUI frame. Removes currently registered solution
407+
"""
340408
global path, cost, counter, runtime
341409
cost = counter = 0
342410
runtime = 0.0
@@ -345,15 +413,22 @@ def reset(self):
345413
app.analysisbox.configure(text='')
346414

347415
@staticmethod
348-
def getStepCount():
416+
def getStepCountString():
417+
"""
418+
Returns string representation of the step count to be displayed on the step-counter
419+
:return: String
420+
"""
349421
return str(statepointer) + ' / ' + str(cost)
350422

351423
@staticmethod
352-
def updateGrid():
424+
def refreshFrame():
425+
"""
426+
Refreshes the frame with all its components: grid, counter, button, etc.
427+
"""
353428
if cost > 0:
354429
state = main.getStringRepresentation(path[statepointer])
355430
app.displayStateOnGrid(state)
356-
app.stepCount.configure(text=app.getStepCount())
431+
app.stepCount.configure(text=app.getStepCountString())
357432
app.displaySearchAnalysis()
358433
if statepointer == 0:
359434
app.resetbutton.configure(state='disabled')
@@ -371,6 +446,33 @@ def updateGrid():
371446
app.fastforwardbutton.configure(state='enabled')
372447
app.nextbutton.configure(state='enabled')
373448

449+
@staticmethod
450+
def validateState(inputState):
451+
"""
452+
Validates given state
453+
:param inputState: String representation of state to be validated
454+
:return: boolean
455+
"""
456+
seen = []
457+
if inputState is None or len(inputState) != 9 or not inputState.isnumeric():
458+
return False
459+
for dig in inputState:
460+
if dig in seen or dig == '9':
461+
return False
462+
seen.append(dig)
463+
return True
464+
465+
@staticmethod
466+
def adjustDigit(dig):
467+
"""
468+
Converts the zero to an empty cell. Otherwise, returns the digit as it is.
469+
:param dig: Character of the digit to be adjusted
470+
:return: string
471+
"""
472+
if dig == '0':
473+
return ' '
474+
return dig
475+
374476

375477
if __name__ == "__main__":
376478
global app

0 commit comments

Comments
 (0)