From 19bdcff7bc07cfdb09004702a85d0cc391822631 Mon Sep 17 00:00:00 2001 From: Corey Ropell Date: Tue, 9 Apr 2019 22:46:29 -0400 Subject: [PATCH 1/5] Merged from Develop for end of Sprint02 Implemented Lesson Creation, Lessons Page, and other functionalities. For more information view Sprint02 Stories on our ZenHub board: https://app.zenhub.com/workspaces/smip-5c5dd5323547567638dbb48a/board?repos=169785349 --- .gitignore | 2 + gui/Drawer.py | 467 ++++++++++++++++-- gui/LessonPage.py | 55 ++- gui/Main.py | 42 +- gui/ReferenceWindow.py | 29 +- gui/Utilities.py | 18 + lesson_files/Another One.xlsx | Bin 0 -> 9257 bytes lesson_files/Another Sample.xlsx | Bin 0 -> 9199 bytes {gui => lesson_files/Base_code}/Sample1.s | 0 lesson_files/Sample Lesson.xlsx | Bin 0 -> 9208 bytes lesson_files/Submissions/(Practice).s | 57 +++ .../Submissions/Another Sample(Submission).s | 9 +- .../Submissions/Sample Lesson(Submission).s | 23 + lesson_files/Submissions/profile.pickle | Bin 0 -> 134 bytes lessons/Lesson.py | 4 +- lessons/Lesson_Transition.py | 70 +++ lessons/Lesson_Workbook.py | 124 +++++ lessons/Submission.py | 6 +- pyspim/__pycache__/pyspim.cpython-35.pyc | Bin 4672 -> 0 bytes pyspim/__pycache__/pyspim.cpython-36.pyc | Bin 4332 -> 0 bytes test/gui/.cache/v/cache/lastfailed | 3 + test/gui/Drawer.py | 109 ++++ test/lessons/.cache/v/cache/lastfailed | 1 + test/lessons/Lesson_Transition.py | 55 +++ test/lessons/Lesson_Workbook.py | 34 ++ 25 files changed, 1014 insertions(+), 94 deletions(-) create mode 100644 lesson_files/Another One.xlsx create mode 100644 lesson_files/Another Sample.xlsx rename {gui => lesson_files/Base_code}/Sample1.s (100%) create mode 100644 lesson_files/Sample Lesson.xlsx create mode 100644 lesson_files/Submissions/(Practice).s rename gui/input.s => lesson_files/Submissions/Another Sample(Submission).s (88%) create mode 100644 lesson_files/Submissions/Sample Lesson(Submission).s create mode 100644 lesson_files/Submissions/profile.pickle create mode 100644 lessons/Lesson_Transition.py create mode 100644 lessons/Lesson_Workbook.py delete mode 100644 pyspim/__pycache__/pyspim.cpython-35.pyc delete mode 100644 pyspim/__pycache__/pyspim.cpython-36.pyc create mode 100644 test/gui/.cache/v/cache/lastfailed create mode 100644 test/gui/Drawer.py create mode 100644 test/lessons/.cache/v/cache/lastfailed create mode 100644 test/lessons/Lesson_Transition.py create mode 100644 test/lessons/Lesson_Workbook.py diff --git a/.gitignore b/.gitignore index 6ddeb29..d727651 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ gui/__pycache__/ pyspim/\.DS_Store \.DS_Store + +pyspim/__pycache__/ diff --git a/gui/Drawer.py b/gui/Drawer.py index b49ae68..30fc154 100644 --- a/gui/Drawer.py +++ b/gui/Drawer.py @@ -1,15 +1,23 @@ import tkinter as tk -from tkinter import font -from tkinter import messagebox +from tkinter import font, messagebox, Menu +from lessons.Lesson_Transition import get_next_lesson, get_previous_lesson, append_new_lesson, set_current_lesson_index from gui.ReferenceWindow import draw_reference -from gui.LessonPage import submit_code -from gui.Utilities import transfer_to +from gui.LessonPage import submit_code, get_text +from gui.Utilities import transfer_to, get_relative_file_path, get_path +from lessons.Lesson_Workbook import initialize_workbook +from lessons.Lesson_Transition import get_current_lesson +from .ReferenceWindow import Reference +import re +from lessons.Lesson_Transition import lessons SIDEBAR_COLUMN_WIDTH = 5 registers = [] -def draw_menu(root, ttk, next_lesson): +def draw_menu(root, ttk): + # Resize in case window has been adjusted + if root.winfo_width() > 700: + root.minsize(700, root.winfo_screenheight()) # Set fonts for the menu widgets. # print(font.families()) to print available font families. menuLabel_font = font.Font(family="Loma", size=24, weight="bold") @@ -29,28 +37,47 @@ def draw_menu(root, ttk, next_lesson): label_banner = ttk.Label(main_frame, text='\tWelcome to SMIP.\n Your Best Friend for Learning MIPS ', style='green/black.TLabel', width=700, anchor="center") label_plug = ttk.Label(main_frame, style='textBox.TLabel', text=' Our repo: https://github.com/coreyrop/SMIP\n\t' - '-Last Updated: 02/20/2019-') + '-Last Updated: 03/26/2019-') label_banner.pack(pady=10) label_plug.pack(side="bottom", pady=5) + def lesson_validation(): + if lessons: + transfer_to( + lambda: draw_lesson(root, ttk, get_current_lesson()), + main_frame) + pass + button1 = ttk.Button(main_frame, text='Start', style='green/black.TButton', - command=lambda: transfer_to( - lambda: draw_lesson(root, ttk, next_lesson, submit_code, messagebox.showinfo), main_frame)) - button2 = ttk.Button(main_frame, text='Select Lesson', style='green/black.TButton') - button3 = ttk.Button(main_frame, text='Practice', style='green/black.TButton') - button4 = ttk.Button(main_frame, text='Reference', style='green/black.TButton', command=draw_reference) + command=lesson_validation) + + button2 = ttk.Button(main_frame, text='Select Lesson', style='green/black.TButton', + command=lambda: transfer_to(lambda: draw_lesson_select(root, ttk), main_frame)) + button3 = ttk.Button(main_frame, text='Practice', style='green/black.TButton', command=lambda: transfer_to( + lambda: draw_practice(root, ttk), main_frame)) + button4 = ttk.Button(main_frame, text='Reference', style='green/black.TButton', + command=lambda: draw_reference(Reference.local_file.value, + '/References/MIPS_Green_Sheet.pdf')) + create_lesson_button = ttk.Button(main_frame, text='Create Lesson', style='green/black.TButton', + command=lambda: transfer_to(lambda: draw_create_lessons_form(root, ttk), + main_frame)) button5 = ttk.Button(main_frame, text='Exit', style='green/black.TButton', command=quit) button1.pack(pady=30) button2.pack(pady=30) button3.pack(pady=30) button4.pack(pady=30) + create_lesson_button.pack(pady=30) button5.pack(pady=30) pass -def draw_lesson(root, ttk, lesson, submit_function, hint_function): +def draw_lesson(root, ttk, lesson): + # Resize page. + if root.winfo_width() < 875: + root.minsize(875, root.winfo_screenheight()) + # Set fonts for the menu widgets. # print(font.families()) to print available font families. menuLabel_font = font.Font(family="Loma", size=22, weight="bold") @@ -61,42 +88,72 @@ def draw_lesson(root, ttk, lesson, submit_function, hint_function): lesson_header = tk.Frame(master=root, bg="medium blue") center_frame = tk.Frame(master=root, bg="medium blue") - bottom_frame_top = tk.Frame(master=root, bg="medium blue") - bottom_frame_bottom = tk.Frame(master=root, bg="medium blue") - register_frame = tk.Frame(root, width=200, bg='white', height=500, relief='sunken', borderwidth=2) + register_frame = tk.Frame(root, width=200, bg='medium blue', height=500, relief='sunken', borderwidth=2) + + # TODO Need to properly gridify this page. + # This is a temp fix for functionality. + tk.Grid.columnconfigure(lesson_header, 0, weight=1) + tk.Grid.rowconfigure(lesson_header, 0, weight=1) + tk.Grid.columnconfigure(center_frame, 0, weight=1) + tk.Grid.rowconfigure(center_frame, 1, weight=1) registers = [] draw_sidebar(register_frame, registers) # Pack lesson_header Frame over the top of the center_frame. - register_frame.pack(expand=True, fill='both', side='left') - lesson_header.pack(fill="x") - center_frame.pack(expand=True, fill="both") - bottom_frame_top.pack(expand=True, fill="both") - bottom_frame_bottom.pack(expand=True, fill="both", side="bottom") + register_frame.grid(row=0, column=1, columnspan=1, sticky='w') + lesson_header.grid(row=0, column=0) + center_frame.grid(row=1, column=0, columnspan=2) - label_instruction = ttk.Label(center_frame, text=lesson.lesson_prompt, style='B_DO1.TLabel') - lesson_input = ttk.Entry(master=center_frame, font=menuLabel_font) - lesson_input = tk.Text(center_frame, height=30, width=100) - lesson_input.insert(tk.END, lesson.code_base) + label_instruction = ttk.Label(lesson_header, text=lesson.lesson_prompt, style='B_DO1.TLabel') + lesson_input = tk.Text(lesson_header, height=30, width=100) + lesson_input.insert(tk.END, get_text(lesson)) - label_instruction.pack(side="top", pady=5) - lesson_input.pack(pady=20, padx=10) + label_instruction.grid(pady=5, sticky='n') + lesson_input.grid(pady=20, padx=10) - menu_escape = ttk.Button(bottom_frame_top, text='Main Menu', style='B_DO1.TButton', cursor="target", - command=lambda: transfer_to(lambda: draw_menu(root, ttk, lesson), center_frame, - bottom_frame_top, bottom_frame_bottom, register_frame)) - hint_button = ttk.Button(bottom_frame_bottom, text='Hint', style='B_DO1.TButton', - cursor="target", command=lambda: hint_function("Hint", lesson.lesson_hint)) - reference_button = ttk.Button(bottom_frame_bottom, text='Reference', style='B_DO1.TButton', - cursor="target", command=draw_reference) - submit_button = ttk.Button(bottom_frame_top, text='Submit Code', style='B_DO1.TButton', - cursor="target", command=lambda: submit_function(lesson_input, registers, lesson)) + hints = ''.join([str(i + 1) + '. ' + lesson.lesson_hint[i] + '\n\n' for i in range(len(lesson.lesson_hint))]) - menu_escape.pack(side='left', padx=10) - submit_button.pack(side='right', padx=10) - hint_button.pack(side='left', padx=10) - reference_button.pack(side='right', padx=10) + menu_escape = ttk.Button(center_frame, text='Main Menu', style='B_DO1.TButton', cursor="target", + command=lambda: transfer_to(lambda: draw_menu(root, ttk), center_frame, + register_frame)) + hint_button = ttk.Button(center_frame, text='Hint', style='B_DO1.TButton', + cursor="target", command=lambda: messagebox.showinfo("Hint", hints)) + reference_button = ttk.Button(center_frame, text='Reference', style='B_DO1.TButton', + cursor="target") + + popup_reference = Menu(root, tearoff=0, bg='#f27446', font=20) + + for reference in lesson.lesson_reference: + popup_reference.add_command(label=reference['Name'], + command=lambda r=reference: draw_reference(r['Type'], r['Path'])) + + def do_popup_ref(event): + # display the popup menu + popup_reference.tk_popup(event.x_root, event.y_root, 0) + + reference_button.bind("", do_popup_ref) + + submit_button = ttk.Button(center_frame, text='Submit Code', style='B_DO1.TButton', + cursor="target", command=lambda: submit_code(lesson_input, registers, lesson)) + previous_lesson_button = ttk.Button(center_frame, text='Previous Lesson', style='B_DO1.TButton', + cursor='target', command=lambda: transfer_to(lambda: draw_lesson(root, ttk, + get_previous_lesson()), + center_frame, + register_frame)) + + next_lesson_button = ttk.Button(center_frame, text='Next Lesson', style='B_DO1.TButton', + cursor='target', command=lambda: transfer_to(lambda: draw_lesson(root, ttk, + get_next_lesson()), + center_frame, + register_frame)) + + menu_escape.grid(row=4, column=0, padx=20, pady=20) + submit_button.grid(row=4, column=2, padx=20) + previous_lesson_button.grid(row=5, column=0, padx=20, pady=10) + next_lesson_button.grid(row=5, column=2, padx=20) + hint_button.grid(row=4, column=1, padx=20, pady=20) + reference_button.grid(row=5, column=1, padx=20) pass @@ -106,7 +163,7 @@ def draw_sidebar(sidebar, registers): tk.Label(sidebar, text="VALUE", width=SIDEBAR_COLUMN_WIDTH).grid(row=0, column=2) for i in range(32): - label = tk.Label(sidebar, text="0", width="5") + label = tk.Label(sidebar, text="undef", width="5") label.grid(row=i + 1, column=2) registers.append(label) # global arr so labels can be updated @@ -145,3 +202,333 @@ def draw_sidebar(sidebar, registers): for i in range(32): tk.Label(sidebar, text=i, width=SIDEBAR_COLUMN_WIDTH).grid(row=i + 1, column=1) pass + + +def draw_create_lessons_form(root, ttk): + # Need extra room because we have 3 rows of info. + root.minsize(900, root.winfo_screenheight()) + # Cover the whole screen with the frame. + main_frame = tk.Frame(root, bg='medium blue', width=root.winfo_width(), height=root.winfo_height()) + # Fill the frame with the background. + main_frame.pack(expand=True, fill="both") + # Include separate font choices for human readable text. + menuButton_font = font.Font(family="Loma", size=22, weight="bold") + create_field_font = font.Font(family="Loma", size=20, weight="normal") + create_button_font = font.Font(family="Loma", size=18, weight="bold") + register_label_font = font.Font(family="Latin Modern Roman", size=14, weight="bold") + register_entry_font = font.Font(family="Latin Modern Roman", size=14, weight="normal") + # Apply style settings. + ttk.Style().configure('B_DO1.TLabel', foreground='black', background='DarkOrange1', width=20, + font=create_field_font, anchor="CENTER") + ttk.Style().configure('B_DO1.TButton', foreground='black', background='DarkOrange1', font=create_button_font, + width=15) + ttk.Style().configure('menu_buttons.TButton', foreground='black', background='DarkOrange1', font=menuButton_font, + width=15, padx=5) + ttk.Style().configure('references.TLabel', foreground='black', background='DarkOrange1', width=12, + font=create_field_font, anchor="CENTER") + ttk.Style().configure('TMenuButton', background='DarkOrange1') + + # Define register fields. + register_fields = {i: {} for i in range(32)} + + references = [] + + # Make an "added reference dropdown" which reflects added references. + str_var = tk.StringVar(root) + included_references = ['None'] + str_var.set('None') + reference_menu = tk.OptionMenu(main_frame, str_var, *included_references) + + lesson_title_label = ttk.Label(main_frame, text='Lesson Title', style='B_DO1.TLabel') + lesson_prompt_label = ttk.Label(main_frame, text='Lesson Prompt', style='B_DO1.TLabel') + lesson_hint_label = ttk.Label(main_frame, text='Lesson Hint', style='B_DO1.TLabel') + lesson_filepath_label = ttk.Label(main_frame, text='Relative File Path', style='B_DO1.TLabel') + reference_menu_label = ttk.Label(main_frame, text='References', style='references.TLabel') + + for i in register_fields.keys(): + register_fields[i]['label'] = ttk.Label(main_frame, text='$r' + str(i) + ' ', font=register_label_font) + + lesson_title_label.grid(row=0, column=0, pady=10, padx=20) + lesson_prompt_label.grid(row=1, column=0, pady=10) + lesson_hint_label.grid(row=2, column=0, pady=10) + lesson_filepath_label.grid(row=3, column=0, pady=20) + reference_menu_label.grid(row=0, column=2) + reference_menu.grid(row=1, column=2) + + for i in register_fields.keys(): + if i < 16: + if i > 9: + new_text = register_fields[i]['label'].cget("text") + register_fields[i]['label'] = ttk.Label(main_frame, text=new_text[:4], font=register_label_font) + register_fields[i]['label'].grid(row=i + 4, column=1, sticky='w', padx=10) + else: + new_text = register_fields[i]['label'].cget("text") + register_fields[i]['label'] = ttk.Label(main_frame, text=new_text[:4], font=register_label_font) + register_fields[i]['label'].grid(row=i - 16 + 4, column=1, stick='e', padx=3) + + lesson_title_entry = ttk.Entry(main_frame, font=create_button_font) + lesson_prompt_entry = ttk.Entry(main_frame, font=create_button_font) + lesson_hint_entry = ttk.Entry(main_frame, font=create_button_font) + lesson_filepath_current_label = ttk.Label(main_frame, text='None Set', font=create_field_font, width=9, + anchor='center') + for i in register_fields.keys(): + register_fields[i]['entry'] = ttk.Entry(main_frame, font=register_entry_font) + + lesson_title_entry.grid(row=0, column=1) + lesson_prompt_entry.grid(row=1, column=1) + lesson_hint_entry.grid(row=2, column=1) + lesson_filepath_current_label.grid(row=3, column=1) + for i in register_fields.keys(): + if i < 16: + register_fields[i]['entry'].grid(row=i + 4, column=0, sticky='e', padx=3) + else: + register_fields[i]['entry'].grid(row=i - 16 + 4, column=2, padx=3) + + lesson_filepath_button = ttk.Button(main_frame, text='Select', cursor='target', style='B_DO1.TButton', + command=lambda: lesson_filepath_current_label.config( + text=get_relative_file_path([('MIPS code files', '*.s')]))) + lesson_filepath_button.grid(row=3, column=2) + + main_menu_button = ttk.Button(main_frame, text='Main Menu', cursor='target', style='menu_buttons.TButton', + command=lambda: transfer_to(lambda: draw_menu(root, ttk), main_frame)) + + def submit_confirmation(): + if bool(lesson_title_entry.get() and not lesson_title_entry.get().isspace()) and bool( + lesson_prompt_entry.get() and not lesson_prompt_entry.get().isspace()) and bool( + lesson_hint_entry.get() and not lesson_hint_entry.get().isspace()) and all( + [re.match('[+-]?\d', register_fields[i]['entry'].get()) is not None for i in register_fields.keys() if + not register_fields[i]['entry'].get().isspace() and register_fields[i]['entry'].get()]): + + append_new_lesson(initialize_workbook('/lesson_files/' + lesson_title_entry.get(), + lesson_title=lesson_title_entry.get(), + lesson_prompt=lesson_prompt_entry.get(), + lesson_hint=lesson_hint_entry.get(), + lesson_filepath=lesson_filepath_current_label + ['text'], + registers={ + j: register_fields[j]['entry'].get() for j in + register_fields.keys()}, references=references)) + + lesson_title_entry.delete(0, 'end') + lesson_prompt_entry.delete(0, 'end') + lesson_hint_entry.delete(0, 'end') + lesson_filepath_current_label.config(text='None Set') + + included_references.clear() + reference_menu['menu'].delete(0, 'end') + reference_menu['menu'].add_command(label='None', command=lambda: str_var.set('None')) + + for i in register_fields.keys(): + register_fields[i]['entry'].delete(0, 'end') + + # Make a lesson created user alert here, then grid forget. + # NOTICE: It is forgotten, NOT destroyed. + alert = tk.Label(main_frame, background='green2', text='Lesson Created!', font=menuButton_font) + else: + alert = tk.Label(main_frame, background='red2', text='Invalid Values', font=menuButton_font) + + alert.grid(row=40, column=1) + # Root must be updated for changes to be displayed. + root.update() + # Modest sleep time of about 3 seconds, for user to take notice. + alert.after(500, alert.grid_forget) + pass + + # This part is just to make the button string more accessible. + # It might be deleted later. + create_lesson_str = tk.StringVar() + create_lesson_str.set('Create Lesson') + submit_lesson_button = ttk.Button(main_frame, text=create_lesson_str.get(), cursor='target', + style='menu_buttons.TButton', + command=submit_confirmation) + + def submit_ref(ref, dict, win): + ref.append(dict) + included_references.append(dict['Name']) + reference_menu['menu'].add_command(label=dict['Name'], command=lambda value=dict['Name']: str_var.set(value)) + win.destroy() + pass + + def add_pdf_reference_form(references): + + win = tk.Toplevel() + win.wm_title("Add PDF Reference") + + name_label = tk.Label(win, text="Reference Name") + name_label.grid(row=0, column=0) + + name_entry = tk.Entry(win, font=create_field_font) + name_entry.grid(row=0, column=1) + + current_path_label = tk.Label(win, text='None Set', font=create_field_font) + select_file_button = ttk.Button(win, text="Select PDF", command=lambda: current_path_label.config( + text=get_relative_file_path([('PDF files', '*.pdf')]))) + select_file_button.grid(row=1, column=0) + current_path_label.grid(row=1, column=1) + + submit_button = tk.Button(win, text='Add Reference', command=lambda: submit_ref(references, + {'Name': name_entry.get(), + 'Type': 'local_file', + 'Path': current_path_label[ + 'text']}, win)) + submit_button.grid(row=2, column=0) + pass + + def add_link_reference_form(references): + + win = tk.Toplevel() + win.wm_title("Add Link Reference") + + name_label = tk.Label(win, text="Reference Name") + name_label.grid(row=0, column=0) + + name_entry = tk.Entry(win, font=create_field_font) + name_entry.grid(row=0, column=1) + + url_label = tk.Label(win, text='URL') + url_entry = tk.Entry(win, font=create_field_font) + url_label.grid(row=1, column=0) + url_entry.grid(row=1, column=1) + + submit_button = tk.Button(win, text='Add Reference', + command=lambda: submit_ref(references, {'Name': name_entry.get(), 'Type': 'web_link', + 'Path': url_entry.get()}, + win)) + submit_button.grid(row=2, column=0) + pass + + popup = Menu(root, tearoff=0, bg='#f27446', font=20) + popup.add_command(label='PDF', command=lambda: add_pdf_reference_form(references)) + popup.add_command(label='Link', command=lambda: add_link_reference_form(references)) + + reference_menu_button = ttk.Button(main_frame, text='Add a Reference', cursor='target', + style='menu_buttons.TButton') + reference_menu_button.bind("", lambda event: popup.tk_popup(event.x_root, event.y_root, 0)) + + reference_menu_button.grid(row=2, column=2, padx=10) + main_menu_button.grid(row=40, column=0, sticky='s', pady=15) + submit_lesson_button.grid(row=40, column=1, sticky='s', pady=15) + pass + + +def draw_practice(root, ttk): + # Set fonts for the menu widgets. + # print(font.families()) to print available font families. + + menuLabel_font = font.Font(family="Loma", size=22, weight="bold") + menuButton_font = font.Font(family="Loma", size=20, weight="normal") + # background="..." doesn't work... + ttk.Style().configure('B_DO1.TLabel', foreground='black', background='DarkOrange1', font=menuLabel_font) + ttk.Style().configure('B_DO1.TButton', foreground='black', background='DarkOrange1', font=menuButton_font, width=15) + + lesson_header = tk.Frame(master=root, bg="medium blue") + center_frame = tk.Frame(master=root, bg="medium blue") + bottom_frame_top = tk.Frame(master=root, bg="medium blue") + bottom_frame_bottom = tk.Frame(master=root, bg="medium blue") + register_frame = tk.Frame(root, width=200, bg='white', height=500, relief='sunken', borderwidth=2) + + registers = [] + draw_sidebar(register_frame, registers) + + # Pack lesson_header Frame over the top of the center_frame. + register_frame.pack(expand=True, fill='both', side='left') + lesson_header.pack(fill="x") + center_frame.pack(expand=True, fill="both") + bottom_frame_top.pack(expand=True, fill="both") + bottom_frame_bottom.pack(expand=True, fill="both", side="bottom") + + label_instruction = ttk.Label(center_frame, text="Time to Practice Some MIPS ", style='B_DO1.TLabel') + lesson_input = tk.Text(center_frame, height=30, width=100) + lesson_input.insert(tk.END, get_text(is_practice=True)) + + label_instruction.pack(side="top", pady=5) + lesson_input.pack(pady=20, padx=10) + lesson = "" + + menu_escape = ttk.Button(bottom_frame_top, text='Main Menu', style='B_DO1.TButton', cursor="target", + command=lambda: transfer_to(lambda: draw_menu(root, ttk), center_frame, + bottom_frame_top, bottom_frame_bottom, register_frame)) + + reference_button = ttk.Button(bottom_frame_bottom, text='Reference', style='B_DO1.TButton', + cursor="target", command=draw_reference) + run_button = ttk.Button(bottom_frame_top, text='Run Code', style='B_DO1.TButton', + cursor="target", command=lambda: submit_code(lesson_input, registers, is_practice=True)) + menu_escape.pack(side='left', padx=10) + run_button.pack(side='right', padx=10) + reference_button.pack(padx=10) + pass + + +def draw_lesson_select(root, ttk): + main_frame = tk.Frame(root, bg='medium blue', width=root.winfo_width(), height=root.winfo_height()) + main_frame.pack(expand=True, fill="both") + + scframe = VerticalScrolledFrame(main_frame) + scframe.pack() + + def lesson_selected(i): + set_current_lesson_index(i) + transfer_to(lambda: draw_lesson(root, ttk, lessons[i]), main_frame) + pass + + for i in range(len(lessons)): + color = lambda i=i: 'green' if lessons[i].lesson_completed else 'red' + btn = tk.Button(scframe.interior, height=1, width=20, relief=tk.FLAT, + bg="gray99", fg=color(i), + font="Dosis", text=lessons[i].lesson_title, command=lambda i=i: lesson_selected(i)) + + btn.pack(padx=10, pady=5, side=tk.TOP) + main_menu_button = ttk.Button(main_frame, text='Main Menu', cursor='target', style='menu_buttons.TButton', + command=lambda: transfer_to(lambda: draw_menu(root, ttk), main_frame)) + main_menu_button.pack() + pass + + +# tkinter hates scrollbars with buttons so I grabbed this solution from stackoverflow +# https://stackoverflow.com/questions/31762698/dynamic-button-with-scrollbar-in-tkinter-python +class VerticalScrolledFrame(tk.Frame): + """A pure Tkinter scrollable frame that actually works! + + * Use the 'interior' attribute to place widgets inside the scrollable frame + * Construct and pack/place/grid normally + * This frame only allows vertical scrolling + """ + + def __init__(self, parent, *args, **kw): + tk.Frame.__init__(self, parent, *args, **kw) + + # create a canvas object and a vertical scrollbar for scrolling it + vscrollbar = tk.Scrollbar(self, orient=tk.VERTICAL) + vscrollbar.pack(fill=tk.Y, side=tk.RIGHT, expand=tk.FALSE) + canvas = tk.Canvas(self, bd=0, highlightthickness=0, + yscrollcommand=vscrollbar.set) + canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE) + vscrollbar.config(command=canvas.yview) + + # reset the view + canvas.xview_moveto(0) + canvas.yview_moveto(0) + + # create a frame inside the canvas which will be scrolled with it + self.interior = interior = tk.Frame(canvas) + interior_id = canvas.create_window(0, 0, window=interior, + anchor=tk.NW) + + # track changes to the canvas and frame width and sync them, + # also updating the scrollbar + def _configure_interior(event): + # update the scrollbars to match the size of the inner frame + size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) + canvas.config(scrollregion="0 0 %s %s" % size) + if interior.winfo_reqwidth() != canvas.winfo_width(): + # update the canvas's width to fit the inner frame + canvas.config(width=interior.winfo_reqwidth()) + + interior.bind('', _configure_interior) + + def _configure_canvas(event): + if interior.winfo_reqwidth() != canvas.winfo_width(): + # update the inner frame's width to fill the canvas + canvas.itemconfigure(interior_id, width=canvas.winfo_width()) + + canvas.bind('', _configure_canvas) diff --git a/gui/LessonPage.py b/gui/LessonPage.py index a1c1833..9957430 100644 --- a/gui/LessonPage.py +++ b/gui/LessonPage.py @@ -1,5 +1,7 @@ import tkinter as tk from lessons.Submission import run_MIPS +from lessons.Lesson_Transition import write_completed +from gui.Utilities import get_path """ Draws a lesson to the frame root: tkinter root to draw to @@ -7,27 +9,54 @@ lesson: the lesson to be drawn to the screen """ +practice_filename = get_path('/lesson_files/Submissions/(Practice).s') -def submit_code(user_input, register_labels, lesson): - f = open('input.s', 'w') + +def get_submission_file(lesson): + return get_path('/lesson_files/Submissions/'+lesson.lesson_title + '(Submission).s') + + +def submit_code(user_input, register_labels, lesson=None, is_practice=False): + if is_practice: + filename = practice_filename + else: + filename = get_submission_file(lesson) + + f = open(filename, 'w') f.write(user_input.get("1.0", tk.END)) f.close() - results = run_MIPS('input.s') + results = run_MIPS(filename) update_registers(results, register_labels) - if lesson.check_solution(results): - print('Passed!!') - else: - print('Failed!!') + if not is_practice: + if lesson.check_solution(results): + lesson.lesson_completed = True + write_completed(lesson.lesson_title, True) + print('Passed!!') + else: + lesson.lesson_completed = False + write_completed(lesson.lesson_title, False) + print('Failed!!') pass -def get_text(): - f = open('Sample1.s', 'r') - output = "" - for x in f.readlines(): - output += x +def get_text(lesson=None, is_practice=False): + if is_practice: + try: + f = open(practice_filename) + except FileNotFoundError: + return '# Welcome to Practice! Write some code!!' + else: + try: + filename = get_submission_file(lesson) + f = open(filename, 'r') + except FileNotFoundError: + try: + f = open(get_path(lesson.code_base), 'r') + except FileNotFoundError: + return "# No base code" + output = ''.join(line for line in f.readlines()) f.close() return output @@ -36,5 +65,3 @@ def update_registers(answers, register_labels): for register, value in answers.items(): register_labels[register].config(text=value) pass - - diff --git a/gui/Main.py b/gui/Main.py index abad396..0fa9656 100644 --- a/gui/Main.py +++ b/gui/Main.py @@ -1,28 +1,24 @@ import tkinter as tk from tkinter import ttk from gui.Drawer import draw_menu -from gui.LessonPage import get_text -from lessons.Lesson import Lesson -root = tk.Tk() -root.title("SMIP: The Student MIPS Instruction Program") +if __name__ == '__main__': + root = tk.Tk() + root.title("SMIP: The Student MIPS Instruction Program") + # Turning off pack propagate to prevent widgets from determining window size. + # Max and Min window sizes may change. + root.pack_propagate(0) + # Max size of window is the dimensions of their screen. + their_width = root.winfo_screenwidth() + their_height = root.winfo_screenheight() + root.maxsize(their_width, their_height) + # Buttons need room, so we need max height. + root.minsize(700, their_height) + # Put app at the center of the screen. + x = (their_width / 2) - (700 / 2) + y = (their_height / 2) - (800 / 2) + root.geometry("%dx%d+%d+%d" % (700, 800, x, y)) + root.configure(background='medium blue') + draw_menu(root, ttk) -sample_lesson = Lesson('Lesson 1: Register Addition'," Use the \'add\' instruction to set\n $t0 to 3,$t1 to 5,\n and then their sum in $t2.", - {8: 3, 9: 5, 10: 8}, " The add instruction expects a destination register followed by two source registers," - " which are comma separated.", get_text()) - -# Turning off pack propagate to prevent widgets from determining window size. -# Max and Min window sizes may change. -root.pack_propagate(0) -# Max size of window is the dimensions of their screen. -their_width = root.winfo_screenwidth() -their_height = root.winfo_screenheight() -root.maxsize(their_width, their_height) -root.minsize(700, 800) -# Put app at the center of the screen. -x = (their_width / 2) - (700 / 2) -y = (their_height / 2) - (800 / 2) -root.geometry("%dx%d+%d+%d" % (700, 800, x, y)) - -draw_menu(root, ttk, sample_lesson) -root.mainloop() + root.mainloop() diff --git a/gui/ReferenceWindow.py b/gui/ReferenceWindow.py index f1d3cc2..029980e 100644 --- a/gui/ReferenceWindow.py +++ b/gui/ReferenceWindow.py @@ -1,13 +1,14 @@ -import os, sys, subprocess +import os, sys, subprocess, webbrowser, enum +from gui.Utilities import get_path -def draw_reference(): - dirname = os.path.dirname(__file__) +class Reference(enum.Enum): + web_link = 'web_link' + local_file = 'local_file' - #Removing gui folder from the absolute path - dirname = dirname[:-3] - - filename = os.path.join(dirname,'References','MIPS_Green_Sheet.pdf') + +def draw_reference_path(file_path): + filename = get_path(file_path) if sys.platform == "win32": os.startfile(filename) @@ -15,4 +16,16 @@ def draw_reference(): opener = "open" if sys.platform == "darwin" else "xdg-open" subprocess.call([opener, filename]) - pass \ No newline at end of file + pass + + +def draw_reference_link(file_link): + webbrowser.open(file_link) + + +def draw_reference(type, path): + if Reference[type].value == Reference.local_file.value: + draw_reference_path(file_path = path) + elif Reference[type].value == Reference.web_link.value: + draw_reference_link(file_link = path) + diff --git a/gui/Utilities.py b/gui/Utilities.py index 6c30ee7..f01c0eb 100644 --- a/gui/Utilities.py +++ b/gui/Utilities.py @@ -1,3 +1,11 @@ +from os.path import relpath +from tkinter.filedialog import askopenfilename +from pathlib import Path + + +top_path = str(Path().absolute())[:str(Path().absolute()).rfind('SMIP')+len('SMIP')] + + """ Destroys all content inside of the given frame frame: frame who's content will be destroyed @@ -10,6 +18,16 @@ def destroy_content(frame): pass +def get_relative_file_path(filetypes): + try: + s = askopenfilename(filetypes=filetypes) + return '/'+relpath(s, top_path) + except ValueError: + return 'None Set' + + +def get_path(relpath): + return top_path + relpath """ Destroys the given frames content and then draws new content to that frame using the given function diff --git a/lesson_files/Another One.xlsx b/lesson_files/Another One.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e1fc3d09375bb8f3654b696ed16255227a78a868 GIT binary patch literal 9257 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%0AUSGb8)aer*sQ1p6V+$BS=Q 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 literal 0 HcmV?d00001 diff --git a/lesson_files/Another Sample.xlsx b/lesson_files/Another Sample.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bd9cfbb0c19f6415e33503c28122aa56e7be157c GIT binary patch literal 9199 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 literal 0 HcmV?d00001 diff --git a/gui/Sample1.s b/lesson_files/Base_code/Sample1.s similarity index 100% rename from gui/Sample1.s rename to lesson_files/Base_code/Sample1.s diff --git a/lesson_files/Sample Lesson.xlsx b/lesson_files/Sample Lesson.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..bda5ecaba40f805cfd02330690279eea27c13f48 GIT binary patch literal 9208 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 Hzy11u3?&0T literal 0 HcmV?d00001 diff --git a/lesson_files/Submissions/(Practice).s b/lesson_files/Submissions/(Practice).s new file mode 100644 index 0000000..305e8e5 --- /dev/null +++ b/lesson_files/Submissions/(Practice).s @@ -0,0 +1,57 @@ +# Welcome to Practice! Write some code!! +.globl main +.globl init_values +.globl end_program +.globl print_value + +# $t0 = $s5 +# $s5 += $t0 +# $s5 += $t0 +# $s5 += $t0 +# $s5 += $t0 +# $s5 += $t0 +# +# $s4 = $s5 + $s3 +# $s4 -= $s2 + +.text +init_values: + addi $s4, $0, 4 + addi $s5, $0, 4 + addi $s3, $0, 4 + addi $s2, $0, 4 + jr $ra + add $0, $0, $0 + +main: + jal init_values + add $0, $0, $0 + + add $t0, $s5, $0 + add $s5, $s5, $t0 + add $s5, $s5, $t0 + add $s5, $s5, $t0 + add $s5, $s5, $t0 + add $s5, $s5, $t0 + + add $s4, $s5, $s3 + + sub $s4, $s4, $s2 + + jal print_value + add $0, $0, $0 + j end_program + add $0, $0, $0 + +print_value: + addi $v0, $0, 1 + add $a0, $s4, $0 + syscall + jr $ra + add $0, $0, $0 + +end_program: + addi $v0, $0, 10 + syscall + + diff --git a/gui/input.s b/lesson_files/Submissions/Another Sample(Submission).s similarity index 88% rename from gui/input.s rename to lesson_files/Submissions/Another Sample(Submission).s index df48157..c0f3644 100644 --- a/gui/input.s +++ b/lesson_files/Submissions/Another Sample(Submission).s @@ -12,11 +12,12 @@ main: # your code here - addi $t0, $0, 3 - addi $t1, $0, 5 - add $t4, $t0, $t1 - + addi $s0, $0, 0 # All memory structures are placed after the # .data assembler directive .data + + + + diff --git a/lesson_files/Submissions/Sample Lesson(Submission).s b/lesson_files/Submissions/Sample Lesson(Submission).s new file mode 100644 index 0000000..7155507 --- /dev/null +++ b/lesson_files/Submissions/Sample Lesson(Submission).s @@ -0,0 +1,23 @@ +# "Hello World" in MIPS assembly +# From: http://labs.cs.upt.ro/labs/so2/html/resources/nachos-doc/mipsf.html + + # All program code is placed after the + # .text assembler directive + .text + + # Declare main as a global function + .globl main + +# The label 'main' represents the starting point +main: + + # your code here + addi $a3, $0, 5 + + # All memory structures are placed after the + # .data assembler directive + .data + + + + diff --git a/lesson_files/Submissions/profile.pickle b/lesson_files/Submissions/profile.pickle new file mode 100644 index 0000000000000000000000000000000000000000..5ca5614a4541756ec05a16d3a753c2c1a22da2ce GIT binary patch literal 134 zcmZo*t}SHHh+tu0V5m$@Eh%K|h~NdXeNu~y^Yavf6LSl4QVW?n!GaK;0z|Hmxg&xb zsM0Ynza%5INWniZwUDJFf)7aus++X~Yn3sc}Y{NoHwTMsgv0XQ>_l D>ue^F literal 0 HcmV?d00001 diff --git a/lessons/Lesson.py b/lessons/Lesson.py index beab7df..d6212f6 100644 --- a/lessons/Lesson.py +++ b/lessons/Lesson.py @@ -7,12 +7,14 @@ class Lesson: answer: Dict that stores the necessary registers as keys and the correct final values as their value mapping """ - def __init__(self, title, prompt, answer, hint, base_code): + def __init__(self, title, prompt, answer, hint, reference, base_code, completed): self.lesson_prompt = prompt self.lesson_hint = hint + self.lesson_reference = reference self.lesson_title = title self.lesson_answer = answer self.code_base = base_code + self.lesson_completed = completed pass """ diff --git a/lessons/Lesson_Transition.py b/lessons/Lesson_Transition.py new file mode 100644 index 0000000..79082c2 --- /dev/null +++ b/lessons/Lesson_Transition.py @@ -0,0 +1,70 @@ +import pickle +lesson_index = 0 +lessons = [] +filename = '../lesson_files/Submissions/profile.pickle' + + +def set_current_lesson_index(i): + global lesson_index + if i > -1: + lesson_index = i + pass + + +def get_current_lesson(): + if lessons: + return lessons[lesson_index] + pass + + +def get_next_lesson(): + if lessons: + global lesson_index + if lesson_index + 1 < len(lessons): + lesson_index += 1 + return lessons[lesson_index] + else: + return lessons[lesson_index] + + +def get_previous_lesson(): + global lesson_index + if lesson_index - 1 > -1: + lesson_index -= 1 + return lessons[lesson_index] + else: + return lessons[lesson_index] + + +def append_new_lesson(new_lesson): + lessons.append(new_lesson) + pass + + +def read_from_pickle(filename): + dict = {} + try: + with open(filename, 'rb') as file: + dict = pickle.load(file) + except FileNotFoundError: + return dict + return dict + + +dict = read_from_pickle(filename) + + +def write_completed(title, bool): + dict[title] = bool + with open(filename, 'wb') as file: + pickle.dump(dict, file) + pass + + +def init_lessons(): + from lessons.Lesson_Workbook import load_lessons + global lessons + lessons = load_lessons() + + +init_lessons() \ No newline at end of file diff --git a/lessons/Lesson_Workbook.py b/lessons/Lesson_Workbook.py new file mode 100644 index 0000000..50e2519 --- /dev/null +++ b/lessons/Lesson_Workbook.py @@ -0,0 +1,124 @@ +from openpyxl import Workbook, load_workbook +from openpyxl.styles import PatternFill, Font +from .Lesson_Transition import dict +from .Lesson import Lesson +from gui.Utilities import get_path +import re +import os + + +def get_register_index(register_num, get_value_index=True): + if get_value_index: + if register_num < 16: + return 'D' + str(register_num+1) + else: + return 'F' + str(register_num+1 - 16) + else: + if register_num < 16: + return 'C' + str(register_num+1) + else: + return 'E' + str(register_num+1 - 16) + + +def initialize_workbook(filename, **kwargs): + filename = get_path(filename) + book = Workbook() + sheet = book.active + + sheet.column_dimensions['A'].width = 50 + sheet.column_dimensions['B'].width = 50 + sheet.column_dimensions['G'].width = 50 + sheet.column_dimensions['H'].width = 50 + sheet.column_dimensions['I'].width = 50 + + greenFill = PatternFill(start_color='00FF00', end_color='00FF00', fill_type='solid') + cyanFill = PatternFill(start_color='00FFFF', end_color='00FFFF', fill_type='solid') + labelFont = Font(name='Calibri', size=14, bold=True) + valueFont = Font(name='Calibri', size=14) + + sheet['A1'] = 'Lesson Title' + sheet['A2'] = 'Lesson Prompt' + sheet['A3'] = 'Lesson Hint' + sheet['A4'] = 'Base Code Relative File Path' + sheet['B1'] = kwargs['lesson_title'] + sheet['B2'] = kwargs['lesson_prompt'] + sheet['B3'] = kwargs['lesson_hint'] + sheet['B4'] = kwargs['lesson_filepath'] + + sheet['A1'].fill = greenFill + sheet['A2'].fill = greenFill + sheet['A3'].fill = greenFill + sheet['A4'].fill = greenFill + sheet['A1'].font = labelFont + sheet['A2'].font = labelFont + sheet['A3'].font = labelFont + sheet['A4'].font = labelFont + + sheet['B1'].fill = cyanFill + sheet['B2'].fill = cyanFill + sheet['B3'].fill = cyanFill + sheet['B4'].fill = cyanFill + sheet['B1'].font = valueFont + sheet['B2'].font = valueFont + sheet['B3'].font = valueFont + sheet['B4'].font = valueFont + + for i in range(32): + sheet[get_register_index(i, False)] = '$r'+str(i) + sheet[get_register_index(i, False)].fill = greenFill + sheet[get_register_index(i, False)].font = labelFont + + sheet[get_register_index(i)] = kwargs['registers'][i] + sheet[get_register_index(i)].fill = cyanFill + sheet[get_register_index(i)].font = valueFont + + sheet['G1'] = 'Reference Name' + sheet['H1'] = 'Reference Type' + sheet['I1'] = 'Reference Path' + + sheet['G1'].fill = greenFill + sheet['H1'].fill = greenFill + sheet['I1'].fill = greenFill + sheet['G1'].font = labelFont + sheet['H1'].font = labelFont + sheet['I1'].font = labelFont + + index = 2 + for reference in kwargs.get('references', []): + sheet['G'+str(index)] = reference['Name'] + sheet['H'+str(index)] = reference['Type'] + sheet['I'+str(index)] = reference['Path'] + if reference['Type'] == 'web_link': + sheet['I'+str(index)].style = 'Hyperlink' + + sheet['G' + str(index)].fill = cyanFill + sheet['H' + str(index)].fill = cyanFill + sheet['I' + str(index)].fill = cyanFill + sheet['G' + str(index)].font = valueFont + sheet['H' + str(index)].font = valueFont + sheet['I' + str(index)].font = valueFont + index += 1 + + book.save(filename+'.xlsx') + return load_lesson_from_workbook(filename+'.xlsx') + + +def load_lesson_from_workbook(filename): + book = load_workbook(filename) + sheet = book.active + answer = {i: int(sheet[get_register_index(i)].value) for i in range(32) if + sheet[get_register_index(i)].value is not None and re.match('[+-]?\d', sheet[ + get_register_index(i)].value) is not None} + + references = [] + index = 2 + while sheet['G'+str(index)].value is not None: + references.append({'Name': sheet['G'+str(index)].value, 'Type': sheet['H'+str(index)].value, 'Path': sheet['I'+str(index)].value}) + index += 1 + return Lesson(sheet['B1'].value, sheet['B2'].value, answer, [sheet['B3'].value], references, sheet['B4'].value, dict.get(sheet['B1'].value, False)) + + +def load_lessons(): + lesson_path = get_path('/lesson_files/') + return [load_lesson_from_workbook(lesson_path + file) for file in os.listdir(lesson_path) if file.endswith('.xlsx')] + diff --git a/lessons/Submission.py b/lessons/Submission.py index 6b8bce8..1a64f44 100644 --- a/lessons/Submission.py +++ b/lessons/Submission.py @@ -1,10 +1,8 @@ from pyspim.pyspim import Spim -sp = Spim(debug = False) - def run_MIPS(filename): + sp = Spim(debug=False) sp.load(filename) sp.run() - - return {i: sp.reg(i) for i in range(32)} \ No newline at end of file + return {i: sp.reg(i) for i in range(32)} diff --git a/pyspim/__pycache__/pyspim.cpython-35.pyc b/pyspim/__pycache__/pyspim.cpython-35.pyc deleted file mode 100644 index a75992ba4d94a86bc2beaea5a342900924c82b86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4672 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 SQfkVD+|ty~jZ^Mv*Zdb Date: Wed, 10 Apr 2019 11:15:46 -0400 Subject: [PATCH 2/5] Bug Fix: Lesson Creation Validation When doing last minute testing found a bug in the way we validate inputs on the lesson creation form. Would not catch cases where invalid characters followed numbers. Changed implementation to check that all characters are numbers. This bug managed to get through all previous tests we did and that is why the fix is happening here. --- gui/Drawer.py | 4 ++-- lesson_files/Submissions/(Practice).s | 1 + lessons/Lesson_Workbook.py | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gui/Drawer.py b/gui/Drawer.py index 30fc154..2c1232f 100644 --- a/gui/Drawer.py +++ b/gui/Drawer.py @@ -7,8 +7,8 @@ from lessons.Lesson_Workbook import initialize_workbook from lessons.Lesson_Transition import get_current_lesson from .ReferenceWindow import Reference -import re from lessons.Lesson_Transition import lessons +from string import digits SIDEBAR_COLUMN_WIDTH = 5 registers = [] @@ -296,7 +296,7 @@ def submit_confirmation(): if bool(lesson_title_entry.get() and not lesson_title_entry.get().isspace()) and bool( lesson_prompt_entry.get() and not lesson_prompt_entry.get().isspace()) and bool( lesson_hint_entry.get() and not lesson_hint_entry.get().isspace()) and all( - [re.match('[+-]?\d', register_fields[i]['entry'].get()) is not None for i in register_fields.keys() if + [all(c in digits for c in register_fields[i]['entry'].get()) for i in register_fields.keys() if not register_fields[i]['entry'].get().isspace() and register_fields[i]['entry'].get()]): append_new_lesson(initialize_workbook('/lesson_files/' + lesson_title_entry.get(), diff --git a/lesson_files/Submissions/(Practice).s b/lesson_files/Submissions/(Practice).s index 305e8e5..c123b17 100644 --- a/lesson_files/Submissions/(Practice).s +++ b/lesson_files/Submissions/(Practice).s @@ -55,3 +55,4 @@ end_program: syscall + diff --git a/lessons/Lesson_Workbook.py b/lessons/Lesson_Workbook.py index 50e2519..0743266 100644 --- a/lessons/Lesson_Workbook.py +++ b/lessons/Lesson_Workbook.py @@ -3,8 +3,8 @@ from .Lesson_Transition import dict from .Lesson import Lesson from gui.Utilities import get_path -import re import os +from string import digits def get_register_index(register_num, get_value_index=True): @@ -107,8 +107,8 @@ def load_lesson_from_workbook(filename): book = load_workbook(filename) sheet = book.active answer = {i: int(sheet[get_register_index(i)].value) for i in range(32) if - sheet[get_register_index(i)].value is not None and re.match('[+-]?\d', sheet[ - get_register_index(i)].value) is not None} + sheet[get_register_index(i)].value is not None and all(c in digits for c in sheet[ + get_register_index(i)].value)} references = [] index = 2 From 472b556fb6bec153746b1f11c5e2d42b5b342b44 Mon Sep 17 00:00:00 2001 From: Corey Ropell Date: Wed, 10 Apr 2019 11:15:46 -0400 Subject: [PATCH 3/5] Bug Fix: Lesson Creation Validation When doing last minute testing found a bug in the way we validate inputs on the lesson creation form. Would not catch cases where invalid characters followed numbers. Changed implementation to check that all characters are numbers. This bug managed to get through all previous tests we did and that is why the fix is happening here. --- gui/Drawer.py | 4 ++-- lesson_files/Submissions/(Practice).s | 1 + lessons/Lesson_Workbook.py | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gui/Drawer.py b/gui/Drawer.py index 30fc154..a10f499 100644 --- a/gui/Drawer.py +++ b/gui/Drawer.py @@ -7,8 +7,8 @@ from lessons.Lesson_Workbook import initialize_workbook from lessons.Lesson_Transition import get_current_lesson from .ReferenceWindow import Reference -import re from lessons.Lesson_Transition import lessons +from string import digits SIDEBAR_COLUMN_WIDTH = 5 registers = [] @@ -296,7 +296,7 @@ def submit_confirmation(): if bool(lesson_title_entry.get() and not lesson_title_entry.get().isspace()) and bool( lesson_prompt_entry.get() and not lesson_prompt_entry.get().isspace()) and bool( lesson_hint_entry.get() and not lesson_hint_entry.get().isspace()) and all( - [re.match('[+-]?\d', register_fields[i]['entry'].get()) is not None for i in register_fields.keys() if + [all(c in digits for c in register_fields[i]['entry'].get() if not (c is register_fields[i]['entry'].get()[0] and (c == '-' or c == '+'))) for i in register_fields.keys() if not register_fields[i]['entry'].get().isspace() and register_fields[i]['entry'].get()]): append_new_lesson(initialize_workbook('/lesson_files/' + lesson_title_entry.get(), diff --git a/lesson_files/Submissions/(Practice).s b/lesson_files/Submissions/(Practice).s index 305e8e5..c123b17 100644 --- a/lesson_files/Submissions/(Practice).s +++ b/lesson_files/Submissions/(Practice).s @@ -55,3 +55,4 @@ end_program: syscall + diff --git a/lessons/Lesson_Workbook.py b/lessons/Lesson_Workbook.py index 50e2519..935da9f 100644 --- a/lessons/Lesson_Workbook.py +++ b/lessons/Lesson_Workbook.py @@ -3,8 +3,8 @@ from .Lesson_Transition import dict from .Lesson import Lesson from gui.Utilities import get_path -import re import os +from string import digits def get_register_index(register_num, get_value_index=True): @@ -107,8 +107,8 @@ def load_lesson_from_workbook(filename): book = load_workbook(filename) sheet = book.active answer = {i: int(sheet[get_register_index(i)].value) for i in range(32) if - sheet[get_register_index(i)].value is not None and re.match('[+-]?\d', sheet[ - get_register_index(i)].value) is not None} + sheet[get_register_index(i)].value is not None and all(c in digits for c in sheet[ + get_register_index(i)].value if not(c is sheet[get_register_index(i)].value[0] and(c == '-' or c == '+')))} references = [] index = 2 From 4026ad4433591e7716ec258607d36286dc30e1f4 Mon Sep 17 00:00:00 2001 From: Christian Rowe-Slack <26044605+cprowesl@users.noreply.github.com> Date: Mon, 15 Apr 2019 15:19:01 -0400 Subject: [PATCH 4/5] Updated README for class activity I updated the README so that another group can fork this repo, and run our acceptance tests. --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fbb924c..9dd353c 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,19 @@ We are working to create an educational program that will be used to help students learn how to program in MIPS code. We are using the pyspim code base to run spim through python. -ZenHub: https://app.zenhub.com/workspaces/smip-5c5dd5323547567638dbb48a/boards?repos=169785349 \ No newline at end of file +# OS REQUIREMENTS: +-Linux +-MacOS +(Windows unsupported at this time). + +# IDE REQUIREMENTS: +Pycharm Community 2018 (Highly Recommended unless serious CLI experience): +https://www.jetbrains.com/pycharm/ +Python Interpreter 3.5+ + +# INSTRUCTIONS: +- Open forked repo in IDE. +- Run Main.py found in /gui/Main.py. + +# ASSOCIATED SCRUMBOARD: +ZenHub: https://app.zenhub.com/workspaces/smip-5c5dd5323547567638dbb48a/boards?repos=169785349 From 33cf0d375a429edce1a0ce90934293423adb5a9b Mon Sep 17 00:00:00 2001 From: Matthew Gibbs Date: Mon, 22 Apr 2019 11:45:30 -0400 Subject: [PATCH 5/5] Made changes to lesson prompt. This issue has been triaged but I've Included notes on how to make a more effective solution. The best solution that I've included is making a custom ValidationEntry and MaxLengthValidation class that will perform validation depending on developer/client needs. All of the other textboxes require similar changes which is why this option is the best (will require the least changes and result in the least duplication of code. Please let me know if you have any questions. --- gui/Drawer.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/gui/Drawer.py b/gui/Drawer.py index a10f499..6eee136 100644 --- a/gui/Drawer.py +++ b/gui/Drawer.py @@ -267,7 +267,17 @@ def draw_create_lessons_form(root, ttk): register_fields[i]['label'].grid(row=i - 16 + 4, column=1, stick='e', padx=3) lesson_title_entry = ttk.Entry(main_frame, font=create_button_font) - lesson_prompt_entry = ttk.Entry(main_frame, font=create_button_font) + + #TODO MYG + # My Solution for prompt size + MAX_ENTRY_SIZE = 20 + entry_text = tk.StringVar() + lesson_prompt_entry = ttk.Entry(main_frame, font=create_button_font, width=20, textvariable=entry_text) + entry_text.set(lesson_prompt_entry.get()[:MAX_ENTRY_SIZE]) + + # Source of implemented solution: + # https://stackoverflow.com/questions/16373887/how-to-set-the-text-value-content-of-an-entry-widget-using-a-button-in-tkinter + lesson_hint_entry = ttk.Entry(main_frame, font=create_button_font) lesson_filepath_current_label = ttk.Label(main_frame, text='None Set', font=create_field_font, width=9, anchor='center') @@ -293,6 +303,23 @@ def draw_create_lessons_form(root, ttk): command=lambda: transfer_to(lambda: draw_menu(root, ttk), main_frame)) def submit_confirmation(): + + #TODO: MYG + # Truncate entry + # truncate_entry_size(lesson_prompt_entry, 50) + + # Perform validation. + # if not validate_entry_size(lesson_prompt_entry.get(), 50): + # alert = tk.Label(main_frame, background='red2', text='Invalid Values', font=menuButton_font) + + # Other options: + # Option A: StringVar() + # v = StringVar() + # e = Entry(master, textvariable=v) + # e.pack() + # Option B: Use validate attribute OR Trigger validation alert when prompt text passes width. + # https://stackoverflow.com/questions/18741078/ttk-entry-widget-validate-entry-invalid-text-entry-does-not-cause-reverting + if bool(lesson_title_entry.get() and not lesson_title_entry.get().isspace()) and bool( lesson_prompt_entry.get() and not lesson_prompt_entry.get().isspace()) and bool( lesson_hint_entry.get() and not lesson_hint_entry.get().isspace()) and all( @@ -410,6 +437,30 @@ def add_link_reference_form(references): submit_lesson_button.grid(row=40, column=1, sticky='s', pady=15) pass + #TODO MYG + # def truncate_entry_size(entry, size): + # """ + # Takes a TTK Entry object and truncates it. + # :param entry: A Entry object. + # :param size: Number of characters to limit + # :return: The truncated Entry object. + # """ + # str_var = tk.StringVar() + # text = entry.get() + # if len(entry.get()) > size: + # entry.set(text[:size]) + # return entry + + # def validate_entry_size(entry, size): + # """ + # Takes a TTK Entry object and returns if it is the valid size. + # :param entry: A Entry object. + # :param size: Valid number of characters. + # :return: Returns bool value of whether the Entry text is valid. + # """ + # value = entry.get() + # return len(value) <= size + def draw_practice(root, ttk): # Set fonts for the menu widgets. @@ -532,3 +583,45 @@ def _configure_canvas(event): canvas.itemconfigure(interior_id, width=canvas.winfo_width()) canvas.bind('', _configure_canvas) + + +#TODO: MYG +# class ValidatingEntry(tk.Entry): +# # base class for validating entry widgets +# +# def __init__(self, master, value="", **kw): +# apply(Entry.__init__, (self, master), kw) +# self.__value = value +# self.__variable = tk.StringVar() +# self.__variable.set(value) +# self.__variable.trace("w", self.__callback) +# self.config(textvariable=self.__variable) +# +# def __callback(self, *dummy): +# value = self.__variable.get() +# newvalue = self.validate(value) +# if newvalue is None: +# self.__variable.set(self.__value) +# elif newvalue != value: +# self.__value = newvalue +# self.__variable.set(self.newvalue) +# else: +# self.__value = value +# +# def validate(self, value): +# # override: return value, new value, or None if invalid +# return value +# +# class MaxLengthEntry(ValidatingEntry): +# +# def __init__(self, master, value="", maxlength=None, **kw): +# self.maxlength = maxlength +# apply(ValidatingEntry.__init__, (self, master), kw) +# +# def validate(self, value): +# if self.maxlength is None or len(value) <= self.maxlength: +# return value +# return None # new value too long +# Useful References: +# https://stackoverflow.com/questions/4140437/interactively-validating-entry-widget-content-in-tkinter +# https://mail.python.org/pipermail/tkinter-discuss/2006-August/000863.html