-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUserInterface.py
439 lines (316 loc) · 20.7 KB
/
UserInterface.py
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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
import tkinter as tk
import tkinter.messagebox
class SetupFrames(tk.Frame): # Class initialises all frames in application
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.frames = {} # Dict that holds record of all frames
# setup frames
for new_frame in(LogInFrame, AdminPageFrame, EnterGraphFrame,RandomGraphFrame ,AlgorithmFrame):
frame = new_frame(self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames[new_frame] = frame
self.change_frame(LogInFrame) # Display Mainframe
def change_frame(self, frame): # Method to change between frames when called
self.frames[frame].tkraise()
class LogInFrame(tk.Frame): # first frame shown when application run
def __init__(self, parent):
tk.Frame.__init__(self, parent)
import Database as db
self.database = db.Database() # Composition used to access database class
self.label_username = tk.Label(self, text="Enter username: ") # label for username
self.label_pass = tk.Label(self, text="Enter password: ") # label for password
self.label_username.grid(row=0, sticky=tk.E) # Labels placed on frame
self.label_pass.grid(row=1, sticky=tk.E)
self.entry_user = tk.Entry(self) # Entries for user input
self.entry_pass = tk.Entry(self, show="*")
self.entry_user.grid(row=0, column=1) # Positions of entries on frame using
self.entry_pass.grid(row=1, column=1)
self.button_checkinputs_user = tk.Button(self, text="Press to log in", command=self.check_valid_user)
self.button_checkinputs_user.grid(row=3, column=5) # Positioning buttons on frame
def check_valid_user(self): # Method validate userinput
username = self.entry_user.get() # User input is fetched
password = self.entry_pass.get()
username = username.strip() # Remove any whitespace before/after entry
password = password.strip()
if (len(username)) == 0 or (len(password) == 0):
tk.messagebox.showinfo("title", "Invalid username or password entered, please try again or register")
else:
# Validity checked from Method within database (method returns user if valid user or admin if valid admin)
is_valid = self.database.check_login(username, password)
if is_valid == "user": # if valid user
tk.messagebox.showinfo("title", "You are logged in")
self.database.c.close() # close connections to db if user log in
self.database.conn.close()
self.master.change_frame(EnterGraphFrame)
elif is_valid == 'admin':
tk.messagebox.showinfo('Admin', 'You have logged in as admin')
self.database.c.close() # close connections to db if user log in
self.database.conn.close()
self.master.change_frame(AdminPageFrame) # Change frame to admin one
tk.messagebox.showinfo("note to admins", "If you wish to add another admin leave assigned adminid entry "
"blank, otherwise add to it an exisitng adminid for the new user.")
else: # If incorrect user must re-enter.
tk.messagebox.showinfo('Invalid input', 'Password or username not found in records')
class AdminPageFrame(tk.Frame): # Admin class
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.new_user = '' # variable that stores the username from entry
self.new_pass = '' # variable that stores password from entry
self.login_adminid = '' # variable to hold adminid (if entered)
self.login_adminname = '' # varuabke to hold admin name (if entered)
self.label_title_add = tk.Label(self, text ="ADD/REMOVE USER") # title for adding user
self.label_newuser = tk.Label(self, text="Enter new id: ") # label for new userid
self.label_pass = tk.Label(self, text="Enter id password: ") # label for userid password
self.label_adminname = tk.Label(self, text="Enter name of admin (if admin): ") # name of admin if admin
self.label_addadminid = tk.Label(self, text="Enter assigned admin id (if user)") #label for adding admin to userid
self.label_viewusers = tk.Label(self, text="VIEW USERS AND ASSIGNED ADMINS")
self.label_title_add.grid(row=0, sticky=tk.NSEW)
self.label_newuser.grid(row=1, sticky=tk.NSEW) # Labels placed on frame
self.label_pass.grid(row=2, sticky=tk.NSEW)
self.label_adminname.grid(row=3, sticky=tk.NSEW)
self.label_addadminid.grid(row=4, sticky=tk.NSEW)
self.label_viewusers.grid(row=7, sticky=tk.NSEW)
self.entry_newuser = tk.Entry(self) # Entries for user input
self.entry_pass = tk.Entry(self, show="*") # hide characters for password entry
self.entry_getname = tk.Entry(self) # entry for admin name
self.entry_getid = tk.Entry(self) # get adminid for the new userid (if user being added)
self.entry_newuser.grid(row=1, column=2) # Positions of entries on frame using
self.entry_pass.grid(row=2, column=2)
self.entry_getname.grid(row=3, column=2)
self.entry_getid.grid(row=4, column=2)
self.button_removeuser = tk.Button(self, text='Remove user', command = self.remove_user)
self.button_adduser = tk.Button(self, text="Register user", command=self.add_user)
self.button_view_users = tk.Button(self, text="View system details", command=self.view_users)
self.button_removeuser.grid(row=4, column=10) # Positioning buttons on frame
self.button_adduser.grid(row=4, column=7)
self.button_view_users.grid(row=9)
def get_info(self):
self.new_user = self.entry_newuser.get() # User input is fetched and assigned
self.new_pass = self.entry_pass.get() # user input for password fetched and assigned
self.login_adminname = self.entry_getname.get() # user input for admin name fetched
self.login_adminid = self.entry_getid.get() # admin id fetched
def remove_user(self): # Method to remove user which can be user or admin
self.get_info() # fetch data from entries
import Database as db # importing my database module
self.database = db.Database()
isadminid = self.database.check_adminid(self.new_user) # check if Id was admins
if isadminid: # Id was admins
self.database.remove_admin(self.new_user)
tk.messagebox.showinfo('Removed', 'Admin and associated users have been removed')
else:
notuserid = self.database.check_newuser(self.new_user)
if not notuserid: # if no invalid userid was found then admin must wish to remove userid
self.database.remove_user(self.new_user)
tk.messagebox.showinfo('Removed', 'User has been removed')
else:
tk.messagebox.showinfo('Invalid user/admin selected', 'Userid/adminid could not be found in records')
def validate_info(self):
import Database as db # importing my database module
self.database = db.Database() # creating connections to database
self.get_info() #collect entry information and store in initalised variables
x = self.database.check_adminid(self.login_adminid)
if (len(self.new_pass)) == 0 or (len(self.new_user) == 0): # if no data entered
tk.messagebox.showinfo("title", "One or more required fields was left blank")
return False # invalid data entry
elif (len(self.login_adminid) != 0) and (len(self.login_adminname) != 0):
return False # invalid data entry
elif (len(self.login_adminid) == 0) and (len(self.login_adminname) == 0):
return False # invalid data entry
elif x: # if valid adminid added then user must want to add new person as user
self.new_pass = self.new_pass.strip() # Remove any whitespace before/after entry
self.new_user = self.new_user.strip()
self.login_adminid = self.login_adminid.strip()
return 'user'
elif (not x) and (len(self.login_adminid) == 0) and (len(self.login_adminname) != 0):
# no adminid was given and an admin name was given so must be admin being entered
self.new_pass = self.new_pass.strip() # Remove any whitespace before/after entry
self.new_user = self.new_user.strip()
return 'admin'
else:
return False # invalid data entry
def add_user(self): # Method to add users to system
check_valid = self.validate_info() # check for valid data entry and type (admin/user)
if check_valid == 'user' or check_valid == 'admin':
import Database as db # importing my database module
self.database = db.Database() # creating connections to database
valid = self.database.check_newuser(self.new_user) # check if valid user is being added
if valid:
if check_valid == 'admin':
self.database.enter_newadmin(self.new_user, self.login_adminname, self.new_pass) # add admin data
self.database.c.close() # close connections to db if user log in
self.database.conn.close()
else:
self.database.enter_newuser(self.new_user, self.new_pass, self.login_adminid) # add user data
self.database.c.close() # close connections to db if user log in
self.database.conn.close()
else: # user data is invalid
tk.messagebox.showinfo('Invalid input', 'Userid already exists please create another id')
else:
tk.messagebox.showinfo('Invalid input', 'Please check data was entered correctly')
def view_users(self): # Method to view users in system
import Database as db # importing my database module
self.database = db.Database() # creating connections to database
tk.messagebox.showinfo('Users with assigned admins', self.database.get_joins())
self.get_counts()
self.get_mostfrequser()
def get_counts(self): # Method to get sum of system users
import Database as db # importing my database module
self.database = db.Database() # creating connections to database
tk.messagebox.showinfo("Total number of users in system including admins",
list(self.database.get_numusers())[0] + list(self.database.get_numadmins())[0])
def get_mostfrequser(self): # Method get user with most logins
import Database as db # importing my database module
self.database = db.Database() # creating connections to database
self.database.getmostlogin()
class RandomGraphFrame(tk.Frame): # Class for random graph generation
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# Button to 'create' random graph
self.run_random = tk.Button(self, text="press to generate a random graph", command=self.setup_complex)
self.run_random.grid(row=10, column=15)
def setup_complex(self):
import GraphGenerator as graphgen # importing my random graph generator module
random_graph = graphgen.ComplexGraph() # compositon used to access random graph class
random_graph.setup_nodes() # random graph being set up
random_graph.setup_neighbours()
self.display_data(random_graph.graph)
def display_data(self, graph):
import AdjancencyMatrix as amatrix
display_graph = amatrix.Matrix()
display_graph.graph = graph
tk.messagebox.showinfo('Adjacency Matrix for graph', display_graph.create_matrix()) # Complex graph displayed
self.master.frames[EnterGraphFrame].graph_nodes = graph # set graph in application to the random graph
self.master.change_frame(AlgorithmFrame) # change frame to frame with alogrithm
class EnterGraphFrame(tk.Frame): # Frame where user graph is collected
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.graph_nodes = {} # Graph is stored as dictionary
# Button for graph input
self.run_alg = tk.Button(self, text="press to begin graph entry", command=self.setup_nodes)
self.run_alg.grid(row=10, column=10)
self.run_random = tk.Button(self, text="press to generate random graph", command=self.setup_random)
self.run_random.grid(row=15, column=10)
self.source_node = tk.StringVar() # string variable to store source node
self.neighbour = tk.StringVar() # string variable to store neighbour node
self.distance = tk.DoubleVar() # Float/Double variable to store distance
self.entry_source = tk.Entry(self, textvariable=self.source_node) # Entry for source
self.entry_neighbour = tk.Entry(self, textvariable=self.neighbour) # Entry for neighbour
self.entry_distance = tk.Entry(self, textvariable=self.distance) # Entry for distance
# Sends information to add_node function for error checking and storage
self.button_addnode = tk.Button(self, text="Press to add source node and neighbours",
command=self.validate_nodes)
# Button that calls method that changes frame
self.button_graphdone = tk.Button(text="Press when graph completed", command=self.graph_entered)
def setup_random(self):
self.master.change_frame(RandomGraphFrame)
def setup_nodes(self): # Method allows user input for nodes, neighbours and distances
self.run_alg.grid_forget() # Hide button that began graph entry
self.run_random.grid_forget() # # Hide button that gave option for random generation graph
enter_source = tk.Label(self, text="Enter the source node: ")
enter_neighbour = tk.Label(self, text="Enter its neighbour node: ")
enter_distance = tk.Label(self, text="Enter int value for distance from source to neighbour: ")
enter_source.grid(row=1)
enter_neighbour.grid(row=3)
enter_distance.grid(row=5)
self.entry_source.grid(row=2, column=0)
self.entry_neighbour.grid(row=4, column=0)
self.entry_distance.grid(row=6)
self.button_addnode.grid(row=0, column=1)
self.button_graphdone.grid(row=5, column=5)
def add_nodes(self, source, neighbour, distance): # Method to add validated nodes to graph variable
# Creates nested dict for both source and neighbour to add
# As distance a-b = b-a so when one entered
# Other can be automatically entered as well
if source not in self.graph_nodes.keys():
self.graph_nodes[source] = {}
self.graph_nodes[source][neighbour] = distance
self.graph_nodes[neighbour] = {}
self.graph_nodes[neighbour][source] = distance
elif neighbour not in self.graph_nodes.keys():
self.graph_nodes[neighbour] = {}
self.graph_nodes[source][neighbour] = distance
self.graph_nodes[neighbour][source] = distance
else:
self.graph_nodes[source][neighbour] = distance
self.graph_nodes[neighbour][source] = distance
tk.messagebox.showinfo("Current graph", '{}'.format(self.graph_nodes)) # Displays graph user entered thus far
self.entry_neighbour.delete(0, "end") # Clear entry for neighbour
self.entry_distance.delete(0, "end") # Clear entry for distance
def graph_entered(self): # Method changes frame to DisplayAlgorithm Frame
if len(self.graph_nodes) > 2:
tk.messagebox.showinfo("Completed graph", '{}'.format(self.graph_nodes)) # display completed graph
print(self.graph_nodes)
self.master.change_frame(AlgorithmFrame) # change frame
self.button_graphdone.grid_forget() # Hide button
else:
tk.messagebox.showerror('Graph Invalid', 'Please ensure it has at least two unique nodes') # catch graph error
def validate_nodes(self): # Method to validate user inputs
is_valid = False
try:
if (self.distance.get() >=0) and (len(self.source_node.get()) > 0) and (len(self.neighbour.get()) > 0):
is_valid = True
except tk.TclError: # when non double data type entered error raised
self.entry_distance.delete(0, 'end')
if is_valid:
self.add_nodes(self.source_node.get(), self.neighbour.get(), self.distance.get())
else:
tk.messagebox.showinfo("Invalid distance", "Invalid input detected, please ensure no fields are blank "
"and that distance is greater than or equal to 0")
self.entry_distance.delete(0, 'end')
class AlgorithmFrame(tk.Frame): # Frame to display results of algorithm
def __init__(self, parent):
tk.Frame.__init__(self, parent)
import Dijks as dk # Dijkstra module imported
self.graph = dk.Dijkstra() # Graph class called using composition
self.button_display_sp = tk.Button(self, text="Press to apply Dijkstra's Algorithm to graph",
command=self.check_startend)
self.button_display_sp.grid(row=1, column=4)
# Button closes application , only defined here but placed on frame after algorithm is run
self.quitapp = tk.Button(self, text="Press to quit application", command=self.end_program)
self.label_start_node = tk.Label(self, text="Enter start node: ") # labels for start node
self.label_end_node = tk.Label(self, text="Enter end node: ") # labels for end node
self.entry_start = tk.Entry(self) # entry for start node
self.entry_end = tk.Entry(self) # entry for end node
self.label_start_node.grid(row=0, sticky=tk.E)
self.label_end_node.grid(row=1, sticky=tk.E)
self.entry_start.grid(row=0, column=1)
self.entry_end.grid(row=1, column=1)
def display_data(self): # Method displays result of algorithm and allows user to exit
# Hide buttons previously on frame
# Not destroyed as user may wish to restart program
self.button_display_sp.grid_forget()
self.label_end_node.grid_forget()
self.label_start_node.grid_forget()
self.entry_end.grid_forget()
self.entry_start.grid_forget()
# Displays user graph , shortest path and the distance of shortest path
label_graph = tk.Label(self, text="User graph: "
""+'{}'.format(self.master.frames[EnterGraphFrame].graph_nodes))
label_sp = tk.Label(self, text="A shortest path is through : " + '{}'.format(self.graph.shortest_path))
label_distance = tk.Label(self, text="With distance: "'{}'.format(self.graph.distance_node[self.graph.end]))
label_graph.grid(row=0, sticky=tk.E)
label_sp.grid(row=1, sticky=tk.E)
label_distance.grid(row=2, sticky=tk.E)
self.quitapp.grid(row=4, sticky=tk.NSEW) # Adds button that exits program.
def check_startend(self): # Method to check userinputs for start end nodes
# user entered graph is fetched from previous frame
self.graph.nodes = self.master.frames[EnterGraphFrame].graph_nodes
# checks to ensure start and end node are in user graph
if (self.entry_start.get() not in self.graph.nodes.keys()) or \
(self.entry_end.get() not in self.graph.nodes.keys()): # if invalid
tk.messagebox.showerror("Invalid start/end node","Please enter a valid start/end node")
self.entry_start.delete(0, 'end') # clear start node entry
self.entry_end.delete(0, 'end') # clear end node entry
else:
self.apply_algorithm() # if valid then apply algorithm
def apply_algorithm(self): # Method apply Dijkstra algorithm to user graph
self.graph.start = self.entry_start.get() # user input fetched for start node
self.graph.end = self.entry_end.get() # user input fetched for end node
self.graph.calc_shortest_path() # apply algorithm given graph
self.display_data() # Call method to display result of algorithm
def end_program(self): # Method when called ends program
root.destroy()
if __name__ == '__main__':
root = tk.Tk() # Initialising GUI and running it
gui = SetupFrames(root).grid()
root.mainloop()