Added: Layout Switcher
This commit is contained in:
parent
59e00cb812
commit
7635df42b4
36
class_editor.py
Normal file
36
class_editor.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import imgui
|
||||||
|
|
||||||
|
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.begin("Class Editor", False, imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
||||||
|
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]
|
15
datatypes.py
Normal file
15
datatypes.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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
|
89
gui.py
Normal file
89
gui.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import imgui
|
||||||
|
import glfw
|
||||||
|
import OpenGL.GL as gl
|
||||||
|
from imgui.integrations.glfw import GlfwRenderer
|
||||||
|
from datatypes import *
|
||||||
|
from view import View
|
||||||
|
|
||||||
|
def impl_glfw_init(window_name="Grapher Tool", width=1280, height=720):
|
||||||
|
if not glfw.init():
|
||||||
|
print("Could not initialize OpenGL context")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# OS X supports only forward-compatible core profiles from 3.2
|
||||||
|
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
|
||||||
|
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
|
||||||
|
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
|
||||||
|
|
||||||
|
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE)
|
||||||
|
|
||||||
|
# Create a windowed mode window and its OpenGL context
|
||||||
|
window = glfw.create_window(int(width), int(height), window_name, None, None)
|
||||||
|
glfw.make_context_current(window)
|
||||||
|
|
||||||
|
if not window:
|
||||||
|
glfw.terminate()
|
||||||
|
print("Could not initialize Window")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
return window
|
||||||
|
|
||||||
|
class GUI(object):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# Window States
|
||||||
|
self.window = impl_glfw_init()
|
||||||
|
gl.glClearColor(*COLOR_BACKGROUND)
|
||||||
|
imgui.create_context()
|
||||||
|
self.impl = GlfwRenderer(self.window)
|
||||||
|
self.io = imgui.get_io()
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
self.view = View()
|
||||||
|
self.loop()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 loop(self):
|
||||||
|
while not glfw.window_should_close(self.window):
|
||||||
|
glfw.poll_events()
|
||||||
|
self.impl.process_inputs()
|
||||||
|
imgui.new_frame()
|
||||||
|
|
||||||
|
self.view()
|
||||||
|
|
||||||
|
#imgui.show_test_window()
|
||||||
|
|
||||||
|
imgui.render()
|
||||||
|
|
||||||
|
gl.glClearColor(*COLOR_BACKGROUND)
|
||||||
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||||
|
|
||||||
|
self.impl.render(imgui.get_draw_data())
|
||||||
|
glfw.swap_buffers(self.window)
|
||||||
|
|
||||||
|
self.impl.shutdown()
|
||||||
|
glfw.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
gui = GUI()
|
45
lecture_editor.py
Normal file
45
lecture_editor.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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]
|
250
main.py
250
main.py
@ -1,24 +1,6 @@
|
|||||||
import imgui
|
import imgui
|
||||||
import glfw
|
|
||||||
import OpenGL.GL as gl
|
|
||||||
from imgui.integrations.glfw import GlfwRenderer
|
|
||||||
from PIL import ImageColor
|
|
||||||
from dataclasses import dataclass
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Lecture:
|
|
||||||
name: str
|
|
||||||
points: float
|
|
||||||
max_points: int
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Student:
|
|
||||||
name: str
|
|
||||||
jupyter_id: str
|
|
||||||
project: str
|
|
||||||
lectures: list
|
|
||||||
|
|
||||||
phil = Student(
|
phil = Student(
|
||||||
"Phil Keier", "772fb04b24caa68fd38a05ec2a22e62b", "Geomapping",
|
"Phil Keier", "772fb04b24caa68fd38a05ec2a22e62b", "Geomapping",
|
||||||
[Lecture("1. Tutorial 1", 28.5, 31), Lecture("2. Tutorial 2", 4.5, 15), Lecture("3. Extended Application", 18, 18)]
|
[Lecture("1. Tutorial 1", 28.5, 31), Lecture("2. Tutorial 2", 4.5, 15), Lecture("3. Extended Application", 18, 18)]
|
||||||
@ -33,239 +15,7 @@ kathi = Student(
|
|||||||
)
|
)
|
||||||
|
|
||||||
students = [phil, nova, kathi]
|
students = [phil, nova, kathi]
|
||||||
# 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="Grapher Tool", width=1280, height=720):
|
|
||||||
if not glfw.init():
|
|
||||||
print("Could not initialize OpenGL context")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# OS X supports only forward-compatible core profiles from 3.2
|
|
||||||
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
|
|
||||||
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
|
|
||||||
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
|
|
||||||
|
|
||||||
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE)
|
|
||||||
|
|
||||||
# Create a windowed mode window and its OpenGL context
|
|
||||||
window = glfw.create_window(int(width), int(height), window_name, None, None)
|
|
||||||
glfw.make_context_current(window)
|
|
||||||
|
|
||||||
if not window:
|
|
||||||
glfw.terminate()
|
|
||||||
print("Could not initialize Window")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
return window
|
|
||||||
|
|
||||||
|
|
||||||
class GUI(object):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.backgroundColor = COLOR_BACKGROUND
|
|
||||||
self.window = impl_glfw_init()
|
|
||||||
gl.glClearColor(*self.backgroundColor)
|
|
||||||
imgui.create_context()
|
|
||||||
self.impl = GlfwRenderer(self.window)
|
|
||||||
|
|
||||||
# App states
|
|
||||||
self.select_student: int = 0
|
|
||||||
self.select_lecture: int = 0
|
|
||||||
self.lectures: list = list()
|
|
||||||
self.add_lecture_text = str()
|
|
||||||
self.add_lecture_points = 0
|
|
||||||
self.edit_lecture_title = str()
|
|
||||||
self.edit_lecture_points = 0
|
|
||||||
|
|
||||||
# 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 * font_scaling_factor)
|
|
||||||
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.4)
|
|
||||||
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"{overall_points}/{overall_max} | {round(overall_points/overall_max*100, 1)}%"
|
|
||||||
imgui.text_colored(student.name, *COLOR_TEXT)
|
|
||||||
w, h = imgui.get_window_size()
|
|
||||||
imgui.progress_bar(overall_points/overall_max, (w*0.5, h*0.1), s)
|
|
||||||
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(io.display_size.x*0.15, io.display_size.y*0.95)
|
|
||||||
imgui.set_next_window_position(0, io.display_size.y*0.05)
|
|
||||||
|
|
||||||
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 n, student in enumerate(students):
|
|
||||||
opened, _ = imgui.selectable(student.name, self.select_student == n)
|
|
||||||
if opened:
|
|
||||||
self.select_student = n
|
|
||||||
|
|
||||||
|
|
||||||
def student_graph(self, student: Student):
|
|
||||||
|
|
||||||
# Window Position and Sizing
|
|
||||||
io = imgui.get_io()
|
|
||||||
imgui.set_next_window_size(io.display_size.x*0.55, io.display_size.y*0.4)
|
|
||||||
imgui.set_next_window_position(io.display_size.x*0.15, io.display_size.y*0.05)
|
|
||||||
|
|
||||||
# Setup Data
|
|
||||||
data = numpy.array([float(lecture.points) / float(lecture.max_points) * 100 for lecture in student.lectures], dtype=numpy.float32)
|
|
||||||
|
|
||||||
with imgui.begin("Student Graph", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
|
||||||
if not students:
|
|
||||||
imgui.text("No Dataset selected")
|
|
||||||
return
|
|
||||||
imgui.plot_histogram(
|
|
||||||
"##Data", data, overlay_text="Performance per Lecture (in %)",
|
|
||||||
scale_min=0.0, scale_max=100,
|
|
||||||
graph_size=imgui.get_content_region_available()
|
|
||||||
)
|
|
||||||
|
|
||||||
def header(self):
|
|
||||||
|
|
||||||
# Window Position and Sizing
|
|
||||||
io = imgui.get_io()
|
|
||||||
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)
|
|
||||||
|
|
||||||
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 editor(self):
|
|
||||||
|
|
||||||
# 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.45)
|
|
||||||
lecture = None
|
|
||||||
|
|
||||||
with imgui.begin("Lecture Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE):
|
|
||||||
with imgui.begin_group():
|
|
||||||
if not self.lectures:
|
|
||||||
imgui.text("No Lecture Added")
|
|
||||||
else:
|
|
||||||
for n, lecture in enumerate(self.lectures):
|
|
||||||
opened, _ = imgui.selectable(lecture.name, self.select_lecture == n, width=imgui.get_window_size().x*0.3)
|
|
||||||
if opened:
|
|
||||||
self.select_lecture = n
|
|
||||||
lecture = self.lectures[self.select_lecture]
|
|
||||||
self.edit_lecture_title = lecture.name
|
|
||||||
self.edit_lecture_points = lecture.max_points
|
|
||||||
|
|
||||||
imgui.same_line(spacing=int(imgui.get_window_size().x * 0.05))
|
|
||||||
|
|
||||||
with imgui.begin_group():
|
|
||||||
imgui.push_item_width(imgui.get_window_size().x * 0.2)
|
|
||||||
imgui.text("Add Lecture")
|
|
||||||
_, self.add_lecture_text = imgui.input_text(" ", self.add_lecture_text)
|
|
||||||
_, self.add_lecture_points = imgui.input_int("", self.add_lecture_points)
|
|
||||||
|
|
||||||
if self.add_lecture_points < 0:
|
|
||||||
self.add_lecture_points = 0
|
|
||||||
|
|
||||||
clicked = imgui.button("Confirm")
|
|
||||||
if clicked:
|
|
||||||
self.lectures.append(Lecture(self.add_lecture_text, 0, self.add_lecture_points))
|
|
||||||
self.add_lecture_points = 0
|
|
||||||
self.add_lecture_text = str()
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
if lecture:
|
|
||||||
imgui.same_line(spacing=int(imgui.get_window_size().x * 0.05))
|
|
||||||
with imgui.begin_group():
|
|
||||||
_, self.edit_lecture_title = imgui.input_text(" ", self.edit_lecture_title)
|
|
||||||
_, self.edit_lecture_points = imgui.input_int("", self.edit_lecture_points)
|
|
||||||
if self.edit_lecture_points < 0:
|
|
||||||
self.edit_lecture_points = 0
|
|
||||||
|
|
||||||
def main_menu(self):
|
|
||||||
with imgui.begin_main_menu_bar() as main_menu_bar:
|
|
||||||
if main_menu_bar:
|
|
||||||
with imgui.begin_menu("File", True) as file_menu:
|
|
||||||
if file_menu.opened:
|
|
||||||
imgui.menu_item("New", " ", False, True)
|
|
||||||
imgui.menu_item("Open", " ", False, True)
|
|
||||||
imgui.menu_item("Save", " ", False, True)
|
|
||||||
imgui.menu_item("Save as", " ", False, True)
|
|
||||||
|
|
||||||
def loop(self):
|
|
||||||
while not glfw.window_should_close(self.window):
|
|
||||||
glfw.poll_events()
|
|
||||||
self.impl.process_inputs()
|
|
||||||
imgui.new_frame()
|
|
||||||
|
|
||||||
self.main_menu()
|
|
||||||
self.header()
|
|
||||||
self.table(students)
|
|
||||||
self.student_info(students[self.select_student])
|
|
||||||
self.student_graph(students[self.select_student])
|
|
||||||
self.editor()
|
|
||||||
#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__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
gui = GUI()
|
gui = GUI()
|
||||||
|
|
||||||
|
26
main_menu.py
Normal file
26
main_menu.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import imgui
|
||||||
|
|
||||||
|
from datatypes import *
|
||||||
|
|
||||||
|
class MainMenu:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
with imgui.begin_main_menu_bar() as main_menu_bar:
|
||||||
|
if main_menu_bar:
|
||||||
|
with imgui.begin_menu("File", True) as file_menu:
|
||||||
|
if file_menu.opened:
|
||||||
|
imgui.menu_item("New", " ", False, True)
|
||||||
|
imgui.menu_item("Open", " ", False, True)
|
||||||
|
imgui.menu_item("Save", " ", False, True)
|
||||||
|
imgui.menu_item("Save as", " ", False, True)
|
||||||
|
with imgui.begin_menu("View", True) as view_menu:
|
||||||
|
if view_menu.opened:
|
||||||
|
with imgui.begin_menu("Change Layout", True) as open_layout_menu:
|
||||||
|
if open_layout_menu.opened:
|
||||||
|
layout_options = list(LayoutOptions)
|
||||||
|
for n, l in enumerate(layout_options):
|
||||||
|
clicked, _ = imgui.menu_item(l.name.title(), None, False, True)
|
||||||
|
if clicked:
|
||||||
|
return l
|
61
model.py
Normal file
61
model.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from peewee import *
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
db = SqliteDatabase('test.db')
|
||||||
|
|
||||||
|
class BaseModel(Model):
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
class Class(BaseModel):
|
||||||
|
name = CharField()
|
||||||
|
created_at = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
class Student(BaseModel):
|
||||||
|
prename = CharField()
|
||||||
|
surname = CharField()
|
||||||
|
sex = CharField()
|
||||||
|
class_id = ForeignKeyField(Class, backref='class')
|
||||||
|
created_at = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
class Lecture(BaseModel):
|
||||||
|
title = CharField()
|
||||||
|
points = IntegerField()
|
||||||
|
class_id = ForeignKeyField(Class, backref='class')
|
||||||
|
created_at = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
class Submission(BaseModel):
|
||||||
|
student_id = ForeignKeyField(Student, backref='student')
|
||||||
|
lecture_id = ForeignKeyField(Lecture, backref='lecture')
|
||||||
|
points = FloatField()
|
||||||
|
created_at = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
db.connect()
|
||||||
|
db.create_tables([Class, Student, Lecture, Submission])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import random
|
||||||
|
# Generate Test Data
|
||||||
|
class1 = Class.create(name="WiSe 22/23")
|
||||||
|
class2 = Class.create(name="WiSe 23/24")
|
||||||
|
class3 = Class.create(name="WiSe 24/25")
|
||||||
|
|
||||||
|
phil = Student.create(prename="Phil", surname="Keier", sex="Male", class_id=class1.id)
|
||||||
|
calvin = Student.create(prename="Calvin", surname="Brandt", sex="Male", class_id=class2.id)
|
||||||
|
nova = Student.create(prename="Nova", surname="Eib", sex="Female", class_id=class1.id)
|
||||||
|
kathi = Student.create(prename="Katharina", surname="Walz", sex="Female", class_id=class3.id)
|
||||||
|
victoria = Student.create(prename="Victoria", surname="Möller", sex="Female", class_id=class3.id)
|
||||||
|
|
||||||
|
lec1 = Lecture.create(title="Tutorial 1", points=30, class_id=class1.id)
|
||||||
|
lec2 = Lecture.create(title="Tutorial 1", points=30, class_id=class3.id)
|
||||||
|
lec3 = Lecture.create(title="Tutorial 2", points=20, class_id=class1.id)
|
||||||
|
lec4 = Lecture.create(title="Tutorial 2", points=20, class_id=class2.id)
|
||||||
|
lec5 = Lecture.create(title="Extended Applications", points=44, class_id=class1.id)
|
||||||
|
|
||||||
|
sub1_phil = Submission.create(student_id=phil.id, lecture_id=lec1.id, points=random.randint(0, lec1.points))
|
||||||
|
sub2_phil = Submission.create(student_id=phil.id, lecture_id=lec3.id, points=random.randint(0, lec3.points))
|
||||||
|
sub3_phil = Submission.create(student_id=phil.id, lecture_id=lec5.id, points=random.randint(0, lec5.points))
|
||||||
|
sub1_nova = Submission.create(student_id=nova.id, lecture_id=lec1.id, points=random.randint(0, lec1.points))
|
||||||
|
sub2_nova = Submission.create(student_id=nova.id, lecture_id=lec3.id, points=random.randint(0, lec3.points))
|
||||||
|
sub1_kathi = Submission.create(student_id=kathi.id, lecture_id=lec3.id, points=random.randint(0, lec3.points))
|
||||||
|
sub1_vici = Submission.create(student_id=victoria.id, lecture_id=lec2.id, points=random.randint(0, lec2.points))
|
42
student_editor.py
Normal file
42
student_editor.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import imgui
|
||||||
|
|
||||||
|
from model import *
|
||||||
|
|
||||||
|
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()
|
27
student_graph.py
Normal file
27
student_graph.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import imgui
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
|
||||||
|
from model import *
|
||||||
|
|
||||||
|
class StudentGraph:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
# Setup Data
|
||||||
|
submissions = Submission.select().where(Submission.student_id == 1)
|
||||||
|
data = np.array([submission.points/Lecture.get_by_id(submission.lecture_id).points*100 for submission in submissions], dtype=np.float32)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
imgui.plot_histogram(
|
||||||
|
"##Data", data, overlay_text="Performance per Lecture (in %)",
|
||||||
|
scale_min=0.0, scale_max=100,
|
||||||
|
graph_size=(w, h*0.9)
|
||||||
|
)
|
||||||
|
|
||||||
|
imgui.button("Text")
|
||||||
|
|
60
student_info.py
Normal file
60
student_info.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import imgui
|
||||||
|
from datatypes import *
|
||||||
|
|
||||||
|
from model import *
|
||||||
|
|
||||||
|
class StudentInfo:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __call__(self, student: Student):
|
||||||
|
submissions = Submission.select().where(Submission.student_id == student.id) if student else None
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
w, h = imgui.get_window_size()
|
||||||
|
imgui.text_colored(f"{student.prename} {student.surname}", *COLOR_TEXT)
|
||||||
|
|
||||||
|
content = Class.get_by_id(student.class_id).name
|
||||||
|
text_size = imgui.calc_text_size(content)
|
||||||
|
imgui.same_line(position=w-1.5*text_size.x)
|
||||||
|
imgui.text(content)
|
||||||
|
|
||||||
|
if submissions:
|
||||||
|
overall_points = sum([s.points for s in submissions])
|
||||||
|
if overall_points.is_integer():
|
||||||
|
overall_points = int(overall_points)
|
||||||
|
overall_max = sum([lectures.points for lectures in [Lecture.get_by_id(s.lecture_id) for s in submissions]])
|
||||||
|
percentile = overall_points / overall_max
|
||||||
|
imgui.progress_bar(percentile, (w*0.5, h*0.05), f"{overall_points}/{overall_max} {percentile:.1%}")
|
||||||
|
|
||||||
|
content = "Delete"
|
||||||
|
if submissions:
|
||||||
|
text_size = imgui.calc_text_size(content)
|
||||||
|
imgui.same_line(position=w-2*text_size.x)
|
||||||
|
if imgui.button(content):
|
||||||
|
# Delete all Submissions related to that Student
|
||||||
|
#for submission in submissions:
|
||||||
|
# submission.delete().execute()
|
||||||
|
# Delete Student
|
||||||
|
#student.delete().execute()
|
||||||
|
return
|
||||||
|
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
if not submissions:
|
||||||
|
imgui.text("No Submission for this Student")
|
||||||
|
|
||||||
|
for n, submission in enumerate(submissions, start=1):
|
||||||
|
lecture = Lecture.get_by_id(submission.lecture_id)
|
||||||
|
|
||||||
|
points = submission.points
|
||||||
|
if points.is_integer():
|
||||||
|
points = int(points)
|
||||||
|
|
||||||
|
display = f"{n}. {lecture.title} {points}/{lecture.points}"
|
||||||
|
COLOR = COLOR_TEXT_PASSED if points > lecture.points*0.3 else COLOR_TEXT_FAILED
|
||||||
|
imgui.text_colored(display, *COLOR)
|
28
student_list.py
Normal file
28
student_list.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import imgui
|
||||||
|
|
||||||
|
from model import *
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
67
submission_editor.py
Normal file
67
submission_editor.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
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()
|
86
view.py
Normal file
86
view.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import imgui
|
||||||
|
|
||||||
|
from datatypes import *
|
||||||
|
|
||||||
|
from main_menu import MainMenu
|
||||||
|
|
||||||
|
from student_editor import StudentEditor
|
||||||
|
from student_list import StudentList
|
||||||
|
from student_info import StudentInfo
|
||||||
|
from class_editor import ClassEditor
|
||||||
|
from lecture_editor import LectureEditor
|
||||||
|
from submission_editor import SubmissionEditor
|
||||||
|
|
||||||
|
from student_graph import StudentGraph
|
||||||
|
|
||||||
|
def set_layout(size: tuple, pos: tuple) -> None:
|
||||||
|
io = imgui.get_io()
|
||||||
|
imgui.set_next_window_size(
|
||||||
|
io.display_size.x*size[0],
|
||||||
|
io.display_size.y*size[1]
|
||||||
|
)
|
||||||
|
imgui.set_next_window_position(
|
||||||
|
io.display_size.x*pos[0],
|
||||||
|
io.display_size.y*pos[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
class GrapherLayout:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.student_graph = StudentGraph()
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
set_layout((0.7, 0.4), (0.1, 0.1))
|
||||||
|
self.student_graph()
|
||||||
|
|
||||||
|
class EditorLayout:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.io = imgui.get_io()
|
||||||
|
|
||||||
|
self.student_editor = StudentEditor()
|
||||||
|
self.student_list = StudentList()
|
||||||
|
self.student_info = StudentInfo()
|
||||||
|
self.class_editor = ClassEditor()
|
||||||
|
self.lecture_editor = LectureEditor()
|
||||||
|
self.submission_editor = SubmissionEditor()
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
set_layout((0.3, 0.3), (0.7, 0.4))
|
||||||
|
clas = self.class_editor()
|
||||||
|
|
||||||
|
set_layout((0.5, 0.3), (0.2, 0.4))
|
||||||
|
self.student_editor()
|
||||||
|
|
||||||
|
set_layout((0.2, 0.98), (0, 0.02))
|
||||||
|
student = self.student_list(clas)
|
||||||
|
|
||||||
|
set_layout((0.3, 0.38), (0.7, 0.02))
|
||||||
|
self.student_info(student)
|
||||||
|
|
||||||
|
set_layout((0.5, 0.3), (0.2, 0.7))
|
||||||
|
self.lecture_editor(clas)
|
||||||
|
|
||||||
|
set_layout((0.5, 0.3), (0.2, 0.1))
|
||||||
|
self.submission_editor(clas)
|
||||||
|
|
||||||
|
class View:
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.io = imgui.get_io()
|
||||||
|
self.current = LayoutOptions.GRAPHER
|
||||||
|
self.main_menu = MainMenu()
|
||||||
|
self.editor = EditorLayout()
|
||||||
|
self.grapher = GrapherLayout()
|
||||||
|
|
||||||
|
def __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