From 6f3605c4509ba7a3e410c609878e604269412c3d Mon Sep 17 00:00:00 2001 From: DerGrumpf Date: Thu, 9 Jan 2025 22:33:04 +0100 Subject: [PATCH] Changed: Switched ImGUI implementation --- assets/Student_list.csv | 1 + assets/Student_list.xlsx | Bin 10602 -> 10735 bytes assets/test.db | Bin 49152 -> 0 bytes class_editor.py | 5 +- database_editor.py | 261 +++++++++++ demo_docking.py | 926 +++++++++++++++++++++++++++++++++++++++ gui.py | 66 +-- lecture_editor.py | 3 +- main_menu.py | 18 +- student_editor.py | 3 +- student_graph.py | 2 +- student_info.py | 117 ++--- student_list.py | 3 +- student_ranking.py | 2 +- submission_editor.py | 3 +- test.db | Bin 49152 -> 49152 bytes view.py | 74 +--- 17 files changed, 1308 insertions(+), 176 deletions(-) delete mode 100644 assets/test.db create mode 100644 database_editor.py create mode 100644 demo_docking.py diff --git a/assets/Student_list.csv b/assets/Student_list.csv index e05d8f5..064b71e 100644 --- a/assets/Student_list.csv +++ b/assets/Student_list.csv @@ -1,6 +1,7 @@ First Name,Last Name,Sex,Tutorial 1,Tutorial 2,Extended Applications,Numpy & MatPlotLib,SciPy,Monte Carlo,Pandas & Seaborn,Folium Abdalaziz,Abunjaila,Male,30.5,15,18,28,17,17,17,22 Marleen,Adolphi,Female,29.5,15,18,32,19,0,17,24 +Skofiare,Berisha,Female,29.5,13,18,34,20,17,20,26 Aurela,Brahimi,Female,17.5,15,15.5,26,16,17,19,16 Cam Thu,Do,Female,31,15,18,34,19,20,21.5,22 Nova,Eib,Female,31,15,15,34,20,20,21,27 diff --git a/assets/Student_list.xlsx b/assets/Student_list.xlsx index 403425cb77c49d9d24c0c5127f0e2ca42578c502..25d18a9de4ccf483d4264fb2a2fa66130c8933b8 100644 GIT binary patch delta 4390 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 nLa2!FU0beKRyiU-*AK$+L-mvb% diff --git a/assets/test.db b/assets/test.db deleted file mode 100644 index b29087fab36b46af642e8102a9b8569c49cb7f32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49152 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 diff --git a/class_editor.py b/class_editor.py index de1d775..1751ea3 100644 --- a/class_editor.py +++ b/class_editor.py @@ -1,5 +1,4 @@ -import imgui - +from imgui_bundle import imgui, imgui_ctx from model import * class ClassEditor: @@ -11,7 +10,7 @@ class ClassEditor: def __call__(self): classes = Class.select() - with imgui.begin("Class Editor", False, imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + with imgui_ctx.begin("Class Editor"): imgui.text("Add Class") _, self.add_name = imgui.input_text(" ", self.add_name) diff --git a/database_editor.py b/database_editor.py new file mode 100644 index 0000000..78c76a4 --- /dev/null +++ b/database_editor.py @@ -0,0 +1,261 @@ +from imgui_bundle import imgui, imgui_ctx, ImVec2 +from model import * +import random + +class DatabaseEditor: + def __init__(self): + super().__init__() + self.add_name = str() + self.select_class = 0 + self.select_lecture = 0 + self.select_student = 0 + self.select_submission = 0 + + self.class_name = str() + self.add_class_name = str() + + self.student_prename = str() + self.student_surname = str() + self.student_sex = False + self.add_student_prename = str() + self.add_student_surname = str() + self.add_student_sex = False + + self.lecture_title = str() + self.lecture_points = 0 + self.add_lecture_title = str() + self.add_lecture_points = 0 + + self.submission_points = 0.0 + self.add_submission_lecture = 0 + self.add_submission_points = 0.0 + + def content_list(self, content: list, selector: int, id: str, height: int) -> int: + w = imgui.get_window_size().x + with imgui_ctx.begin_child(str(id), ImVec2(w*0.3, height)): + for n, c in enumerate(content, start = 1): + _, clicked = imgui.selectable(f"{n}. {c}", selector == n-1) + if clicked: + selector = n-1 + return selector + + def class_editor(self): + w, h = imgui.get_window_size() + classes = Class.select() + content = [f"{c.name}" for c in classes] + self.select_class = self.content_list(content, self.select_class, "class_content", h*0.15) + imgui.same_line() + + with imgui_ctx.begin_child("Class", ImVec2(w*0.25, h*0.15)): + imgui.text("Edit Class") + _, self.class_name = imgui.input_text_with_hint("##class_edit1", content[self.select_class], self.class_name) + + if imgui.button("Update"): + id = classes[self.select_class].id + Class.update(name=self.class_name).where(Class.id == id).execute() + + imgui.same_line() + + if imgui.button("Delete"): + id = classes[self.select_class].id + students = Student.select().where(Student.class_id == id) + for student in students: + Submission.delete().where(Submission.student_id == student.id).execute() + Student.delete().where(Student.class_id == id).execute() + Lecture.delete().where(Lecture.class_id == id).execute() + Class.delete().where(Class.id == id).execute() + + + imgui.separator() + + imgui.text("Add Class") + _, self.add_class_name = imgui.input_text_with_hint("##class_edit2", "Class Name", self.add_class_name) + + if imgui.button("Add"): + Class.create(name=self.add_class_name) + + return classes[self.select_class].id + + def student_editor(self, class_id: int): + w, h = imgui.get_window_size() + students = Student.select().where(Student.class_id == class_id) + content = [f"{s.prename} {s.surname}" for s in students] + self.select_student = self.content_list(content, self.select_student, "student_content", h*0.45) + imgui.same_line() + + with imgui_ctx.begin_child("Student", ImVec2(w*0.25, h*0.4)): + imgui.text("Edit Student") + + prename = students[self.select_student].prename + _, self.student_prename = imgui.input_text_with_hint("##student_edit1", prename, self.student_prename) + + surname = students[self.select_student].surname + _, self.student_surname = imgui.input_text_with_hint("##student_edit2", surname, self.student_surname) + + if imgui.radio_button("Male##1", not self.student_sex): + self.student_sex = not self.student_sex + imgui.same_line() + if imgui.radio_button("Female##1", self.student_sex): + self.student_sex = not self.student_sex + + if imgui.button("Update"): + Student.update( + prename = self.student_prename, + surname = self.student_surname, + sex = "Female" if self.student_sex else "Male" + ).where(Student.id == students[self.select_student].id).execute() + + imgui.same_line() + + if imgui.button("Delete"): + id = students[self.select_student].id + Student.delete().where(Student.id == id).execute() + Submission.delete().where(Submission.student_id == id).execute() + self.select_student = 0 + + imgui.separator() + + imgui.text("Add Student") + _, self.add_student_prename = imgui.input_text_with_hint("##student_edit3", "First Name", self.add_student_prename) + _, self.add_student_surname = imgui.input_text_with_hint("##student_edit4", "Last Name", self.add_student_surname) + + if imgui.radio_button("Male##2", not self.add_student_sex): + self.add_student_sex = not self.add_student_sex + imgui.same_line() + if imgui.radio_button("Female##2", self.add_student_sex): + self.add_student_sex = not self.add_student_sex + + if imgui.button("Add"): + Student.create( + prename=self.add_student_prename, + surname=self.add_student_surname, + sex="Female" if self.add_student_sex else "Male", + class_id=class_id + ) + self.add_student_prename = str() + self.add_student_surname = str() + self.add_student_sex = False + + return students[self.select_student].id + + def lecture_editor(self, class_id: int): + w, h = imgui.get_window_size() + lectures = Lecture.select().where(Lecture.class_id == class_id) + content = [f"{l.title}" for l in lectures] + self.select_lecture = self.content_list(content, self.select_lecture, "lecture_content", h*0.15) + imgui.same_line() + + with imgui_ctx.begin_child("Lecture", ImVec2(w*0.25, h*0.15)): + imgui.text("Edit Lecture") + _, self.lecture_title = imgui.input_text_with_hint("##lecture_edit1", content[self.select_lecture], self.lecture_title) + _, self.lecture_points = imgui.input_int("##lecture_points1", self.lecture_points) + if self.lecture_points < 0: + self.lecture_points = 0 + + if imgui.button("Update"): + Lecture.update(title=self.lecture_title, points=self.lecture_points).where(Lecture.id == lectures[self.select_lecture].id).execute() + + imgui.same_line() + + if imgui.button("Delete"): + id = lectures[self.select_lecture].id + Submission.delete().where(Submission.lecture_id == id).execute() + Lecture.delete().where(Lecture.id == id).execute() + + imgui.separator() + + imgui.text("Add Lecture") + _, self.add_lecture_title = imgui.input_text_with_hint("##lecture_edit2", "Lecture Title", self.add_lecture_title) + _, self.add_lecture_points = imgui.input_int("##lecture_points2", self.add_lecture_points) + if self.add_lecture_points < 0: + self.add_lecture_points = 0 + + if imgui.button("Add"): + Lecture.create(title=self.add_lecture_title, points=self.add_lecture_points, class_id=class_id) + + return lectures[self.select_lecture].id + + def submission_editor(self, student_id: int): + w, h = imgui.get_window_size() + submissions = Submission.select().where(Submission.student_id == student_id) + lectures = [Lecture.get_by_id(sub.lecture_id) for sub in submissions] + content = [l.title for l in lectures] + self.select_submission = self.content_list(content, self.select_submission, "submission_content", h*0.2) + imgui.same_line() + + with imgui_ctx.begin_child("Submission", ImVec2(w*0.25, h*0.2)): + imgui.text("Edit Submission") + imgui.text(content[self.select_submission]) + + points = submissions[self.select_submission].points + if points.is_integer(): + points = int(points) + + max_points = lectures[self.select_submission].points + + _, self.submission_points = imgui.input_float(f"{points}/{max_points}", self.submission_points, 0.5, 10, "%.1f") + if self.submission_points < 0: + self.submission_points = 0 + + if imgui.button("Update"): + Submission.update(points=self.submission_points).where(Submission.id == submissions[self.select_submission].id).execute() + + imgui.same_line() + + if imgui.button("Delete"): + Submission.delete().where(Submission.id == submissions[self.select_submission].id).execute() + + imgui.separator() + + imgui.text("Add Submission") + available_lectures = Lecture.select().where(Lecture.class_id == Student.get_by_id(student_id).class_id) + combo_items = [l.title for l in available_lectures] + _, self.add_submission_lecture = imgui.combo("##lecture_combo", self.add_submission_lecture, combo_items, len(combo_items)) + _, self.add_submission_points = imgui.input_float("##lecture_title", self.add_submission_points, 0.5, 10, "%.1f") + if self.add_submission_points < 0: + self.add_submission_points = 0 + + if imgui.button("Add"): + Submission.create( + points=self.add_submission_points, + lecture_id=available_lectures[self.add_submission_lecture].id, + student_id=student_id + ) + + return submissions[self.select_submission].id + + def __call__(self): + with imgui_ctx.begin("Database Editor"): + class_id = self.class_editor() + imgui.separator() + self.lecture_editor(class_id) + imgui.separator() + student_id = self.student_editor(class_id) + imgui.separator() + self.submission_editor(student_id) + return + classes = Class.select() + + with imgui_ctx.begin("Database Editor"): + imgui.text("Add Class") + + _, self.add_name = imgui.input_text(" ", self.add_name) + + if imgui.button("Add"): + if self.add_name: + Class.create(name=self.add_name) + self.add_name = str() + + imgui.separator() + + if not classes: + imgui.text("No Dataset could be queried") + return + + for n, c in enumerate(classes, start=1): + display = f"{n}. {c.name}" + opened, _ = imgui.selectable(display, self.select == n-1) + if opened: + self.select = n-1 + + return classes[self.select] diff --git a/demo_docking.py b/demo_docking.py new file mode 100644 index 0000000..1be190e --- /dev/null +++ b/demo_docking.py @@ -0,0 +1,926 @@ +# A more complex app demo +# +# It demonstrates how to: +# - set up a complex docking layouts (with several possible layouts): +# - load additional fonts, possibly colored, and with emojis +# - display a log window +# - use the status bar +# - use default menus (App and view menu), and how to customize them +# - use a specific application state (instead of using static variables) +# - save some additional user settings within imgui ini file +# - use borderless windows, that are movable and resizable +import json +from enum import Enum +import time + +from imgui_bundle import hello_imgui, icons_fontawesome_6, imgui, immapp, imgui_ctx, ImVec4, ImVec2 +from imgui_bundle.demos_python import demo_utils +from typing import List, Any + + +########################################################################## +# Our Application State +########################################################################## +class MyAppSettings: + motto: hello_imgui.InputTextData + value: int = 10 + + def __init__(self): + self.motto = hello_imgui.InputTextData( + "Hello, Dear ImGui\n" + "Unleash your creativity!\n", + True, # multiline + (14.0, 3.0) # initial size (in em) + ) + +class RocketState(Enum): + Init = 0 + Preparing = 1 + Launched = 2 + + +# Struct that holds the application's state +class AppState: + f: float + counter: int + rocket_progress: float + my_app_settings: MyAppSettings + rocket_state: RocketState + rocket_launch_time: float + + title_font: imgui.ImFont + color_font: imgui.ImFont + emoji_font: imgui.ImFont + large_icon_font: imgui.ImFont + + def __init__(self): + self.f = 0 + self.counter = 0 + self.rocket_progress = 0.0 + self.rocket_launch_time = 0.0 + self.my_app_settings = MyAppSettings() + self.rocket_state = RocketState.Init + + +########################################################################## +# Additional fonts handling +########################################################################## +def load_fonts(app_state: AppState): # This is called by runnerParams.callbacks.LoadAdditionalFonts + # First, load the default font (the default font should be loaded first) + # In this example, we instruct HelloImGui to use FontAwesome6 instead of FontAwesome4 + hello_imgui.get_runner_params().callbacks.default_icon_font = hello_imgui.DefaultIconFont.font_awesome6 + hello_imgui.imgui_default_settings.load_default_font_with_font_awesome_icons() + + # Load the title font + # app_state.title_font = hello_imgui.load_font("fonts/DroidSans.ttf", 18.0) + font_loading_params_title_icons = hello_imgui.FontLoadingParams() + font_loading_params_title_icons.merge_font_awesome = True + app_state.title_font = hello_imgui.load_font("fonts/Roboto/Roboto-BoldItalic.ttf", 18, font_loading_params_title_icons) + + # Load the emoji font + font_loading_params_emoji = hello_imgui.FontLoadingParams() + font_loading_params_emoji.use_full_glyph_range = True + app_state.emoji_font = hello_imgui.load_font("fonts/NotoEmoji-Regular.ttf", 24., font_loading_params_emoji) + + # Load a large icon font + font_loading_params_large_icon = hello_imgui.FontLoadingParams() + font_loading_params_large_icon.use_full_glyph_range = True + app_state.large_icon_font = hello_imgui.load_font("fonts/fontawesome-webfont.ttf", 24., font_loading_params_large_icon) + + # Load a colored font + font_loading_params_color = hello_imgui.FontLoadingParams() + font_loading_params_color.load_color = True + app_state.color_font = hello_imgui.load_font("fonts/Playbox/Playbox-FREE.otf", 24., font_loading_params_color) + + + +########################################################################## +# Save additional settings in the ini file +########################################################################## +# This demonstrates how to store additional info in the application settings +# Use this sparingly! +# This is provided as a convenience only, and it is not intended to store large quantities of text data. + +# Warning, the save/load function below are quite simplistic! +def my_app_settings_to_string(settings: MyAppSettings) -> str: + as_dict: dict[str, Any] = {} + as_dict["motto"] = hello_imgui.input_text_data_to_dict(settings.motto) + as_dict["value"] = settings.value + return json.dumps(as_dict) + + +def string_to_my_app_settings(s: str) -> MyAppSettings: + r = MyAppSettings() + try: + as_dict = json.loads(s) + r.motto = hello_imgui.input_text_data_from_dict(as_dict["motto"]) + r.value = as_dict["value"] + except Exception as e: + hello_imgui.log(hello_imgui.LogLevel.error, f"Error while loading user settings: {e}") + return r + + +def load_my_app_settings(app_state: AppState): + """ + Note: load_my_app_settings() and save_my_app_settings() will be called in the callbacks `post_init` & `before_exit` + runner_params.callbacks.post_init = lambda: load_user_settings(app_state) + runner_params.callbacks.before_exit = lambda: save_user_settings(app_state) + """ + app_state.my_app_settings = string_to_my_app_settings( + hello_imgui.load_user_pref("MyAppSettings") + ) + + +def save_my_app_settings(app_state: AppState): + hello_imgui.save_user_pref( + "MyAppSettings", my_app_settings_to_string(app_state.my_app_settings) + ) + + +########################################################################## +# Gui functions used in this demo +########################################################################## +@immapp.static(last_hide_time=1) +def demo_hide_window(app_state: AppState): + # Display a button that will hide the application window + imgui.push_font(app_state.title_font) + imgui.text("Hide app window") + imgui.pop_font() + + if imgui.button("Hide"): + demo_hide_window.last_hide_time = time.time() + hello_imgui.get_runner_params().app_window_params.hidden = True + if imgui.is_item_hovered(): + imgui.set_tooltip("By clicking this button, you can hide the window for 3 seconds.") + if demo_hide_window.last_hide_time > 0.0: + now = time.time() + if now - demo_hide_window.last_hide_time > 3.0: + demo_hide_window.last_hide_time = -1.0 + hello_imgui.get_runner_params().app_window_params.hidden = False + + +# Display a button that will add another dockable window during execution +def demo_show_additional_window(app_state: AppState): + # In order to add a dockable window during execution, you should use + # hello_imgui.add_dockable_window() + # Note: you should not modify manually the content of runnerParams.docking_params.dockable_windows + # (since HelloImGui is constantly looping on it) + + imgui.push_font(app_state.title_font) + imgui.text("Dynamically add window") + imgui.pop_font() + + window_name = "Additional Window" + if imgui.button("Show additional window"): + additional_window = hello_imgui.DockableWindow() + additional_window.label = window_name + additional_window.include_in_view_menu = False # this window is not shown in the view menu, + additional_window.remember_is_visible = False # its visibility is not saved in the settings file, + additional_window.dock_space_name = "MiscSpace" # when shown, it will appear in MiscSpace. + additional_window.gui_function = lambda: imgui.text("This is the additional window") + hello_imgui.add_dockable_window( + additional_window, + force_dockspace=False # means that the window will be docked to the last space it was docked to + # i.e. dock_space_name is ignored if the user previously moved the window to another space + ) + imgui.set_item_tooltip("By clicking this button, you can show an additional window") + + if imgui.button("Remove additional window"): + hello_imgui.remove_dockable_window(window_name) + imgui.set_item_tooltip("By clicking this button, you can remove the additional window") + + +def demo_basic_widgets(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Basic widgets demo") + imgui.pop_font() + + imgui.begin_group() + # Edit a float using a slider from 0.0 to 1.0 + changed, app_state.f = imgui.slider_float("float", app_state.f, 0.0, 1.0) + if changed: + hello_imgui.log( + hello_imgui.LogLevel.warning, f"state.f was changed to {app_state.f}" + ) + + # Buttons return true when clicked (most widgets return true when edited/activated) + if imgui.button("Button"): + app_state.counter += 1 + hello_imgui.log(hello_imgui.LogLevel.info, "Button was pressed") + imgui.same_line() + imgui.text(f"counter = {app_state.counter}") + imgui.end_group() + + if imgui.is_item_hovered(): + imgui.set_tooltip("These widgets will interact with the log window") + + +def demo_user_settings(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("User settings") + imgui.pop_font() + + imgui.begin_group() + + imgui.set_next_item_width(hello_imgui.em_size(7.0)) + _, app_state.my_app_settings.value = imgui.slider_int( + "Value", app_state.my_app_settings.value, 0, 100 + ) + + _ = hello_imgui.input_text_resizable("Motto", app_state.my_app_settings.motto) + imgui.text("(this text widget is resizable)") + + imgui.end_group() + if imgui.is_item_hovered(): + imgui.set_tooltip("The values below are stored in the application settings ini file and restored at startup") + + +def demo_rocket(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Rocket demo") + imgui.pop_font() + + imgui.begin_group() + if app_state.rocket_state == RocketState.Init: + if imgui.button(f"{icons_fontawesome_6.ICON_FA_ROCKET} Launch rocket"): + app_state.rocket_launch_time = time.time() + app_state.rocket_state = RocketState.Preparing + hello_imgui.log(hello_imgui.LogLevel.warning, "Rocket is being prepared") + elif app_state.rocket_state == RocketState.Preparing: + imgui.text("Please Wait") + app_state.rocket_progress = (time.time() - app_state.rocket_launch_time) / 3.0 + if app_state.rocket_progress >= 1.0: + app_state.rocket_state = RocketState.Launched + hello_imgui.log(hello_imgui.LogLevel.warning, "Rocket was launched") + elif app_state.rocket_state == RocketState.Launched: + imgui.text(f"{icons_fontawesome_6.ICON_FA_ROCKET} Rocket launched") + if imgui.button("Reset Rocket"): + app_state.rocket_state = RocketState.Init + app_state.rocket_progress = 0.0 + imgui.end_group() + if imgui.is_item_hovered(): + imgui.set_tooltip("Look at the status bar after clicking") + + +def demo_docking_flags(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Main dock space node flags") + imgui.pop_font() + imgui.text_wrapped( + """ +This will edit the ImGuiDockNodeFlags for "MainDockSpace". +Most flags are inherited by children dock spaces. + """ + ) + + class DockFlagWithInfo: + def __init__(self, flag, label, tip): + self.flag = flag + self.label = label + self.tip = tip + + all_flags = [ + DockFlagWithInfo( + imgui.DockNodeFlags_.no_docking_split, + "NoSplit", + "prevent Dock Nodes from being split", + ), + DockFlagWithInfo( + imgui.DockNodeFlags_.no_resize, + "NoResize", + "prevent Dock Nodes from being resized", + ), + DockFlagWithInfo( + imgui.DockNodeFlags_.auto_hide_tab_bar, + "AutoHideTabBar", + "show tab bar only if multiple windows\n" + + 'You will need to restore the layout after changing (Menu "View/Restore Layout")', + ), + DockFlagWithInfo( + imgui.DockNodeFlags_.no_docking_over_central_node, + "NoDockingInCentralNode", + "prevent docking in central node\n(only works with the main dock space)", + ), + # DockFlagWithInfo(imgui.DockNodeFlags_.passthru_central_node, "PassthruCentralNode", "advanced"), + ] + + main_dock_space_node_flags = ( + hello_imgui.get_runner_params().docking_params.main_dock_space_node_flags + ) + for flag_with_info in all_flags: + _, main_dock_space_node_flags = imgui.checkbox_flags( + flag_with_info.label, main_dock_space_node_flags, flag_with_info.flag + ) + if imgui.is_item_hovered(): + imgui.set_tooltip("%s" % flag_with_info.tip) + + hello_imgui.get_runner_params().docking_params.main_dock_space_node_flags = ( + main_dock_space_node_flags + ) + + +def gui_window_layout_customization(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Switch between layouts") + imgui.pop_font() + imgui.text('with the menu "View/Layouts"') + if imgui.is_item_hovered(): + imgui.set_tooltip( + "Each layout remembers separately the modifications applied by the user, \n" + + "and the selected layout is restored at startup" + ) + + imgui.separator() + + imgui.push_font(app_state.title_font) + imgui.text("Change the theme") + imgui.pop_font() + imgui.text('with the menu "View/Theme"') + if imgui.is_item_hovered(): + imgui.set_tooltip("The selected theme is remembered and restored at startup") + imgui.separator() + + demo_docking_flags(app_state) + imgui.separator() + + +def gui_window_alternative_theme(app_state: AppState): + # Since this window applies a theme, We need to call "imgui.begin" ourselves so + # that we can apply the theme before opening the window. + # + # In order to obtain this, we applied the following option to the window + # that displays this Gui: + # alternative_theme_window.call_begin_end = False + + # emulate C/C++ static variable: we will store some static variables + # as attributes of the function + statics = gui_window_alternative_theme + + # Apply the theme before opening the window + tweaked_theme = hello_imgui.ImGuiTweakedTheme() + tweaked_theme.theme = hello_imgui.ImGuiTheme_.white_is_white + tweaked_theme.tweaks.rounding = 0.0 + hello_imgui.push_tweaked_theme(tweaked_theme) + + # Open the window + window_opened = imgui.begin("Alternative Theme") + if window_opened: + # Display some widgets + imgui.push_font(app_state.title_font) + imgui.text("Alternative Theme") + imgui.pop_font() + imgui.text("This window uses a different theme") + imgui.set_item_tooltip(""" + tweaked_theme = hello_imgui.ImGuiTheme.ImGuiTweakedTheme() + tweaked_theme.theme = hello_imgui.ImGuiTheme_.white_is_white.value + tweaked_theme.tweaks.rounding = 0.0 + hello_imgui.apply_tweaked_theme(tweaked_theme) + """ + ) + + if imgui.collapsing_header("Basic Widgets", imgui.TreeNodeFlags_.default_open.value): + if not hasattr(statics, "checked"): + statics.checked = True + _, statics.checked = imgui.checkbox("Checkbox", statics.checked) + + if imgui.button("Button"): + hello_imgui.log(hello_imgui.LogLevel.info, "Button was pressed") + imgui.set_item_tooltip("This is a button") + + if not hasattr(statics, "radio"): + statics.radio = 0 + if imgui.radio_button("Radio 1", statics.radio == 0): + statics.radio = 0 + imgui.same_line() + if imgui.radio_button("Radio 2", statics.radio == 1): + statics.radio = 1 + imgui.same_line() + if imgui.radio_button("Radio 3", statics.radio == 2): + statics.radio = 2 + + # Haiku + # Display a image of the haiku below with Japanese characters + # with an informative tooltip + haiku_image_height = hello_imgui.em_size(5.0) + hello_imgui.image_from_asset("images/haiku.png", (0.0, haiku_image_height)) + imgui.set_item_tooltip(""" +Extract from Wikipedia +------------------------------------------------------------------------------- + +In early 1686, Bashō composed one of his best-remembered haiku: + + furu ike ya / kawazu tobikomu / mizu no oto + + an ancient pond / a frog jumps in / the splash of water + +This poem became instantly famous. + +------------------------------------------------------------------------------- + +This haiku is here rendered as an image, mainly to preserve space, +because adding a Japanese font to the project would enlarge its size. +Handling Japanese font is of course possible within ImGui / Hello ImGui! + """) + + # Display the haiku text as an InputTextMultiline + if not hasattr(statics, "poem"): + statics.poem = ( + " Old Pond\n" + " Frog Leaps In\n" + " Water's Sound\n" + "\n" + " Matsuo Bashō - 1686" + ) + + _, statics.poem = imgui.input_text_multiline("##Poem", statics.poem, hello_imgui.em_to_vec2(15.0, 5.5)) + + # a popup with a modal window + if imgui.button("Open Modal"): + imgui.open_popup("MyModal") + popup_opened, _ = imgui.begin_popup_modal("MyModal", None, imgui.WindowFlags_.always_auto_resize.value) + if popup_opened: + imgui.text("This is a modal window") + if imgui.button("Close"): + imgui.close_current_popup() + imgui.end_popup() + + if not hasattr(statics, "text"): + statics.text = "Hello, world!" + _, statics.text = imgui.input_text("Input text", statics.text) + + if imgui.tree_node("Text Display"): + imgui.text("Hello, world!") + imgui.text_colored((1.0, 0.5, 0.5, 1.0), "Some text") + imgui.text_disabled("Disabled text") + imgui.text_wrapped("This is a long text that will be wrapped in the window") + imgui.tree_pop() + + # Close the window + imgui.end() + + # Restore the theme + hello_imgui.pop_tweaked_theme() + + +def demo_assets(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Image From Assets") + imgui.pop_font() + hello_imgui.begin_group_column() + imgui.dummy(hello_imgui.em_to_vec2(0.0, 0.45)) + imgui.text("Hello") + hello_imgui.end_group_column() + hello_imgui.image_from_asset("images/world.png", hello_imgui.em_to_vec2(2.5, 2.5)) + + +def demo_fonts(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Fonts - " + icons_fontawesome_6.ICON_FA_ROCKET) + imgui.pop_font() + + imgui.text_wrapped("Mix icons " + icons_fontawesome_6.ICON_FA_FACE_SMILE + " and text " + icons_fontawesome_6.ICON_FA_ROCKET) + if imgui.is_item_hovered(): + imgui.set_tooltip("Example with Font Awesome Icons") + + imgui.text("Emojis") + + with imgui_ctx.begin_group(): + imgui.push_font(app_state.emoji_font) + imgui.text("✌❤🌴🚀") + imgui.pop_font() + + if imgui.is_item_hovered(): + imgui.set_tooltip("Example with NotoEmoji font") + + imgui.text("Colored Fonts") + imgui.push_font(app_state.color_font) + imgui.text("COLOR!") + imgui.pop_font() + if imgui.is_item_hovered(): + imgui.set_tooltip("Example with Playbox-FREE.otf font") + + +def demo_themes(app_state: AppState): + imgui.push_font(app_state.title_font) + imgui.text("Themes") + imgui.pop_font() + + tweaked_theme = hello_imgui.get_runner_params().imgui_window_params.tweaked_theme + + imgui.begin_group() + button_size = hello_imgui.em_to_vec2(7.0, 0.0) + if imgui.button("Cherry", button_size): + tweaked_theme.theme = hello_imgui.ImGuiTheme_.cherry + hello_imgui.apply_tweaked_theme(tweaked_theme) + if imgui.button("DarculaDarker", button_size): + tweaked_theme.theme = hello_imgui.ImGuiTheme_.darcula_darker + hello_imgui.apply_tweaked_theme(tweaked_theme) + imgui.end_group() + if imgui.is_item_hovered(): + imgui.set_tooltip( + "There are lots of other themes: look at the menu View/Theme\n" + "The selected theme is remembered and restored at startup" + ) + + +def gui_window_demo_features(app_state: AppState): + demo_fonts(app_state) + imgui.separator() + demo_assets(app_state) + imgui.separator() + demo_basic_widgets(app_state) + imgui.separator() + demo_rocket(app_state) + imgui.separator() + demo_user_settings(app_state) + imgui.separator() + demo_hide_window(app_state) + imgui.separator() + demo_show_additional_window(app_state) + imgui.separator() + demo_themes(app_state) + imgui.separator() + + +def status_bar_gui(app_state: AppState): + if app_state.rocket_state == RocketState.Preparing: + imgui.text("Rocket completion: ") + imgui.same_line() + imgui.progress_bar(app_state.rocket_progress, hello_imgui.em_to_vec2(7.0, 1.0)) # type: ignore + + +def show_menu_gui(runner_params: hello_imgui.RunnerParams): + hello_imgui.show_app_menu(runner_params) + hello_imgui.show_view_menu(runner_params) + if imgui.begin_menu("My Menu"): + clicked, _ = imgui.menu_item("Test me", "", False) + if clicked: + hello_imgui.log(hello_imgui.LogLevel.warning, "It works") + imgui.end_menu() + + +def show_app_menu_items(): + clicked, _ = imgui.menu_item("A Custom app menu item", "", False) + if clicked: + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on A Custom app menu item") + + +def show_top_toolbar(app_state: AppState): + imgui.push_font(app_state.large_icon_font) + if imgui.button(icons_fontawesome_6.ICON_FA_POWER_OFF): + hello_imgui.get_runner_params().app_shall_exit = True + + imgui.same_line(imgui.get_window_width() - hello_imgui.em_size(7.0)) + if imgui.button(icons_fontawesome_6.ICON_FA_HOUSE): + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Home in the top toolbar") + imgui.same_line() + if imgui.button(icons_fontawesome_6.ICON_FA_FLOPPY_DISK): + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Save in the top toolbar") + imgui.same_line() + if imgui.button(icons_fontawesome_6.ICON_FA_ADDRESS_BOOK): + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Address Book in the top toolbar") + + imgui.same_line(imgui.get_window_width() - hello_imgui.em_size(2.0)) + imgui.text(icons_fontawesome_6.ICON_FA_BATTERY_THREE_QUARTERS) + imgui.pop_font() + + +def show_right_toolbar(app_state: AppState): + imgui.push_font(app_state.large_icon_font) + if imgui.button(icons_fontawesome_6.ICON_FA_CIRCLE_ARROW_LEFT): + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Circle left in the right toolbar") + if imgui.button(icons_fontawesome_6.ICON_FA_CIRCLE_ARROW_RIGHT): + hello_imgui.log(hello_imgui.LogLevel.info, "Clicked on Circle right in the right toolbar") + imgui.pop_font() + + +########################################################################## +# Docking Layouts and Docking windows +########################################################################## + +# +# 1. Define the Docking splits (two versions are available) +# +def create_default_docking_splits() -> List[hello_imgui.DockingSplit]: + # Define the default docking splits, + # i.e. the way the screen space is split in different target zones for the dockable windows + # We want to split "MainDockSpace" (which is provided automatically) into three zones, like this: + # + # ___________________________________________ + # | | | + # | Command| | + # | Space | MainDockSpace | + # |------- | | + # | |--------------------------------| + # | | CommandSpace2 | + # ------------------------------------------- + # | MiscSpace | + # ------------------------------------------- + # + + # Uncomment the next line if you want to always start with this layout. + # Otherwise, modifications to the layout applied by the user layout will be remembered. + # runner_params.docking_params.layout_condition = hello_imgui.DockingLayoutCondition.ApplicationStart + + # Then, add a space named "MiscSpace" whose height is 25% of the app height. + # This will split the preexisting default dockspace "MainDockSpace" in two parts. + split_main_misc = hello_imgui.DockingSplit() + split_main_misc.initial_dock = "MainDockSpace" + split_main_misc.new_dock = "MiscSpace" + split_main_misc.direction = imgui.Dir.down + split_main_misc.ratio = 0.25 + + # Then, add a space to the left which occupies a column whose width is 25% of the app width + split_main_command = hello_imgui.DockingSplit() + split_main_command.initial_dock = "MainDockSpace" + split_main_command.new_dock = "CommandSpace" + split_main_command.direction = imgui.Dir.left + split_main_command.ratio = 0.25 + + # Then, add CommandSpace2 below MainDockSpace + split_main_command2 = hello_imgui.DockingSplit() + split_main_command2.initial_dock = "MainDockSpace" + split_main_command2.new_dock = "CommandSpace2" + split_main_command2.direction = imgui.Dir.down + split_main_command2.ratio = 0.5 + + splits = [split_main_misc, split_main_command, split_main_command2] + return splits + + +def create_alternative_docking_splits() -> List[hello_imgui.DockingSplit]: + # Define alternative docking splits for the "Alternative Layout" + # ___________________________________________ + # | | | + # | Misc | | + # | Space | MainDockSpace | + # | | | + # ------------------------------------------- + # | | | + # | | Command | + # | CommandSpace | Space2 | + # ------------------------------------------- + + split_main_command = hello_imgui.DockingSplit() + split_main_command.initial_dock = "MainDockSpace" + split_main_command.new_dock = "CommandSpace" + split_main_command.direction = imgui.Dir.down + split_main_command.ratio = 0.5 + + split_main_command2 = hello_imgui.DockingSplit() + split_main_command2.initial_dock = "CommandSpace" + split_main_command2.new_dock = "CommandSpace2" + split_main_command2.direction = imgui.Dir.right + split_main_command2.ratio = 0.4 + + split_main_misc = hello_imgui.DockingSplit() + split_main_misc.initial_dock = "MainDockSpace" + split_main_misc.new_dock = "MiscSpace" + split_main_misc.direction = imgui.Dir.left + split_main_misc.ratio = 0.5 + + splits = [split_main_command, split_main_command2, split_main_misc] + return splits + + +# +# 2. Define the Dockable windows +# +def create_dockable_windows(app_state: AppState) -> List[hello_imgui.DockableWindow]: + # A features demo window named "FeaturesDemo" will be placed in "CommandSpace". + # Its Gui is provided by "gui_window_demo_features" + features_demo_window = hello_imgui.DockableWindow() + features_demo_window.label = "Features Demo" + features_demo_window.dock_space_name = "CommandSpace" + features_demo_window.gui_function = lambda: gui_window_demo_features(app_state) + + # A layout customization window will be placed in "MainDockSpace". + # Its Gui is provided by "gui_window_layout_customization" + layout_customization_window = hello_imgui.DockableWindow() + layout_customization_window.label = "Layout customization" + layout_customization_window.dock_space_name = "MainDockSpace" + layout_customization_window.gui_function = lambda: gui_window_layout_customization(app_state) + + # A Log window named "Logs" will be placed in "MiscSpace". It uses the HelloImGui logger gui + logs_window = hello_imgui.DockableWindow() + logs_window.label = "Logs" + logs_window.dock_space_name = "MiscSpace" + logs_window.gui_function = hello_imgui.log_gui + + # A Window named "Dear ImGui Demo" will be placed in "MainDockSpace" + dear_imgui_demo_window = hello_imgui.DockableWindow() + dear_imgui_demo_window.label = "Dear ImGui Demo" + dear_imgui_demo_window.dock_space_name = "MainDockSpace" + dear_imgui_demo_window.imgui_window_flags = imgui.WindowFlags_.menu_bar.value + dear_imgui_demo_window.gui_function = imgui.show_demo_window # type: ignore + + # alternativeThemeWindow + alternative_theme_window = hello_imgui.DockableWindow() + # Since this window applies a theme, We need to call "imgui.begin" ourselves so + # that we can apply the theme before opening the window. + alternative_theme_window.call_begin_end = False + alternative_theme_window.label = "Alternative Theme" + alternative_theme_window.dock_space_name = "CommandSpace2" + alternative_theme_window.gui_function = lambda: gui_window_alternative_theme(app_state) + + dockable_windows = [ + features_demo_window, + layout_customization_window, + logs_window, + dear_imgui_demo_window, + alternative_theme_window, + ] + return dockable_windows + + +# +# 3. Define the layouts: +# A layout is stored inside DockingParams, and stores the splits + the dockable windows. +# Here, we provide the default layout, and two alternative layouts. +def create_default_layout(app_state: AppState) -> hello_imgui.DockingParams: + docking_params = hello_imgui.DockingParams() + # By default, the layout name is already "Default" + # docking_params.layout_name = "Default" + docking_params.docking_splits = create_default_docking_splits() + docking_params.dockable_windows = create_dockable_windows(app_state) + return docking_params + + +def create_alternative_layouts(app_state: AppState) -> List[hello_imgui.DockingParams]: + alternative_layout = hello_imgui.DockingParams() + alternative_layout.layout_name = "Alternative Layout" + alternative_layout.docking_splits = create_alternative_docking_splits() + alternative_layout.dockable_windows = create_dockable_windows(app_state) + + tabs_layout = hello_imgui.DockingParams() + tabs_layout.layout_name = "Tabs Layout" + tabs_layout.dockable_windows = create_dockable_windows(app_state) + # Force all windows to be presented in the MainDockSpace + for window in tabs_layout.dockable_windows: + window.dock_space_name = "MainDockSpace" + # In "Tabs Layout", no split is created + tabs_layout.docking_splits = [] + + return [alternative_layout, tabs_layout] + + +########################################################################## +# Define the app initial theme +########################################################################## +def setup_my_theme(): + """Example of theme customization at App startup + This function is called in the callback `setup_imgui_style` in order to apply a custom theme: + runner_params.callbacks.setup_imgui_style = setup_my_theme() + """ + # Apply default style + hello_imgui.imgui_default_settings.setup_default_imgui_style() + # Create a tweaked theme + tweaked_theme = hello_imgui.ImGuiTweakedTheme() + tweaked_theme.theme = hello_imgui.ImGuiTheme_.material_flat + tweaked_theme.tweaks.rounding = 10.0 + # Apply the tweaked theme + hello_imgui.apply_tweaked_theme(tweaked_theme) # Note: you can also push/pop the theme in order to apply it only to a specific part of the Gui: hello_imgui.push_tweaked_theme(tweaked_theme) / hello_imgui.pop_tweaked_theme() + # Then apply further modifications to ImGui style + imgui.get_style().item_spacing = ImVec2(6, 4) # Reduce spacing between items ((8, 4) by default) + imgui.get_style().set_color_(imgui.Col_.text.value, (0.8, 0.8, 0.85, 1.0)) # Change text color + + +########################################################################## +# main(): here, we simply fill RunnerParams, then run the application +########################################################################## +def main(): + # By default, an assets folder is installed via pip inside site-packages/lg_imgui_bundle/assets + # and provides two fonts (fonts/DroidSans.ttf and fonts/fontawesome-webfont.ttf) + # If you need to add more assets, make a copy of this assets folder and add your own files, + # and call set_assets_folder + hello_imgui.set_assets_folder(demo_utils.demos_assets_folder()) + + # + # Part 1: Define the application state, fill the status and menu bars, and load additional font + # + + # Our application state + app_state = AppState() + + # Hello ImGui params (they hold the settings as well as the Gui callbacks) + runner_params = hello_imgui.RunnerParams() + runner_params.app_window_params.window_title = "Docking Demo" + runner_params.imgui_window_params.menu_app_title = "Docking Demo" + runner_params.app_window_params.window_geometry.size = (1000, 900) + runner_params.app_window_params.restore_previous_geometry = True + runner_params.app_window_params.borderless = True + runner_params.app_window_params.borderless_movable = True + runner_params.app_window_params.borderless_resizable = True + runner_params.app_window_params.borderless_closable = True + + # Set LoadAdditionalFonts callback + runner_params.callbacks.load_additional_fonts = lambda: load_fonts(app_state) + + # + # Status bar + # + # We use the default status bar of Hello ImGui + runner_params.imgui_window_params.show_status_bar = True + # Add custom widgets in the status bar + runner_params.callbacks.show_status = lambda: status_bar_gui(app_state) + # uncomment next line in order to hide the FPS in the status bar + # runner_params.im_gui_window_params.show_status_fps = False + + # + # Menu bar + # + # Here, we fully customize the menu bar: + # by setting `show_menu_bar` to True, and `show_menu_app` and `show_menu_view` to False, + # HelloImGui will display an empty menu bar, which we can fill with our own menu items via the callback `show_menus` + runner_params.imgui_window_params.show_menu_bar = True + runner_params.imgui_window_params.show_menu_app = False + runner_params.imgui_window_params.show_menu_view = False + # Inside `show_menus`, we can call `hello_imgui.show_view_menu` and `hello_imgui.show_app_menu` if desired + runner_params.callbacks.show_menus = lambda: show_menu_gui(runner_params) + # Optional: add items to Hello ImGui default App menu + runner_params.callbacks.show_app_menu_items = show_app_menu_items + + # + # Top and bottom toolbars + # + # toolbar options + edge_toolbar_options = hello_imgui.EdgeToolbarOptions() + edge_toolbar_options.size_em = 2.5 + edge_toolbar_options.window_bg = ImVec4(0.8, 0.8, 0.8, 0.35) + # top toolbar + runner_params.callbacks.add_edge_toolbar( + hello_imgui.EdgeToolbarType.top, + lambda: show_top_toolbar(app_state), + edge_toolbar_options, + ) + # right toolbar + edge_toolbar_options.window_bg.w = 0.4 + runner_params.callbacks.add_edge_toolbar( + hello_imgui.EdgeToolbarType.right, + lambda: show_right_toolbar(app_state), + edge_toolbar_options, + ) + + # + # Load user settings at callbacks `post_init` and save them at `before_exit` + # + runner_params.callbacks.post_init = lambda: load_my_app_settings(app_state) + runner_params.callbacks.before_exit = lambda: save_my_app_settings(app_state) + + # Change style + runner_params.callbacks.setup_imgui_style = setup_my_theme + + # + # Part 2: Define the application layout and windows + # + + # First, tell HelloImGui that we want full screen dock space (this will create "MainDockSpace") + runner_params.imgui_window_params.default_imgui_window_type = ( + hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space + ) + # In this demo, we also demonstrate multiple viewports: you can drag windows outside + # out the main window in order to put their content into new native windows + runner_params.imgui_window_params.enable_viewports = True + # Set the default layout (this contains the default DockingSplits and DockableWindows) + runner_params.docking_params = create_default_layout(app_state) + # Add alternative layouts + runner_params.alternative_docking_layouts = create_alternative_layouts(app_state) + + # + # Part 3: Where to save the app settings + # + # tag::app_settings[] + # By default, HelloImGui will save the settings in the current folder. + # This is convenient when developing, but not so much when deploying the app. + # You can tell HelloImGui to save the settings in a specific folder: choose between + # current_folder + # app_user_config_folder + # app_executable_folder + # home_folder + # temp_folder + # documents_folder + # + # Note: app_user_config_folder is: + # AppData under Windows (Example: C:\Users\[Username]\AppData\Roaming) + # ~/.config under Linux + # "~/Library/Application Support" under macOS or iOS + runner_params.ini_folder_type = hello_imgui.IniFolderType.app_user_config_folder + + # runnerParams.ini_filename: this will be the name of the ini file in which the settings + # will be stored. + # In this example, the subdirectory Docking_Demo will be created under the folder defined + # by runnerParams.ini_folder_type. + # + # Note: if ini_filename is left empty, the name of the ini file will be derived + # from app_window_params.window_title + runner_params.ini_filename = "Docking_Demo/Docking_demo.ini" + # end::app_settings[] + + # + # Part 4: Run the app + # + hello_imgui.run(runner_params) + + +if __name__ == "__main__": + main() diff --git a/gui.py b/gui.py index 1afddce..0128e8e 100644 --- a/gui.py +++ b/gui.py @@ -1,54 +1,24 @@ -import imgui +from imgui_bundle import imgui, immapp import glfw import OpenGL.GL as gl -from imgui.integrations.glfw import GlfwRenderer from datatypes import * from view import View -def impl_glfw_init(window_name="Grapher Tool", width=1280, height=720): - if not glfw.init(): - print("Could not initialize OpenGL context") - exit(1) - - # OS X supports only forward-compatible core profiles from 3.2 - glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) - glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) - glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) - - glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE) - - # Create a windowed mode window and its OpenGL context - window = glfw.create_window(int(width), int(height), window_name, None, None) - glfw.make_context_current(window) - - if not window: - glfw.terminate() - print("Could not initialize Window") - exit(1) - - return window - class GUI(object): def __init__(self): super().__init__() - # Window States - self.window = impl_glfw_init() - gl.glClearColor(*COLOR_BACKGROUND) - imgui.create_context() - self.impl = GlfwRenderer(self.window) - self.io = imgui.get_io() + # self.io = imgui.get_io() # Global GUI Setting - win_w, win_h = glfw.get_window_size(self.window) + '''win_w, win_h = glfw.get_window_size(self.window) fb_w, fb_h = glfw.get_framebuffer_size(self.window) font_scaling_factor = max(float(fb_w) / win_w, float(fb_h) / win_h) font_size_in_pixels = 30 self.io.fonts.add_font_from_file_ttf("assets/MPLUSRounded1c-Regular.ttf", font_size_in_pixels * font_scaling_factor) - self.io.font_global_scale /= font_scaling_factor + self.io.font_global_scale /= font_scaling_factor''' self.view = View() - self.loop() def header(self): imgui.set_next_window_size(io.display_size.x, io.display_size.y*0.03) @@ -62,28 +32,16 @@ class GUI(object): imgui.set_cursor_pos_x((ww - tw) * 0.5) imgui.text("Student Analyzer") - def loop(self): - while not glfw.window_should_close(self.window): - glfw.poll_events() - self.impl.process_inputs() - imgui.new_frame() - + def __call__(self): self.view() - #imgui.show_test_window() - - imgui.render() - - gl.glClearColor(*COLOR_BACKGROUND) - gl.glClear(gl.GL_COLOR_BUFFER_BIT) - - self.impl.render(imgui.get_draw_data()) - glfw.swap_buffers(self.window) - - self.impl.shutdown() - glfw.terminate() if __name__ == "__main__": - - gui = GUI() + immapp.run( + gui_function=GUI(), + window_title="Student Analyzer", + window_size_auto=True, + with_implot=True, + with_markdown=True + ) diff --git a/lecture_editor.py b/lecture_editor.py index b7a2682..235f082 100644 --- a/lecture_editor.py +++ b/lecture_editor.py @@ -1,5 +1,4 @@ -import imgui - +from imgui_bundle import imgui from datatypes import * from model import * diff --git a/main_menu.py b/main_menu.py index 4a1f8f2..94c8a96 100644 --- a/main_menu.py +++ b/main_menu.py @@ -1,4 +1,4 @@ -import imgui +from imgui_bundle import imgui, imgui_ctx from datatypes import * @@ -13,23 +13,23 @@ class MainMenu: if self.new: self.create_new_file() - with imgui.begin_main_menu_bar() as main_menu_bar: + with imgui_ctx.begin_main_menu_bar() as main_menu_bar: if main_menu_bar: - with imgui.begin_menu("File", True) as file_menu: - if file_menu.opened: + with imgui_ctx.begin_menu("File", True) as file_menu: + if file_menu.visible: new, _ = imgui.menu_item("New", " ", False, True) if new: self.new = True imgui.menu_item("Open", " ", False, True) imgui.menu_item("Save", " ", False, True) imgui.menu_item("Save as", " ", False, True) - with imgui.begin_menu("View", True) as view_menu: - if view_menu.opened: - with imgui.begin_menu("Change Layout", True) as open_layout_menu: - if open_layout_menu.opened: + with imgui_ctx.begin_menu("View", True) as view_menu: + if view_menu.visible: + with imgui_ctx.begin_menu("Change Layout", True) as open_layout_menu: + if open_layout_menu.visible: layout_options = list(LayoutOptions) for n, l in enumerate(layout_options): - clicked, _ = imgui.menu_item(l.name.title(), None, False, True) + clicked = imgui.menu_item_simple(l.name.title()) if clicked: return l diff --git a/student_editor.py b/student_editor.py index 48566cb..e4fa3b3 100644 --- a/student_editor.py +++ b/student_editor.py @@ -1,6 +1,5 @@ -import imgui - from model import * +from imgui_bundle import imgui class StudentEditor: def __init__(self): diff --git a/student_graph.py b/student_graph.py index 3c59689..81bd978 100644 --- a/student_graph.py +++ b/student_graph.py @@ -1,5 +1,5 @@ -import imgui import numpy as np +from imgui_bundle import imgui import random from datatypes import * diff --git a/student_info.py b/student_info.py index f64171f..325d695 100644 --- a/student_info.py +++ b/student_info.py @@ -1,60 +1,77 @@ -import imgui from datatypes import * +from imgui_bundle import imgui, imgui_ctx, imgui_md, implot, ImVec2 +import numpy as np from model import * class StudentInfo: def __init__(self): super().__init__() + self.select_class = 0 + self.select_student = 0 - def __call__(self, student: Student): - submissions = Submission.select().where(Submission.student_id == student.id) if student else None + def student_graph(self, student_id: int) -> None: + student = Student.get_by_id(student_id) + clas = Class.get_by_id(student.class_id) + lectures = Lecture.select().where(Lecture.class_id == clas.id) + submissions = Submission.select().where(Submission.student_id == student.id) + + overall_points = np.sum([l.points for l in lectures]) + points = np.sum([sub.points for sub in submissions]) + if points.is_integer(): + points = int(points) - with imgui.begin("Student Info", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): - if not student: - imgui.text("No Student selected") - return - + subs_data = np.array([sub.points/l.points for sub, l in zip(submissions, lectures)])*100 + subs_labels = [str(l.title) for l in lectures] + + with imgui_ctx.begin_group(): + imgui_md.render(f"# {student.prename} {student.surname}") + imgui_md.render(f"### {clas.name}") + + pb_content = f"{points}/{overall_points} {points/overall_points:.1%}" + imgui.progress_bar(points/overall_points, overlay=pb_content) + + implot.push_colormap(implot.Colormap_.deep.value) + if implot.begin_plot("Performance"): + implot.setup_axes("Lectures", "Percentage") + implot.setup_axes_limits(-1, len(subs_data), 0, 110) + implot.setup_axis_ticks(implot.ImAxis_.x1.value, 0, len(subs_labels), len(subs_labels), subs_labels, False) + implot.plot_bars("Submissions", subs_data) + implot.end_plot() + + def student_list(self) -> int: + classes = Class.select() + content = [f"{n}. {c.name}" for n, c in enumerate(classes, start=1)] + students = Student.select().where(Student.class_id == classes[self.select_class].id) + lectures = Lecture.select().where(Lecture.class_id == classes[self.select_class].id) + + overall_points = np.sum([l.points for l in lectures]) + points = list() + for student in students: + submissions = Submission.select().where(Submission.student_id == student.id) + cummultative = [sub.points for sub in submissions] + passed = np.sum([p > overall_points*0.3 for p in cummultative]) + points.append((student, np.sum(cummultative)/overall_points, passed > 1)) + + students = sorted(points, key=lambda x: x[1], reverse=True) + + with imgui_ctx.begin_group(): + _, self.select_class = imgui.combo("##class_list", self.select_class, content, len(content)) + for n, student in enumerate(students, start=1): + s = student[0] + display = f"{n}. {s.prename} {s.surname}" + _, clicked = imgui.selectable(display, self.select_student == n-1) + if clicked: + self.select_student = n-1 + + return students[self.select_student][0].id + + def __call__(self): + with imgui_ctx.begin("Student Info"): w, h = imgui.get_window_size() - imgui.text_colored(f"{student.prename} {student.surname}", *COLOR_TEXT) - - content = Class.get_by_id(student.class_id).name - text_size = imgui.calc_text_size(content) - imgui.same_line(position=w-1.5*text_size.x) - imgui.text(content) - - if submissions: - overall_points = sum([s.points for s in submissions]) - if overall_points.is_integer(): - overall_points = int(overall_points) - overall_max = sum([lectures.points for lectures in [Lecture.get_by_id(s.lecture_id) for s in submissions]]) - percentile = overall_points / overall_max - imgui.progress_bar(percentile, (w*0.5, h*0.05), f"{overall_points}/{overall_max} {percentile:.1%}") - - content = "Delete" - if submissions: - text_size = imgui.calc_text_size(content) - imgui.same_line(position=w-2*text_size.x) - if imgui.button(content): - # Delete all Submissions related to that Student - #for submission in submissions: - # submission.delete().execute() - # Delete Student - #student.delete().execute() - return - - imgui.separator() - - if not submissions: - imgui.text("No Submission for this Student") - - for n, submission in enumerate(submissions, start=1): - lecture = Lecture.get_by_id(submission.lecture_id) - - points = submission.points - if points.is_integer(): - points = int(points) - - display = f"{n}. {lecture.title} {points}/{lecture.points}" - COLOR = COLOR_TEXT_PASSED if points > lecture.points*0.3 else COLOR_TEXT_FAILED - imgui.text_colored(display, *COLOR) + with imgui_ctx.begin_child("Student Selector", ImVec2(w*0.25, h*0.9)): + id = self.student_list() + imgui.same_line() + with imgui_ctx.begin_child("Student Graph", ImVec2(w*0.7, h*0.9)): + self.student_graph(id) + diff --git a/student_list.py b/student_list.py index 23b2942..7ec48e3 100644 --- a/student_list.py +++ b/student_list.py @@ -1,6 +1,5 @@ -import imgui - from model import * +from imgui_bundle import imgui class StudentList: def __init__(self): diff --git a/student_ranking.py b/student_ranking.py index e8e9e1a..6add0d0 100644 --- a/student_ranking.py +++ b/student_ranking.py @@ -1,5 +1,5 @@ -import imgui import numpy as np +from imgui_bundle import imgui from model import * diff --git a/submission_editor.py b/submission_editor.py index 9a3c728..a231fe5 100644 --- a/submission_editor.py +++ b/submission_editor.py @@ -1,5 +1,4 @@ -import imgui - +from imgui_bundle import imgui from model import * class SubmissionEditor: diff --git a/test.db b/test.db index b29087fab36b46af642e8102a9b8569c49cb7f32..846da4461b5795d5ba595aa15316d411084d08aa 100644 GIT binary patch literal 49152 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 diff --git a/view.py b/view.py index 79e22cd..9e3e55a 100644 --- a/view.py +++ b/view.py @@ -1,84 +1,58 @@ -import imgui +from imgui_bundle import imgui, ImVec2 from datatypes import * from main_menu import MainMenu -from student_editor import StudentEditor -from student_list import StudentList -from student_info import StudentInfo -from class_editor import ClassEditor -from lecture_editor import LectureEditor -from submission_editor import SubmissionEditor +from database_editor import DatabaseEditor -from student_graph import StudentGraph -from student_ranking import StudentRanking +from student_info import StudentInfo def set_layout(size: tuple, pos: tuple) -> None: io = imgui.get_io() - imgui.set_next_window_size( - io.display_size.x*size[0], - io.display_size.y*size[1] - ) - imgui.set_next_window_position( - io.display_size.x*pos[0], - io.display_size.y*pos[1] - ) + size = imgui.ImVec2(*size) + pos = imgui.ImVec2(*pos) + imgui.set_next_window_size(size) + imgui.set_next_window_pos(pos) class GrapherLayout: def __init__(self): super().__init__() - self.student_graph = StudentGraph() - self.student_ranking = StudentRanking() + self.student_info = StudentInfo() + + def set_layout(self): + pass def __call__(self): - set_layout((1, 0.4), (0, 0.02)) - self.student_graph() - - set_layout((0.3, 0.6), (0, 0.42)) - self.student_ranking() + self.student_info() class EditorLayout: def __init__(self): super().__init__() - self.io = imgui.get_io() - - self.student_editor = StudentEditor() - self.student_list = StudentList() - self.student_info = StudentInfo() - self.class_editor = ClassEditor() - self.lecture_editor = LectureEditor() - self.submission_editor = SubmissionEditor() + self.database_editor = DatabaseEditor() + + def set_layout(self): + pass def __call__(self): - set_layout((0.3, 0.3), (0.7, 0.4)) - clas = self.class_editor() - - set_layout((0.5, 0.3), (0.2, 0.4)) - self.student_editor() - - set_layout((0.2, 0.98), (0, 0.02)) - student = self.student_list(clas) - - set_layout((0.3, 0.38), (0.7, 0.02)) - self.student_info(student) - - set_layout((0.5, 0.3), (0.2, 0.7)) - self.lecture_editor(clas) - - set_layout((0.5, 0.3), (0.2, 0.1)) - self.submission_editor(clas) + self.database_editor() class View: def __init__(self): super().__init__() - self.io = imgui.get_io() self.current = LayoutOptions.GRAPHER self.main_menu = MainMenu() self.editor = EditorLayout() self.grapher = GrapherLayout() + def switch_context(self, ctx: LayoutOptions) -> None: + match ctx: + case LayoutOptions.EDITOR: + self.editor.set_layout() + case LayoutOptions.GRAPHER: + self.grapher.set_layout() + def __call__(self): option = self.main_menu() if option: