Changed: Complete Overall
This commit is contained in:
parent
6f3605c450
commit
c382aac84d
436
analyzer.py
Normal file
436
analyzer.py
Normal file
@ -0,0 +1,436 @@
|
||||
# Custom
|
||||
from model import *
|
||||
from appstate import AppState
|
||||
|
||||
# External
|
||||
from imgui_bundle import (
|
||||
imgui,
|
||||
immapp,
|
||||
hello_imgui,
|
||||
imgui_md,
|
||||
implot,
|
||||
implot3d,
|
||||
immvision,
|
||||
ImVec2,
|
||||
ImVec4,
|
||||
im_file_dialog
|
||||
)
|
||||
from PIL import ImageColor
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
# Built In
|
||||
from typing import List, Any
|
||||
|
||||
def student_list(app_state: AppState) -> None:
|
||||
statics = student_list
|
||||
if not app_state.current_class_id:
|
||||
imgui.text("No Class found in Database")
|
||||
return
|
||||
|
||||
if not hasattr(statics, "select"):
|
||||
statics.select = 0
|
||||
|
||||
statics.students = Student.select().where(Student.class_id == app_state.current_class_id)
|
||||
statics.students = statics.students if statics.students else None
|
||||
if not statics.students:
|
||||
imgui.text(f"No Stundents found in {Class.get_by_id(app_state.current_class_id).name}")
|
||||
return
|
||||
|
||||
for n, student in enumerate(statics.students, start=1):
|
||||
display = f"{n}. {student.prename} {student.surname}"
|
||||
_, clicked = imgui.selectable(display, statics.select == n-1)
|
||||
if clicked:
|
||||
statics.select = n-1
|
||||
|
||||
app_state.current_student_id = statics.students[statics.select].id
|
||||
|
||||
def lecture_list(app_state: AppState) -> None:
|
||||
statics = lecture_list
|
||||
|
||||
if not app_state.current_class_id:
|
||||
imgui.text("No class found in Database")
|
||||
return
|
||||
|
||||
lectures = Lecture.select().where(Lecture.class_id == app_state.current_class_id)
|
||||
|
||||
if not lectures:
|
||||
imgui.text(f"No Lectures found for {Class.get_by_id(app_state.current_class_id).name}")
|
||||
return
|
||||
|
||||
if not hasattr(statics, "select"):
|
||||
statics.select = 0
|
||||
|
||||
for n, lecture in enumerate(lectures, start=1):
|
||||
display = f"{n}. {lecture.title}"
|
||||
_, clicked = imgui.selectable(display, statics.select == n-1)
|
||||
if clicked:
|
||||
statics.select = n-1
|
||||
|
||||
app_state.current_lecture_id = lectures[statics.select].id
|
||||
|
||||
def class_list(app_state: AppState) -> None:
|
||||
statics = class_list
|
||||
|
||||
if db.is_closed():
|
||||
imgui.text("No Database loaded")
|
||||
return
|
||||
|
||||
classes = Class.select() if db else None
|
||||
if not classes:
|
||||
imgui.text("No Classes currently in Database")
|
||||
return
|
||||
|
||||
if not hasattr(statics, "select"):
|
||||
statics.select = 0
|
||||
|
||||
for n, clas in enumerate(classes, start=1):
|
||||
display = f"{n}. {clas.name}"
|
||||
_, clicked = imgui.selectable(display, statics.select == n-1)
|
||||
if clicked:
|
||||
statics.select = n-1
|
||||
|
||||
app_state.current_class_id = classes[statics.select].id
|
||||
|
||||
def submissions_list(app_state: AppState) -> None:
|
||||
statics = submissions_list
|
||||
|
||||
if not app_state.current_lecture_id:
|
||||
imgui.text("No Lecture found")
|
||||
return
|
||||
if not app_state.current_student_id:
|
||||
imgui.text("No Student found")
|
||||
return
|
||||
|
||||
submissions = Submission.select().where(Submission.lecture_id == app_state.current_lecture_id and Submission.student_id == app_state.current_student_id)
|
||||
|
||||
if not submissions:
|
||||
student = Student.get_by_id(app_state.current_student_id)
|
||||
lecture = Lecture.get_by_id(app_state.current_lecture_id)
|
||||
imgui.text(f"{student.prename} {student.surname} didn't submitted for {lecture.title}")
|
||||
return
|
||||
|
||||
if not hasattr(statics, "select"):
|
||||
statics.select = 0
|
||||
|
||||
for n, sub in enumerate(submissions, start=1):
|
||||
lecture = Lecture.get_by_id(sub.lecture_id)
|
||||
points = sub.points
|
||||
if points.is_integer():
|
||||
points = int(points)
|
||||
display = f"{n}. {lecture.title} {points}/{lecture.points}"
|
||||
_, clicked = imgui.selectable(display, statics.select == n-1)
|
||||
if clicked:
|
||||
statics.select = n-1
|
||||
|
||||
app_state.current_submission_id = submissions[statics.select].id
|
||||
|
||||
def plot_bar_line_percentage(data: np.array, labels: list, avg: float) -> None:
|
||||
if not data.size > 0:
|
||||
imgui.text("No Data available")
|
||||
return
|
||||
|
||||
name = hash(avg)
|
||||
|
||||
if avg.is_integer():
|
||||
avg = int(avg)
|
||||
avg = np.ones(len(data)) * avg
|
||||
|
||||
w, h = imgui.get_window_size()
|
||||
implot.push_colormap(implot.Colormap_.hot.value)
|
||||
if implot.begin_plot(f"Performance##{name}", ImVec2(-1, h*0.4), implot.Flags_.no_mouse_text.value | implot.Flags_.no_inputs.value):
|
||||
implot.setup_axes("Lectures", "Percentage")
|
||||
implot.setup_axes_limits(-1, len(data), 0, 110)
|
||||
implot.push_style_var(implot.StyleVar_.fill_alpha.value, 0.6)
|
||||
implot.push_style_var(implot.StyleVar_.line_weight.value, 3)
|
||||
implot.setup_axis_ticks(implot.ImAxis_.x1.value, 0, len(labels), len(labels), [" " for _ in labels], False)
|
||||
implot.plot_bars("Submissions", data)
|
||||
implot.plot_line("Average", avg)
|
||||
|
||||
implot.push_style_color(implot.Col_.inlay_text, ImVec4(190,190,40,255)/255)
|
||||
for x_pos, label in enumerate(labels):
|
||||
y_pos = 50
|
||||
implot.plot_text(label, x_pos, y_pos//2, ImVec2(0,0), implot.TextFlags_.vertical.value)
|
||||
implot.pop_style_color()
|
||||
|
||||
implot.pop_style_var()
|
||||
implot.end_plot()
|
||||
|
||||
COLOR_TEXT_PASSED = tuple([e/255 for e in ImageColor.getcolor("#1AFE49","RGBA")])
|
||||
COLOR_TEXT_FAILED = tuple([e/255 for e in ImageColor.getcolor("#FF124F","RGBA")])
|
||||
@immapp.static(inited=False)
|
||||
def student_graph(app_state: AppState) -> None:
|
||||
statics = student_graph
|
||||
if not statics.inited:
|
||||
statics.id = -1
|
||||
statics.student = None
|
||||
statics.lectures = None
|
||||
statics.points = None
|
||||
statics.sub_points = None
|
||||
statics.subs_data = None
|
||||
statics.subs_labels = None
|
||||
statics.max_points = None
|
||||
statics.avg = None
|
||||
statics.inited = True
|
||||
|
||||
if id != app_state.current_student_id:
|
||||
statics.id = app_state.current_student_id
|
||||
|
||||
if not app_state.current_student_id:
|
||||
imgui.text("No Students in Database")
|
||||
return
|
||||
statics.student = Student.get_by_id(app_state.current_student_id)
|
||||
submissions = Submission.select().where(Submission.student_id == statics.student.id)
|
||||
|
||||
statics.lectures = [Lecture.get_by_id(sub.lecture_id) for sub in submissions]
|
||||
statics.max_points = np.sum([l.points for l in statics.lectures])
|
||||
statics.sub_points = [sub.points for sub in submissions]
|
||||
statics.points = np.sum(statics.sub_points)
|
||||
if statics.points.is_integer():
|
||||
statics.points = int(statics.points)
|
||||
|
||||
statics.subs_data = np.array([p/mp.points for p, mp in zip(statics.sub_points, statics.lectures)], dtype=np.float32)*100
|
||||
statics.subs_labels = [f"{l.title} {int(points) if points.is_integer() else points}/{l.points}" for l, points in zip(statics.lectures, statics.sub_points)]
|
||||
statics.avg = statics.points/statics.max_points*100
|
||||
|
||||
w, h = imgui.get_window_size()
|
||||
imgui_md.render(f"# {statics.student.prename} {statics.student.surname}")
|
||||
imgui_md.render(f"### {statics.points}/{statics.max_points}")
|
||||
imgui.text(" ")
|
||||
imgui.progress_bar(statics.points/statics.max_points, ImVec2(w*0.9, h*0.05), f"{statics.points}/{statics.max_points} {statics.points/statics.max_points:.1%}")
|
||||
plot_bar_line_percentage(statics.subs_data, statics.subs_labels, statics.avg)
|
||||
imgui.separator()
|
||||
for n, data in enumerate(zip(statics.lectures, statics.sub_points), start=1):
|
||||
lecture, points = data
|
||||
COLOR = COLOR_TEXT_PASSED if points >= lecture.points*0.3 else COLOR_TEXT_FAILED
|
||||
imgui.text_colored(COLOR, f"{n}. {lecture.title}")
|
||||
|
||||
@immapp.static(inited=False)
|
||||
def sex_graph(app_state: AppState) -> None:
|
||||
statics = sex_graph
|
||||
|
||||
if db.is_closed():
|
||||
imgui.text("No Database loaded")
|
||||
return
|
||||
|
||||
if not statics.inited:
|
||||
statics.max_points = None
|
||||
statics.male_points = None
|
||||
statics.female_points = None
|
||||
statics.male_percentage = None
|
||||
statics.female_percentage = None
|
||||
statics.male_labels = None
|
||||
statics.female_labels = None
|
||||
statics.lectures = None
|
||||
statics.class_id = -1
|
||||
statics.state = 0
|
||||
statics.inited = True
|
||||
|
||||
if not app_state.current_class_id:
|
||||
imgui.text("No Class found")
|
||||
return
|
||||
|
||||
if statics.class_id != app_state.current_class_id:
|
||||
statics.class_id = app_state.current_class_id
|
||||
lectures = Lecture.select().where(Lecture.class_id == statics.class_id)
|
||||
statics.lectures = lectures
|
||||
|
||||
statics.max_points = np.empty(len(lectures))
|
||||
statics.male_points = np.empty(len(lectures))
|
||||
statics.female_points = np.empty(len(lectures))
|
||||
for n, lecture in enumerate(lectures):
|
||||
statics.max_points[n] = lecture.points # Acc points
|
||||
|
||||
m_count = 0
|
||||
f_count = 0
|
||||
m_points = 0
|
||||
f_points = 0
|
||||
|
||||
for sub in Submission.select().where(Submission.lecture_id == lecture.id):
|
||||
if Student.get_by_id(sub.student_id).sex == 'Male':
|
||||
m_points += sub.points
|
||||
m_count += 1
|
||||
else:
|
||||
f_points += sub.points
|
||||
f_count += 1
|
||||
statics.male_points[n] = m_points/m_count/lecture.points
|
||||
statics.female_points[n] = f_points/f_count/lecture.points
|
||||
|
||||
statics.male_percentage = np.sum(statics.male_points)/len(statics.male_points)
|
||||
statics.female_percentage = np.sum(statics.female_points)/len(statics.female_points)
|
||||
|
||||
statics.male_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.male_points)]
|
||||
statics.female_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.female_points)]
|
||||
|
||||
w, h = imgui.get_window_size()
|
||||
if statics.state == 0:
|
||||
imgui_md.render("# Male")
|
||||
imgui.progress_bar(statics.male_percentage, ImVec2(w*0.9, h*0.05), f"{statics.male_percentage:.1%} n={len(Student.select().where(Student.sex == 'Male'))}")
|
||||
plot_bar_line_percentage(statics.male_points*100, statics.male_labels, statics.male_percentage*100)
|
||||
|
||||
imgui_md.render("# Female")
|
||||
imgui.progress_bar(statics.female_percentage, ImVec2(w*0.9, h*0.05), f"{statics.female_percentage:.1%} n={len(Student.select().where(Student.sex == 'Female'))}")
|
||||
plot_bar_line_percentage(statics.female_points*100, statics.female_labels, statics.female_percentage*100)
|
||||
|
||||
if statics.state == 1:
|
||||
male_xs = np.arange(1, len(statics.male_points)+1, dtype=np.float32)
|
||||
male_ys = male_xs*0
|
||||
male_zs = np.array(statics.male_points*100, dtype=np.float32)
|
||||
|
||||
female_xs = np.arange(1, len(statics.female_points)+1, dtype=np.float32)
|
||||
female_ys = np.ones(len(statics.female_points), dtype=np.float32)
|
||||
female_zs = np.array(statics.female_points*100, dtype=np.float32)
|
||||
|
||||
if implot3d.begin_plot("3D Gender Plot", ImVec2(w*0.9, h*0.9)):
|
||||
implot3d.setup_axes("Lecture", "Gender", "Percentage")
|
||||
implot3d.setup_box_scale(1.1, 1.1, 1.1)
|
||||
implot3d.setup_axes_limits(0, len(statics.male_points)+1, -0.2, 1.2, 0, 110)
|
||||
|
||||
implot3d.set_next_marker_style(implot3d.Marker_.square.value)
|
||||
implot3d.plot_line("Male", male_xs, male_ys, male_zs)
|
||||
|
||||
implot3d.set_next_marker_style(implot3d.Marker_.circle.value)
|
||||
implot3d.plot_line("Female", female_xs, female_ys, female_zs)
|
||||
|
||||
for n, l in enumerate(statics.lectures, start=1):
|
||||
implot3d.plot_text(l.title, n, np.clip(n/len(statics.lectures)), 50, np.pi/4)
|
||||
|
||||
implot3d.end_plot()
|
||||
|
||||
if imgui.button("Change 2D/3D"):
|
||||
statics.state = not statics.state
|
||||
|
||||
|
||||
|
||||
COLOR_TEXT_FIRST = tuple([e/255 for e in ImageColor.getcolor("#FFCF40","RGBA")])
|
||||
COLOR_TEXT_SECOND = tuple([e/255 for e in ImageColor.getcolor("#A5A9B4","RGBA")])
|
||||
COLOR_TEXT_THIRD = tuple([e/255 for e in ImageColor.getcolor("#97564A","RGBA")])
|
||||
COLOR_TEXT_ELSE = tuple([e/255 for e in ImageColor.getcolor("#85EBD9","RGBA")])
|
||||
@immapp.static(inited=False)
|
||||
def ranking(app_state: AppState) -> None:
|
||||
statics = ranking
|
||||
|
||||
if not statics.inited:
|
||||
statics.id = -1
|
||||
statics.rank = None
|
||||
statics.state = 1
|
||||
statics.inited = True
|
||||
|
||||
imgui_md.render("# Student Ranking")
|
||||
|
||||
if not app_state.current_class_id:
|
||||
imgui.text("No class selected")
|
||||
return
|
||||
|
||||
if statics.id != app_state.current_class_id:
|
||||
statics.id = app_state.current_class_id
|
||||
|
||||
students = Student.select().where(Student.class_id == statics.id)
|
||||
max_points = np.sum([l.points for l in Lecture.select().where(Lecture.class_id == statics.id)])
|
||||
|
||||
statics.rank = list()
|
||||
for student in students:
|
||||
points = list()
|
||||
for sub in Submission.select().where(Submission.student_id == student.id):
|
||||
points.append(sub.points)
|
||||
statics.rank.append((student, sum(points)/max_points))
|
||||
statics.rank = sorted(statics.rank, key=lambda item: item[1], reverse=True)
|
||||
|
||||
if statics.state == 0:
|
||||
for n, data in enumerate(statics.rank, start=1):
|
||||
student, points = data
|
||||
display = f"{n}. {student.prename} {student.surname} {points:.1%}"
|
||||
COLOR = COLOR_TEXT_ELSE
|
||||
if n == 1:
|
||||
COLOR = COLOR_TEXT_FIRST
|
||||
if n == 2:
|
||||
COLOR = COLOR_TEXT_SECOND
|
||||
if n == 3:
|
||||
COLOR = COLOR_TEXT_THIRD
|
||||
imgui.text_colored(COLOR, display)
|
||||
|
||||
if statics.state == 1:
|
||||
w, h = imgui.get_window_size()
|
||||
if implot.begin_plot("Ranking", ImVec2(w*0.9, h*0.9)):
|
||||
implot.plot_bars("Ranking", np.array([p[1] for p in reversed(statics.rank)])*100, 0.67, 0, implot.BarsFlags_.horizontal.value)
|
||||
for n, s in enumerate(reversed(statics.rank)):
|
||||
student = s[0]
|
||||
implot.plot_text(f"{student.prename} {student.surname}", s[1]*50, n)
|
||||
implot.end_plot()
|
||||
|
||||
if imgui.button("Change"):
|
||||
statics.state = not statics.state
|
||||
|
||||
def analyzer_docking_splits() -> List[hello_imgui.DockingSplit]:
|
||||
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.2
|
||||
|
||||
# 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.25
|
||||
|
||||
splits = [split_main_misc, split_main_command, split_main_command2]
|
||||
return splits
|
||||
|
||||
def set_analyzer_layout(app_state: AppState) -> List[hello_imgui.DockableWindow]:
|
||||
student_selector = hello_imgui.DockableWindow()
|
||||
student_selector.label = "Students"
|
||||
student_selector.dock_space_name = "CommandSpace"
|
||||
student_selector.gui_function = lambda: student_list(app_state)
|
||||
|
||||
lecture_selector = hello_imgui.DockableWindow()
|
||||
lecture_selector.label = "Lectures"
|
||||
lecture_selector.dock_space_name = "CommandSpace2"
|
||||
lecture_selector.gui_function = lambda: lecture_list(app_state)
|
||||
|
||||
class_selector = hello_imgui.DockableWindow()
|
||||
class_selector.label = "Classes"
|
||||
class_selector.dock_space_name = "CommandSpace2"
|
||||
class_selector.gui_function = lambda: class_list(app_state)
|
||||
|
||||
submission_selector = hello_imgui.DockableWindow()
|
||||
submission_selector.label = "Submissions"
|
||||
submission_selector.dock_space_name = "CommandSpace"
|
||||
submission_selector.gui_function = lambda: submissions_list(app_state)
|
||||
|
||||
student_info = hello_imgui.DockableWindow()
|
||||
student_info.label = "Student Analyzer"
|
||||
student_info.dock_space_name = "MainDockSpace"
|
||||
student_info.gui_function = lambda: student_graph(app_state)
|
||||
|
||||
sex_info = hello_imgui.DockableWindow()
|
||||
sex_info.label = "Analyze by Gender"
|
||||
sex_info.dock_space_name = "MainDockSpace"
|
||||
sex_info.gui_function = lambda: sex_graph(app_state)
|
||||
|
||||
student_ranking = hello_imgui.DockableWindow()
|
||||
student_ranking.label = "Ranking"
|
||||
student_ranking.dock_space_name = "MainDockSpace"
|
||||
student_ranking.gui_function = lambda: ranking(app_state)
|
||||
|
||||
return [
|
||||
class_selector, student_selector,
|
||||
lecture_selector, submission_selector,
|
||||
student_info, sex_info,
|
||||
student_ranking
|
||||
]
|
||||
|
||||
def analyzer_layout(app_state: AppState) -> hello_imgui.DockingParams:
|
||||
docking_params = hello_imgui.DockingParams()
|
||||
docking_params.layout_name = "Analyzer"
|
||||
docking_params.docking_splits = analyzer_docking_splits()
|
||||
docking_params.dockable_windows = set_analyzer_layout(app_state)
|
||||
return docking_params
|
49
appstate.py
Normal file
49
appstate.py
Normal file
@ -0,0 +1,49 @@
|
||||
from imgui_bundle import hello_imgui
|
||||
from model import *
|
||||
from datetime import datetime
|
||||
|
||||
class AppState:
|
||||
current_class_id: int
|
||||
current_lecture_id: int
|
||||
current_student_id: int
|
||||
current_submission_id: int
|
||||
|
||||
def __init__(self):
|
||||
self.current_class_id = None
|
||||
self.current_lecture_id = None
|
||||
self.current_student_id = None
|
||||
self.current_submission_id = None
|
||||
|
||||
def update(self):
|
||||
|
||||
clas = Class.select()
|
||||
self.current_class_id = clas[0].id if clas else None
|
||||
|
||||
lectures = Lecture.select().where(Lecture.class_id == self.current_class_id)
|
||||
self.current_lecture_id = lectures[0].id if lectures else None
|
||||
|
||||
students = Student.select().where(Student.class_id == self.current_class_id)
|
||||
self.current_student_id = students[0].id if students else None
|
||||
|
||||
submissions = Submission.select().where(Submission.lecture_id == self.current_lecture_id and Submission.student_id == self.current_student_id)
|
||||
self.current_submission_id = submissions[0].id if submissions else None
|
||||
|
||||
LOG_DEBUG(f"Updated App State {repr(self)}")
|
||||
|
||||
def __repr__(self):
|
||||
return f'''
|
||||
Class ID: {self.current_class_id}
|
||||
Lecture ID: {self.current_lecture_id}
|
||||
Student ID: {self.current_student_id}
|
||||
Submission ID: {self.current_submission_id}
|
||||
'''
|
||||
|
||||
def log(log_level: hello_imgui.LogLevel, msg: str) -> None:
|
||||
time = datetime.now().strftime("%X")
|
||||
hello_imgui.log(log_level, f"[{time}] {msg}")
|
||||
|
||||
LOG_DEBUG = lambda msg: log(hello_imgui.LogLevel.debug, msg)
|
||||
LOG_INFO = lambda msg: log(hello_imgui.LogLevel.info, msg)
|
||||
LOG_WARNING = lambda msg: log(hello_imgui.LogLevel.warning, msg)
|
||||
LOG_ERROR = lambda msg: log(hello_imgui.LogLevel.error, msg)
|
||||
|
@ -1,34 +1,35 @@
|
||||
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
|
||||
Nele,Grundke,Female,23.5,13,16,28,20,17,21,18
|
||||
Anna,Grünewald,Female,12,14,16,29,16,15,19,9
|
||||
Yannik,Haupt,Male,18,6,14,21,13,2,9,0
|
||||
Janna,Heiny,Female,30,15,18,33,18,20,22,25
|
||||
Milena,Krieger,Female,30,15,18,33,20,20,21.5,26
|
||||
Julia,Limbach,Female,27.5,12,18,29,11,19,17.5,26
|
||||
Viktoria,Litza,Female,21.5,15,18,27,13,20,22,21
|
||||
Manthey,Leonie,Female,28.5,14,18,29,20,10,18,23
|
||||
Izabel,Mike,Female,29.5,15,15,35,11,4,19,21
|
||||
Lea,Noglik,Female,22.5,15,17,34,13,10,20,0
|
||||
Donika,Nuhiu,Female,31,13.5,18,35,14,10,17,18
|
||||
Julia,Renner,Female,27.5,10,14,0,20,17,11,20
|
||||
Fabian,Rothberger,Male,30.5,15,18,34,17,17,19,22
|
||||
Natascha,Rott,Female,29.5,12,18,32,19,20,21,26
|
||||
Isabel,Rudolf,Female,27.5,9,17,34,16,19,19,21
|
||||
Melina,Sablotny,Female,31,15,18,33,20,20,21,19
|
||||
Alea,Schleier,Female,27,14,18,34,16,18,21.5,22
|
||||
Flemming,Schur,Male,29.5,15,17,34,19,20,19,22
|
||||
Marie,Seeger,Female,27.5,15,18,32,14,9,17,22
|
||||
Lucy,Thiele,Female,27.5,15,18,27,20,17,19,18
|
||||
Lara,Troschke,Female,28.5,14,17,28,13,19,21,25
|
||||
Inga-Brit,Turschner,Female,25.5,14,18,34,20,16,19,22
|
||||
Alea,Unger,Female,30,12,18,31,20,20,21,22
|
||||
Marie,Wallbaum,Female,28.5,14,18,34,17,20,19,24
|
||||
Katharina,Walz,Female,31,15,18,31,19,19,17,24
|
||||
Xiaowei,Wang,Male,30.5,14,18,26,19,17,0,0
|
||||
Lilly-Lu,Warnken,Female,30,15,18,30,14,17,19,14
|
||||
First Name,Last Name,Sex,Tutorial 1,Tutorial 2,Extended Applications,Numpy & MatPlotLib,SciPy,Monte Carlo,Pandas & Seaborn,Folium,Statistical Test Methods,Data Analysis
|
||||
Abdalaziz,Abunjaila,Male,30.5,15,18,28,17,17,17,22,0,0
|
||||
Marleen,Adolphi,Female,29.5,15,18,32,19,20,17,24,23,0
|
||||
Sarina,Apel,Female,28.5,15,18,32,20,20,21,24,20,0
|
||||
Skofiare,Berisha,Female,29.5,13,18,34,20,17,20,26,16,0
|
||||
Aurela,Brahimi,Female,17.5,15,15.5,26,16,17,19,16,0,0
|
||||
Cam Thu,Do,Female,31,15,18,34,19,20,21.5,22,12,0
|
||||
Nova,Eib,Female,31,15,15,34,20,20,21,27,19,0
|
||||
Nele,Grundke,Female,23.5,13,16,28,20,17,21,18,22,0
|
||||
Anna,Grünewald,Female,12,14,16,29,16,15,19,9,0,0
|
||||
Yannik,Haupt,Male,18,6,14,21,13,2,9,0,0,0
|
||||
Janna,Heiny,Female,30,15,18,33,18,20,22,25,24,30
|
||||
Milena,Krieger,Female,30,15,18,33,20,20,21.5,26,20,0
|
||||
Julia,Limbach,Female,27.5,12,18,29,11,19,17.5,26,24,0
|
||||
Viktoria,Litza,Female,21.5,15,18,27,13,20,22,21,21,0
|
||||
Leonie,Manthey,Female,28.5,14,18,29,20,10,18,23,16,28
|
||||
Izabel,Mike,Female,29.5,15,15,35,11,15,19,21,21,27
|
||||
Lea,Noglik,Female,22.5,15,17,34,13,10,20,21,19,0
|
||||
Donika,Nuhiu,Female,31,13.5,18,35,14,10,17,18,19,6
|
||||
Julia,Renner,Female,27.5,10,14,32,20,17,11,20,24,0
|
||||
Fabian,Rothberger,Male,30.5,15,18,34,17,17,19,22,18,0
|
||||
Natascha,Rott,Female,29.5,12,18,32,19,20,21,26,23,0
|
||||
Isabel,Rudolf,Female,27.5,9,17,34,16,19,19,21,16,0
|
||||
Melina,Sablotny,Female,31,15,18,33,20,20,21,19,11,0
|
||||
Alea,Schleier,Female,27,14,18,34,16,18,21.5,22,15,22
|
||||
Flemming,Schur,Male,29.5,15,17,34,19,20,19,22,18,0
|
||||
Marie,Seeger,Female,27.5,15,18,32,14,9,17,22,9,0
|
||||
Lucy,Thiele,Female,27.5,15,18,27,20,17,19,18,22,0
|
||||
Lara,Troschke,Female,28.5,14,17,28,13,19,21,25,12,0
|
||||
Inga-Brit,Turschner,Female,25.5,14,18,34,20,16,19,22,17,0
|
||||
Alea,Unger,Female,30,12,18,31,20,20,21,22,15,21.5
|
||||
Marie,Wallbaum,Female,28.5,14,18,34,17,20,19,24,12,0
|
||||
Katharina,Walz,Female,31,15,18,31,19,19,17,24,17,14.5
|
||||
Xiaowei,Wang,Male,30.5,14,18,26,19,17,0,0,0,0
|
||||
Lilly-Lu,Warnken,Female,30,15,18,30,14,17,19,14,16,0
|
||||
|
|
Binary file not shown.
@ -5,6 +5,7 @@ sys.path.append('..')
|
||||
from model import *
|
||||
|
||||
df = pd.read_csv("Student_list.csv")
|
||||
df = df.dropna()
|
||||
courses = {
|
||||
'Tutorial 1': 31,
|
||||
'Tutorial 2': 15,
|
||||
@ -13,9 +14,17 @@ courses = {
|
||||
'SciPy': 20,
|
||||
'Monte Carlo': 20,
|
||||
'Pandas & Seaborn': 22,
|
||||
'Folium': 27
|
||||
'Folium': 27,
|
||||
'Statistical Test Methods': 24,
|
||||
'Data Analysis': 30
|
||||
}
|
||||
|
||||
print(df)
|
||||
|
||||
db.init("WiSe_24_25.db")
|
||||
db.connect()
|
||||
db.create_tables([Class, Student, Lecture, Submission])
|
||||
|
||||
# Create Class
|
||||
clas = Class.create(name='WiSe 24/25')
|
||||
#print(clas.id)
|
||||
|
BIN
assets/icons/db_icon.png
Normal file
BIN
assets/icons/db_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
@ -1,35 +0,0 @@
|
||||
from imgui_bundle import imgui, imgui_ctx
|
||||
from model import *
|
||||
|
||||
class ClassEditor:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.add_name = str()
|
||||
self.select = 0
|
||||
|
||||
def __call__(self):
|
||||
classes = Class.select()
|
||||
|
||||
with imgui_ctx.begin("Class 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]
|
223
database.py
Normal file
223
database.py
Normal file
@ -0,0 +1,223 @@
|
||||
# Custom
|
||||
from model import *
|
||||
from appstate import *
|
||||
|
||||
# External
|
||||
from imgui_bundle import (
|
||||
imgui,
|
||||
imgui_ctx,
|
||||
immapp,
|
||||
imgui_md,
|
||||
im_file_dialog,
|
||||
hello_imgui
|
||||
)
|
||||
|
||||
# Built In
|
||||
from typing import List
|
||||
import shelve
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
def file_info(path: Path) -> None:
|
||||
stat = path.stat()
|
||||
modified = datetime.fromtimestamp(stat.st_atime)
|
||||
created = datetime.fromtimestamp(stat.st_ctime)
|
||||
format = '%c'
|
||||
|
||||
data = {
|
||||
"File": path.name,
|
||||
"Size": f"{stat.st_size/100} KB",
|
||||
"Modified": modified.strftime(format),
|
||||
"Created": created.strftime(format)
|
||||
}
|
||||
|
||||
if imgui.begin_table("File Info", 2):
|
||||
imgui.table_setup_column(" ", 0)
|
||||
imgui.table_setup_column(" ")
|
||||
|
||||
for k, v in data.items():
|
||||
imgui.push_id(k)
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.text(k)
|
||||
imgui.table_next_column()
|
||||
imgui.text(v)
|
||||
imgui.pop_id()
|
||||
imgui.end_table()
|
||||
|
||||
@immapp.static(inited=False, res=False)
|
||||
def select_file(app_state: AppState):
|
||||
statics = select_file
|
||||
if not statics.inited:
|
||||
statics.res = None
|
||||
statics.current = None
|
||||
with shelve.open("state") as state:
|
||||
statics.current = Path(state["DB"])
|
||||
statics.inited = True
|
||||
|
||||
imgui_md.render("# Database Manager")
|
||||
file_info(statics.current)
|
||||
|
||||
if imgui.button("Open File"):
|
||||
im_file_dialog.FileDialog.instance().open("SelectDatabase", "Open Database", "Database File (*.json;*.db){.json,.db}")
|
||||
if im_file_dialog.FileDialog.instance().is_done("SelectDatabase"):
|
||||
if im_file_dialog.FileDialog.instance().has_result():
|
||||
statics.res = im_file_dialog.FileDialog.instance().get_result()
|
||||
LOG_INFO(f"Load File {statics.res}")
|
||||
im_file_dialog.FileDialog.instance().close()
|
||||
|
||||
if statics.res:
|
||||
filename = statics.res.filename()
|
||||
info = Path(statics.res.path())
|
||||
|
||||
imgui.separator()
|
||||
file_info(info)
|
||||
|
||||
file = None
|
||||
if imgui.button("Load"):
|
||||
if not db.is_closed():
|
||||
db.close()
|
||||
|
||||
if statics.res.extension() == '.json':
|
||||
file = filename.removesuffix('.json')
|
||||
file = file + '.db'
|
||||
db.init(file)
|
||||
db.connect(reuse_if_open=True)
|
||||
db.create_tables([Class, Student, Lecture, Submission])
|
||||
load_from_json(str(info))
|
||||
LOG_INFO(f"Successfully created {file}")
|
||||
|
||||
if statics.res.extension() == '.db':
|
||||
file = str(statics.res.path())
|
||||
db.init(file)
|
||||
db.connect(reuse_if_open=True)
|
||||
db.create_tables([Class, Student, Lecture, Submission])
|
||||
LOG_INFO(f"Successfully loaded {filename}")
|
||||
|
||||
with shelve.open("state") as state:
|
||||
state["DB"] = file
|
||||
app_state.update()
|
||||
statics.res = None
|
||||
|
||||
@immapp.static(inited=False)
|
||||
def table(app_state: AppState) -> None:
|
||||
statics = table
|
||||
if not statics.inited:
|
||||
statics.class_id = None
|
||||
statics.lectures = None
|
||||
statics.points = list()
|
||||
statics.inited = True
|
||||
|
||||
if statics.class_id != app_state.current_class_id:
|
||||
statics.class_id = app_state.current_class_id
|
||||
statics.lectures = Lecture.select().where(Lecture.class_id == statics.class_id)
|
||||
statics.data = dict()
|
||||
for student in Student.select().where(Student.class_id == statics.class_id):
|
||||
subs = Submission.select().where(Submission.student_id == student.id)
|
||||
points = [sub.points for sub in subs]
|
||||
statics.data[f"{student.prename} {student.surname}"] = points
|
||||
|
||||
if not statics.lectures:
|
||||
imgui.text("No Lecture queried")
|
||||
return
|
||||
|
||||
table_flags = (
|
||||
imgui.TableFlags_.row_bg.value
|
||||
| imgui.TableFlags_.borders.value
|
||||
| imgui.TableFlags_.resizable.value
|
||||
| imgui.TableFlags_.sizing_stretch_same.value
|
||||
)
|
||||
|
||||
if imgui.begin_table("Overview", len(statics.lectures)+1, table_flags):
|
||||
imgui.table_setup_column("Students")
|
||||
for n, lecture in enumerate(statics.lectures, start=1):
|
||||
imgui.table_setup_column(f"{n}. {lecture.title} ({lecture.points})")
|
||||
imgui.table_setup_scroll_freeze(1, 1)
|
||||
imgui.table_headers_row()
|
||||
|
||||
for k, v in statics.data.items():
|
||||
imgui.push_id(k)
|
||||
imgui.table_next_row()
|
||||
imgui.table_next_column()
|
||||
imgui.text(k)
|
||||
for points in v:
|
||||
imgui.table_next_column()
|
||||
if points.is_integer():
|
||||
points = int(points)
|
||||
imgui.text(str(points))
|
||||
imgui.pop_id()
|
||||
|
||||
imgui.end_table()
|
||||
|
||||
@immapp.static(inited=False)
|
||||
def class_editor() -> None:
|
||||
statics = class_editor
|
||||
if not statics.inited:
|
||||
statics.classes = None
|
||||
statics.selected = 0
|
||||
statics.inited = True
|
||||
|
||||
statics.classes = Class.select()
|
||||
|
||||
imgui_md.render("# Edit Classes")
|
||||
_, statics.selected = imgui.combo("Classes", statics.selected, [c.name for c in statics.classes])
|
||||
imgui.text(statics.classes[statics.selected].name)
|
||||
|
||||
def database_editor(app_state: AppState) -> None:
|
||||
class_editor()
|
||||
|
||||
def database_docking_splits() -> List[hello_imgui.DockingSplit]:
|
||||
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.3
|
||||
|
||||
# Log Space
|
||||
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.3
|
||||
|
||||
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.2
|
||||
|
||||
splits = [split_main_misc, split_main_command, split_main_command2]
|
||||
return splits
|
||||
|
||||
def set_database_editor_layout(app_state: AppState) -> List[hello_imgui.DockableWindow]:
|
||||
|
||||
file_dialog = hello_imgui.DockableWindow()
|
||||
file_dialog.label = "Database"
|
||||
file_dialog.dock_space_name = "MiscSpace"
|
||||
file_dialog.gui_function = lambda: select_file(app_state)
|
||||
|
||||
log = hello_imgui.DockableWindow()
|
||||
log.label = "Logs"
|
||||
log.dock_space_name = "CommandSpace2"
|
||||
log.gui_function = hello_imgui.log_gui
|
||||
|
||||
table_view = hello_imgui.DockableWindow()
|
||||
table_view.label = "Table"
|
||||
table_view.dock_space_name = "MainDockSpace"
|
||||
table_view.gui_function = lambda: table(app_state)
|
||||
|
||||
editor = hello_imgui.DockableWindow()
|
||||
editor.label = "Editor"
|
||||
editor.dock_space_name = "CommandSpace"
|
||||
editor.gui_function = lambda: database_editor(app_state)
|
||||
|
||||
return [
|
||||
file_dialog, log, table_view, editor
|
||||
]
|
||||
|
||||
def database_editor_layout(app_state: AppState) -> hello_imgui.DockingParams:
|
||||
docking_params = hello_imgui.DockingParams()
|
||||
docking_params.layout_name = "Database Editor"
|
||||
docking_params.docking_splits = database_docking_splits()
|
||||
docking_params.dockable_windows = set_database_editor_layout(app_state)
|
||||
return docking_params
|
15
datatypes.py
15
datatypes.py
@ -1,15 +0,0 @@
|
||||
from PIL import ImageColor
|
||||
from enum import IntEnum
|
||||
|
||||
# Global Color Pallet
|
||||
COLOR_BACKGROUND = tuple([e/255 for e in ImageColor.getcolor("#29132E","RGBA")])
|
||||
COLOR_1 = tuple([e/255 for e in ImageColor.getcolor("#321450","RGBA")])
|
||||
COLOR_2 = tuple([e/255 for e in ImageColor.getcolor("#860029","RGBA")])
|
||||
COLOR_3 = tuple([e/255 for e in ImageColor.getcolor("#DE004E","RGBA")])
|
||||
COLOR_TEXT = tuple([e/255 for e in ImageColor.getcolor("#F887FF","RGBA")])
|
||||
COLOR_TEXT_PASSED = tuple([e/255 for e in ImageColor.getcolor("#1AFE49","RGBA")])
|
||||
COLOR_TEXT_FAILED = tuple([e/255 for e in ImageColor.getcolor("#FF124F","RGBA")])
|
||||
|
||||
class LayoutOptions(IntEnum):
|
||||
EDITOR = 1
|
||||
GRAPHER = 2
|
926
demo_docking.py
926
demo_docking.py
@ -1,926 +0,0 @@
|
||||
# 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()
|
121
gui.py
121
gui.py
@ -1,47 +1,96 @@
|
||||
from imgui_bundle import imgui, immapp
|
||||
import glfw
|
||||
import OpenGL.GL as gl
|
||||
from datatypes import *
|
||||
from view import View
|
||||
# Custom
|
||||
from model import *
|
||||
from appstate import AppState
|
||||
|
||||
class GUI(object):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Layouts
|
||||
from analyzer import analyzer_layout
|
||||
from database import database_editor_layout
|
||||
|
||||
# self.io = imgui.get_io()
|
||||
# External
|
||||
from imgui_bundle import imgui, immapp, hello_imgui, ImVec2
|
||||
|
||||
# Global GUI Setting
|
||||
'''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'''
|
||||
# Built In
|
||||
import shelve
|
||||
from typing import List
|
||||
|
||||
self.view = View()
|
||||
def menu_bar(runner_params: hello_imgui.RunnerParams) -> None:
|
||||
hello_imgui.show_app_menu(runner_params)
|
||||
hello_imgui.show_view_menu(runner_params)
|
||||
if imgui.begin_menu("File"):
|
||||
clicked, _ = imgui.menu_item("Open", "", False)
|
||||
if clicked:
|
||||
pass
|
||||
imgui.end_menu()
|
||||
|
||||
def header(self):
|
||||
imgui.set_next_window_size(io.display_size.x, io.display_size.y*0.03)
|
||||
imgui.set_next_window_position(0, io.display_size.y*0.02)
|
||||
def status_bar(app_state: AppState) -> None:
|
||||
imgui.text("Student Analyzer by @DerGrumpf")
|
||||
|
||||
with imgui.begin("HEADER", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE | imgui.WINDOW_NO_TITLE_BAR):
|
||||
imgui.set_window_font_scale(1.3)
|
||||
text = "Student Analyzer"
|
||||
ww = imgui.get_window_size().x
|
||||
tw = imgui.calc_text_size(text).x
|
||||
imgui.set_cursor_pos_x((ww - tw) * 0.5)
|
||||
imgui.text("Student Analyzer")
|
||||
def main() -> None:
|
||||
app_state = AppState()
|
||||
|
||||
def __call__(self):
|
||||
self.view()
|
||||
# Load Database
|
||||
with shelve.open("state") as state:
|
||||
v = state.get("DB")
|
||||
if v:
|
||||
db.init(v)
|
||||
db.connect()
|
||||
db.create_tables([Class, Student, Lecture, Submission])
|
||||
app_state.update()
|
||||
|
||||
# Set Asset Folder
|
||||
#hello_imgui.set_assets_folder()
|
||||
|
||||
# Set Theme
|
||||
|
||||
# Set Window Params
|
||||
runner_params = hello_imgui.RunnerParams()
|
||||
runner_params.app_window_params.window_title = "Analyzer"
|
||||
runner_params.imgui_window_params.menu_app_title = "Analyzer"
|
||||
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
|
||||
|
||||
# Load Fonts
|
||||
#runner_params.callbacks.load_additional_fonts = lambda: f()
|
||||
|
||||
# Status Bar & Main Menu
|
||||
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
|
||||
runner_params.imgui_window_params.show_status_bar = True
|
||||
# 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: menu_bar(runner_params)
|
||||
# Optional: add items to Hello ImGui default App menu
|
||||
#runner_params.callbacks.show_app_menu_items = show_app_menu_items
|
||||
runner_params.callbacks.show_status = lambda: status_bar(app_state)
|
||||
|
||||
# Application layout
|
||||
runner_params.imgui_window_params.default_imgui_window_type = (
|
||||
hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
|
||||
)
|
||||
runner_params.imgui_window_params.enable_viewports = True
|
||||
|
||||
runner_params.docking_params = analyzer_layout(app_state)
|
||||
runner_params.alternative_docking_layouts = [
|
||||
database_editor_layout(app_state)
|
||||
]
|
||||
|
||||
# Save App Settings
|
||||
runner_params.ini_folder_type = hello_imgui.IniFolderType.app_user_config_folder
|
||||
runner_params.ini_filename = "Analyzer/Analyzer.ini"
|
||||
|
||||
# Uncomment if layout will stay the same at start
|
||||
runner_params.docking_params.layout_condition = hello_imgui.DockingLayoutCondition.application_start
|
||||
|
||||
# Run it
|
||||
add_ons_params = immapp.AddOnsParams()
|
||||
add_ons_params.with_markdown = True
|
||||
add_ons_params.with_implot = True
|
||||
add_ons_params.with_implot3d = True
|
||||
immapp.run(runner_params, add_ons_params)
|
||||
|
||||
if __name__ == "__main__":
|
||||
immapp.run(
|
||||
gui_function=GUI(),
|
||||
window_title="Student Analyzer",
|
||||
window_size_auto=True,
|
||||
with_implot=True,
|
||||
with_markdown=True
|
||||
)
|
||||
main()
|
||||
|
@ -1,44 +0,0 @@
|
||||
from imgui_bundle import imgui
|
||||
from datatypes import *
|
||||
|
||||
from model import *
|
||||
|
||||
class LectureEditor:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.select = 0
|
||||
self.add_lecture_text = str()
|
||||
self.add_lecture_points = 0
|
||||
|
||||
def __call__(self, clas: Class):
|
||||
id = clas.id if clas else None
|
||||
lectures = Lecture.select().where(Lecture.class_id == id) if id else None
|
||||
|
||||
with imgui.begin("Lecture Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
imgui.text("Add Lecture")
|
||||
_, self.add_lecture_text = imgui.input_text("Title", self.add_lecture_text)
|
||||
if self.add_lecture_points < 0:
|
||||
self.add_lecture_points = 0
|
||||
_, self.add_lecture_points = imgui.input_int("Points", self.add_lecture_points)
|
||||
|
||||
if imgui.button("Add"):
|
||||
Lecture.create(
|
||||
title=self.add_lecture_text,
|
||||
points=self.add_lecture_points,
|
||||
class_id=id
|
||||
)
|
||||
|
||||
imgui.separator()
|
||||
|
||||
if not lectures:
|
||||
imgui.text("No Lectures could be queried")
|
||||
return
|
||||
|
||||
for n, lecture in enumerate(lectures, start=1):
|
||||
display = f"{n}. {lecture.title}"
|
||||
opened, _ = imgui.selectable(display, self.select == n-1)
|
||||
if opened:
|
||||
self.select = n-1
|
||||
|
||||
return lectures[self.select]
|
37
main_menu.py
37
main_menu.py
@ -1,37 +0,0 @@
|
||||
from imgui_bundle import imgui, imgui_ctx
|
||||
|
||||
from datatypes import *
|
||||
|
||||
class MainMenu:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.new = False
|
||||
self.new_text = str()
|
||||
|
||||
def __call__(self):
|
||||
if self.new:
|
||||
self.create_new_file()
|
||||
|
||||
with imgui_ctx.begin_main_menu_bar() as main_menu_bar:
|
||||
if main_menu_bar:
|
||||
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_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_simple(l.name.title())
|
||||
if clicked:
|
||||
return l
|
||||
|
||||
def create_new_file(self):
|
||||
pass
|
6
model.py
6
model.py
@ -123,10 +123,6 @@ def dump_to_json(fp: Path, indent=None) -> None:
|
||||
with open(fp, "w") as file:
|
||||
json.dump(d, file, indent=indent)
|
||||
|
||||
db.init('test.db')
|
||||
db.connect()
|
||||
db.create_tables([Class, Student, Lecture, Submission])
|
||||
|
||||
def main():
|
||||
import random
|
||||
# Generate Test Data
|
||||
@ -166,4 +162,6 @@ def main():
|
||||
|
||||
if __name__ == "__main__":
|
||||
# main()
|
||||
db.init('wise_24_25.db')
|
||||
db.connect()
|
||||
dump_to_json(Path().cwd()/"TEST.json")
|
||||
|
@ -1,41 +0,0 @@
|
||||
from model import *
|
||||
from imgui_bundle import imgui
|
||||
|
||||
class StudentEditor:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.prename = str()
|
||||
self.surname = str()
|
||||
self.sex = True
|
||||
self.current = 0
|
||||
|
||||
def __call__(self):
|
||||
with imgui.begin("Student Editor", False, imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
imgui.text("Add Student")
|
||||
|
||||
_, self.prename = imgui.input_text("First Name", self.prename)
|
||||
_, self.surname = imgui.input_text("Last Name", self.surname)
|
||||
|
||||
with imgui.begin_group():
|
||||
if imgui.radio_button("Male", self.sex):
|
||||
self.sex = True
|
||||
|
||||
imgui.same_line()
|
||||
|
||||
if imgui.radio_button("Female", not self.sex):
|
||||
self.sex = False
|
||||
|
||||
classes = Class.select()
|
||||
if classes:
|
||||
_, self.current = imgui.combo("Classes", self.current, [c.name for c in classes])
|
||||
|
||||
if imgui.button("Confirm") and classes:
|
||||
Student.create(
|
||||
prename=self.prename,
|
||||
surname=self.surname,
|
||||
sex='Male' if self.sex else 'Female',
|
||||
class_id = classes[self.current]
|
||||
)
|
||||
self.prename = str()
|
||||
self.surname = str()
|
@ -1,78 +0,0 @@
|
||||
import numpy as np
|
||||
from imgui_bundle import imgui
|
||||
import random
|
||||
|
||||
from datatypes import *
|
||||
|
||||
from model import *
|
||||
|
||||
class StudentGraph:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.classes = None
|
||||
self.students = None
|
||||
|
||||
self.select_class = 0
|
||||
self.select_student = 0
|
||||
|
||||
def __call__(self):
|
||||
|
||||
self.classes = Class.select()
|
||||
self.students = Student.select().where(Student.class_id == self.classes[self.select_class].id) if self.classes else None
|
||||
|
||||
# Setup Data
|
||||
submissions = Submission.select().where(Submission.student_id == self.students[self.select_student].id) if self.students else None
|
||||
data = np.array([submission.points/Lecture.get_by_id(submission.lecture_id).points*100 for submission in submissions], dtype=np.float32) if submissions else None
|
||||
|
||||
with imgui.begin("Student Graph", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
|
||||
w, h = imgui.get_content_region_available()
|
||||
|
||||
if not isinstance(data, np.ndarray):
|
||||
imgui.text("No Submission available for this Student")
|
||||
else:
|
||||
imgui.plot_histogram(
|
||||
"##Data", data, overlay_text="Performance per Lecture (in %)",
|
||||
scale_min=0.0, scale_max=100,
|
||||
graph_size=(w, h*0.69)
|
||||
)
|
||||
|
||||
with imgui.begin_child("Select Class", w/3, h*0.3, border=True):
|
||||
if not self.classes:
|
||||
imgui.text("No Class could be queried")
|
||||
else:
|
||||
for n, c in enumerate(self.classes, start = 1):
|
||||
display = f"{n}. {c.name}"
|
||||
opened, _ = imgui.selectable(display, self.select_class == n-1)
|
||||
if opened:
|
||||
self.select_class = n-1
|
||||
self.select_student = 0
|
||||
|
||||
imgui.same_line()
|
||||
|
||||
with imgui.begin_child("Select Student", w/3, h*0.3, border=True):
|
||||
if not self.students:
|
||||
imgui.text("No Student in this class")
|
||||
else:
|
||||
for n, s in enumerate(self.students, start = 1):
|
||||
display = f"{n}. {s.prename} {s.surname}"
|
||||
opened, _ = imgui.selectable(display, self.select_student == n-1)
|
||||
if opened:
|
||||
self.select_student = n-1
|
||||
|
||||
imgui.same_line()
|
||||
|
||||
with imgui.begin_child("Student Info", w/3, h*0.3, border=True):
|
||||
if not submissions:
|
||||
imgui.text("No Submissions for this Student")
|
||||
else:
|
||||
for n, s in enumerate(submissions):
|
||||
lecture = Lecture.get_by_id(s.lecture_id)
|
||||
points = s.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)
|
@ -1,77 +0,0 @@
|
||||
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 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)
|
||||
|
||||
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()
|
||||
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)
|
||||
|
@ -1,27 +0,0 @@
|
||||
from model import *
|
||||
from imgui_bundle import imgui
|
||||
|
||||
class StudentList:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.select: int = 0
|
||||
|
||||
def __call__(self, clas: Class):
|
||||
id = clas.id if clas else None
|
||||
students = Student.select().where(Student.class_id == id) if id else None
|
||||
|
||||
with imgui.begin("Student Table", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
if not students:
|
||||
imgui.text("No Dataset could be queried")
|
||||
return
|
||||
|
||||
for n, student in enumerate(students, start=1):
|
||||
display = f"{n}. {student.prename} {student.surname}"
|
||||
opened, _ = imgui.selectable(display, self.select == n-1)
|
||||
if opened:
|
||||
self.select = n-1
|
||||
|
||||
return students[self.select]
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
import numpy as np
|
||||
from imgui_bundle import imgui
|
||||
|
||||
from model import *
|
||||
|
||||
class StudentRanking:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def __call__(self):
|
||||
students = Student.select().where(Student.class_id == 1)
|
||||
lectures = Lecture.select().where(Lecture.class_id == 1)
|
||||
|
||||
overall_points = sum([l.points for l in lectures])
|
||||
|
||||
ranking = list()
|
||||
avg = list()
|
||||
for s in students:
|
||||
rank = sum([sub.points for sub in Submission.select().where(Submission.student_id == s.id)])/overall_points
|
||||
ranking.append((f"{s.prename} {s.surname}", rank))
|
||||
avg.append(rank)
|
||||
ranking = sorted(ranking, key=lambda x: x[1], reverse=True)
|
||||
avg = sum(avg)/len(avg)
|
||||
|
||||
flag = True
|
||||
|
||||
with imgui.begin("Student Ranking", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
for n, rank in enumerate(ranking, start=1):
|
||||
if rank[1] < avg and flag:
|
||||
imgui.separator()
|
||||
flag = False
|
||||
|
||||
imgui.text(f"{n}. {rank[0]} {rank[1]:.1%}")
|
||||
|
||||
imgui.separator()
|
||||
imgui.text(f"Average: {avg:.1%}")
|
||||
|
@ -1,66 +0,0 @@
|
||||
from imgui_bundle import imgui
|
||||
from model import *
|
||||
|
||||
class SubmissionEditor:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.current_lecture = 0
|
||||
self.current_student = 0
|
||||
self.points = 0
|
||||
|
||||
def __call__(self, clas: Class):
|
||||
id = clas.id if clas else None
|
||||
lectures = Lecture.select().where(Lecture.class_id == id) if id else None
|
||||
students = Student.select().where(Student.class_id == id) if id else None
|
||||
|
||||
with imgui.begin("Submission Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||
imgui.text("Add Submission")
|
||||
|
||||
if not lectures:
|
||||
imgui.text("No Lectures queried")
|
||||
return
|
||||
|
||||
if not students:
|
||||
imgui.text("No Students queried")
|
||||
return
|
||||
|
||||
_, self.current_lecture = imgui.combo("Lecture", self.current_lecture, [f"{l.title} ({l.points})" for l in lectures])
|
||||
_, self.current_student = imgui.combo("Student", self.current_student, [f"{s.prename} {s.surname}" for s in students])
|
||||
|
||||
if self.points < 0:
|
||||
self.points = 0
|
||||
|
||||
max = lectures[self.current_lecture].points
|
||||
if self.points > max:
|
||||
self.points = max
|
||||
|
||||
_, self.points = imgui.input_float("Points", self.points, format='%.1f', step=0.5, step_fast=1.0)
|
||||
|
||||
if imgui.button("Add"):
|
||||
if not Submission.select().where(
|
||||
Submission.student_id == students[self.current_student].id and
|
||||
Submission.lecture_id == lectures[self.current_lecture].id
|
||||
):
|
||||
Submission.create(
|
||||
student_id=students[self.current_student].id,
|
||||
lecture_id=lectures[self.current_lecture].id,
|
||||
points=self.points
|
||||
)
|
||||
|
||||
imgui.same_line()
|
||||
|
||||
if imgui.button("Update"):
|
||||
submission = Submission.select().where(
|
||||
Submission.student_id == students[self.current_student].id and
|
||||
Submission.lecture_id == lectures[self.current_lecture].id
|
||||
).get()
|
||||
submission.points = self.points
|
||||
submission.save()
|
||||
|
||||
imgui.same_line()
|
||||
|
||||
if imgui.button("Delete"):
|
||||
Submission.delete().where(
|
||||
Submission.student_id == students[self.current_student].id and
|
||||
Submission.lecture_id == lectures[self.current_lecture].id
|
||||
).execute()
|
65
view.py
65
view.py
@ -1,65 +0,0 @@
|
||||
from imgui_bundle import imgui, ImVec2
|
||||
|
||||
from datatypes import *
|
||||
|
||||
from main_menu import MainMenu
|
||||
|
||||
from database_editor import DatabaseEditor
|
||||
|
||||
from student_info import StudentInfo
|
||||
|
||||
def set_layout(size: tuple, pos: tuple) -> None:
|
||||
io = imgui.get_io()
|
||||
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_info = StudentInfo()
|
||||
|
||||
def set_layout(self):
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
self.student_info()
|
||||
|
||||
class EditorLayout:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.database_editor = DatabaseEditor()
|
||||
|
||||
def set_layout(self):
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
self.database_editor()
|
||||
|
||||
class View:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
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:
|
||||
self.current = option
|
||||
|
||||
if self.current == LayoutOptions.EDITOR:
|
||||
self.editor()
|
||||
|
||||
if self.current == LayoutOptions.GRAPHER:
|
||||
self.grapher()
|
Loading…
Reference in New Issue
Block a user