Changed: Switched ImGUI implementation

This commit is contained in:
DerGrumpf 2025-01-09 22:33:04 +01:00
parent 70ea8899e2
commit 6f3605c450
17 changed files with 1308 additions and 176 deletions

View File

@ -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

1 First Name Last Name Sex Tutorial 1 Tutorial 2 Extended Applications Numpy & MatPlotLib SciPy Monte Carlo Pandas & Seaborn Folium
2 Abdalaziz Abunjaila Male 30.5 15 18 28 17 17 17 22
3 Marleen Adolphi Female 29.5 15 18 32 19 0 17 24
4 Skofiare Berisha Female 29.5 13 18 34 20 17 20 26
5 Aurela Brahimi Female 17.5 15 15.5 26 16 17 19 16
6 Cam Thu Do Female 31 15 18 34 19 20 21.5 22
7 Nova Eib Female 31 15 15 34 20 20 21 27

Binary file not shown.

Binary file not shown.

View File

@ -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)

261
database_editor.py Normal file
View File

@ -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]

926
demo_docking.py Normal file
View File

@ -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()

66
gui.py
View File

@ -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
)

View File

@ -1,5 +1,4 @@
import imgui
from imgui_bundle import imgui
from datatypes import *
from model import *

View File

@ -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

View File

@ -1,6 +1,5 @@
import imgui
from model import *
from imgui_bundle import imgui
class StudentEditor:
def __init__(self):

View File

@ -1,5 +1,5 @@
import imgui
import numpy as np
from imgui_bundle import imgui
import random
from datatypes import *

View File

@ -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)
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
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)
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)
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)
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)

View File

@ -1,6 +1,5 @@
import imgui
from model import *
from imgui_bundle import imgui
class StudentList:
def __init__(self):

View File

@ -1,5 +1,5 @@
import imgui
import numpy as np
from imgui_bundle import imgui
from model import *

View File

@ -1,5 +1,4 @@
import imgui
from imgui_bundle import imgui
from model import *
class SubmissionEditor:

BIN
test.db

Binary file not shown.

72
view.py
View File

@ -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.database_editor = DatabaseEditor()
self.student_editor = StudentEditor()
self.student_list = StudentList()
self.student_info = StudentInfo()
self.class_editor = ClassEditor()
self.lecture_editor = LectureEditor()
self.submission_editor = SubmissionEditor()
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: