diff --git a/README.md b/README.md index 377a040..b59a1a4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ -# grapher +# Grapher -A Grapher for "Einführung in die Programmierung für Nichtinformatiker*innen" \ No newline at end of file +A Grapher for "Einführung in die Programmierung für Nicht Informatiker*innen" + +writin in Python usign Dear ImGui diff --git a/assets/MPLUSRounded1c-Regular.ttf b/assets/MPLUSRounded1c-Regular.ttf new file mode 100644 index 0000000..c8a6a55 Binary files /dev/null and b/assets/MPLUSRounded1c-Regular.ttf differ diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..213e088 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,20 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Custom window] +Pos=355,219 +Size=321,302 +Collapsed=0 + +[Window][Dear ImGui Demo] +Pos=323,104 +Size=550,680 +Collapsed=0 + +[Window][Student Table] +Pos=5,9 +Size=251,142 +Collapsed=0 + diff --git a/main.py b/main.py new file mode 100644 index 0000000..4bab58d --- /dev/null +++ b/main.py @@ -0,0 +1,159 @@ +import imgui +import glfw +import OpenGL.GL as gl +from imgui.integrations.glfw import GlfwRenderer +from PIL import ImageColor +from dataclasses import dataclass + +@dataclass +class Lecture: + name: str + points: float + max_points: int + +@dataclass +class Student: + name: str + jupyter_id: str + project: str + lectures: list + +# 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")]) + +def impl_glfw_init(window_name="minimal ImGui/GLFW3 example", width=1280, height=720): + if not glfw.init(): + print("Could not initialize OpenGL context") + exit(1) + + # OS X supports only forward-compatible core profiles from 3.2 + glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) + glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) + glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) + + glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE) + + # Create a windowed mode window and its OpenGL context + window = glfw.create_window(int(width), int(height), window_name, None, None) + glfw.make_context_current(window) + + if not window: + glfw.terminate() + print("Could not initialize Window") + exit(1) + + return window + + +class GUI(object): + def __init__(self): + super().__init__() + self.backgroundColor = COLOR_BACKGROUND + self.window = impl_glfw_init() + gl.glClearColor(*self.backgroundColor) + imgui.create_context() + self.impl = GlfwRenderer(self.window) + + + # 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 + io = imgui.get_io() + io.fonts.add_font_from_file_ttf( + "assets/MPLUSRounded1c-Regular.ttf", font_size_in_pixels + ) + io.font_global_scale /= font_scaling_factor + + self.loop() + + def student_info(self, student: Student): + + # Window Position and Sizing + io = imgui.get_io() + imgui.set_next_window_size(io.display_size.x*0.3, io.display_size.y*0.3) + imgui.set_next_window_position(io.display_size.x*0.7, io.display_size.y*0.05) + + # Student accumulated Info + overall_points = sum([lecture.points for lecture in student.lectures]) + if overall_points.is_integer(): + overall_points = int(overall_points) + overall_max = sum([lecture.max_points for lecture in student.lectures]) + + with imgui.begin("Student Info", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + if not student: + imgui.text("No Student selected") + return + + s = f"{student.name} {overall_points}/{overall_max}" + imgui.text_colored(s, *COLOR_TEXT) + imgui.text("Jupyter ID:") + imgui.text_colored(student.jupyter_id.rjust(4+len(student.jupyter_id), " "), *COLOR_TEXT) + + imgui.separator() + imgui.text("Project:") + imgui.text_colored(student.project.rjust(4+len(student.project), " "), *COLOR_TEXT) + imgui.text("Lectures:") + for lecture in student.lectures: + COLOR = COLOR_TEXT_PASSED if lecture.points >= lecture.max_points/3 else COLOR_TEXT_FAILED + s = f"{lecture.name}: {lecture.points}/{lecture.max_points}" + imgui.text_colored(s.rjust(4+len(s), " "), *COLOR) + + def table(self, students: list): + + # Window Position and Sizing + io = imgui.get_io() + imgui.set_next_window_size(max(min(io.display_size.x*0.2, 200), 120), io.display_size.y*0.9) + imgui.set_next_window_position(io.display_size.y*0.01, io.display_size.y*0.05) + + # Boiler + selected = [False for _ in range(len(students))] + visible = True + + 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 selected") + return + + for i, student in enumerate(students): + _, selected[i] = imgui.selectable(student, selected[i]) + + def loop(self): + + phil = Student( + "Phil Keier", "772fb04b24caa68fd38a05ec2a22e62b", "Geomapping", + [Lecture("1. Tutorial 1", 28.5, 31), Lecture("2. Tutorial 2", 4.5, 15), Lecture("3. Extended Application", 18, 18)] + ) + + while not glfw.window_should_close(self.window): + glfw.poll_events() + self.impl.process_inputs() + imgui.new_frame() + + self.table(["Phil Keier", "Nova Eib", "Katharina Walz"]) + self.student_info(phil) + #imgui.show_test_window() + + imgui.render() + + gl.glClearColor(*self.backgroundColor) + gl.glClear(gl.GL_COLOR_BUFFER_BIT) + + self.impl.render(imgui.get_draw_data()) + glfw.swap_buffers(self.window) + + self.impl.shutdown() + glfw.terminate() + + +if __name__ == "__main__": + + gui = GUI() +