def move_desk(self, start, end): """Moves a desk from start to end if end is out of the screen, we remove the desk""" max_row = int(AssetManager.getInstance().config( "size", "default_room_rows")) max_col = int(AssetManager.getInstance().config( "size", "default_room_columns")) if len(start) == 0: return id_desk_start = self.mod_bdd.get_desk_id_in_course_by_coords( self.main_ctrl.id_course, start[0], start[1]) if 0 <= end[0] < max_row and 0 <= end[1] < max_col: # Destination is in the canvas id_desk_end = self.mod_bdd.get_desk_id_in_course_by_coords( self.main_ctrl.id_course, end[0], end[1]) if id_desk_end == 0 and id_desk_start != 0: # We just change the coordinates self.mod_bdd.move_desk_by_id(id_desk_start, end[0], end[1]) # We update the view self.v_canvas.move_tile(id_desk_start, end) elif id_desk_end != 0 and id_desk_start != 0: # We swap the two desks self.mod_bdd.move_desk_by_id(id_desk_start, end[0], end[1]) self.mod_bdd.move_desk_by_id(id_desk_end, start[0], start[1]) # We update the view self.v_canvas.move_tile(id_desk_start, end) self.v_canvas.move_tile(id_desk_end, start, True) else: self.mod_bdd.remove_desk_by_id(id_desk_start) self.show_course() self.__bdd.commit() self.v_canvas.repaint()
def on_edit_config(self) -> None: """ Displays the edition dialog for application settings """ dlg = SettingsEditionDialog() if dlg.exec_(): if dlg.restore_default(): AssetManager.getInstance().restore_default_settings() else: AssetManager.getInstance().save_config(dlg.new_config()) self.status_bar.showMessage(tr("acknowledge_changes"), 3000) self.repaint() if dlg.need_restart(): restart_confirm = VConfirmDialog(self, "need_restart") restart_confirm.ok_btn.setText(tr("restart_now")) restart_confirm.ok_btn.setFixedSize(QSize(105, 33)) restart_confirm.cancel_btn.setText(tr("restart_later")) restart_confirm.cancel_btn.setFixedSize(QSize(105, 33)) if restart_confirm.exec_(): self.reboot_requested = True self.close() self.restart()
def __draw_dragged_tile(self, painter, tile, x, y): """ Draws the given tile under the mouse position :param painter: painter object :param tile: tile data object :param x: real mouse position x :param y: real mouse position y """ if not self.hovered: # If there is no tile under the dragged one, we draw a light gray rect below it # Convert x and y to get the hovered empty tile position hov_x = x // self.desk_h_size * self.desk_h_size hov_y = y // self.desk_w_size * self.desk_w_size hov_rect = self.__get_rect_at(hov_y, hov_x) painter.fillRect( hov_rect, QColor(AssetManager.getInstance().config( 'colors', 'hovered_empty_tile'))) rect = QRect( QPoint(PADDING + y - self.desk_w_size / 2, PADDING + x - self.desk_h_size / 2), QPoint(y + self.desk_w_size / 2 - PADDING, x + self.desk_h_size / 2 - PADDING)) painter.fillRect( rect, QColor(AssetManager.getInstance().config('colors', 'dragged_tile'))) painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap, f"{tile.lastname()}\n{tile.firstname()}")
def __init__(self, sig_move_animation_ended): """ Application's main canvas, in which is drawn desks and student's names. :param sig_move_animation_ended: signal to trigger when the move animation ends :type sig_move_animation_ended: Signal """ QWidget.__init__(self) self.desk_h_size = int(AssetManager.getInstance().config( 'size', 'desk_height')) self.desk_w_size = int(AssetManager.getInstance().config( 'size', 'desk_width')) self.setAutoFillBackground(True) self.__tiles = {} # All drawn tiles self.tmp = {} self.hovered = False self.is_view_students = True self.__do_switch = False self.__is_config = False self.sig_canvas_click = None # Signal triggered when a click is performed on a desk self.sig_desk_selected = None # Signal triggered when a non empty desk is selected self.sig_canvas_drag = None # Signal triggered when a drag operation is performed on the canvas self.sig_select_tile = None # pushed by the controller. emits when tile selection change. self.sig_tile_info = None # Signal triggered when a right-click is performed on a desk. Info is to be displayed self.sig_move_animation_ended = sig_move_animation_ended # Tracking for drag/drop operation self.__click_pos = () # Stored (row, column) position self.__mouse_pos = () # Stored (x, y) mouse position # Signals self.sig_move_ended.connect(self.on_move_ended) self.__running_animations = 0 self.update_timer = None # Timer running only during animations to perform the UI update self.nb_rows = int(AssetManager.getInstance().config( 'size', 'default_room_rows')) self.nb_columns = int(AssetManager.getInstance().config( 'size', 'default_room_columns')) self.setFixedSize(self.desk_w_size * self.nb_columns, self.desk_h_size * self.nb_rows) self.__init_style()
def __init_style(self): """ Sets the background of this canvas widget """ color = AssetManager.getInstance().config( 'colors', 'room_bg') if self.__is_config else "white" pal = QPalette() pal.setColor(QPalette.Background, QColor(color)) self.setPalette(pal)
def __init__(self, sig_move_animation_ended): """ Widget containing the main canvas (classroom), the board's position and topic selection. :param sig_move_animation_ended: Signal to trigger when the move animation ended """ QWidget.__init__(self) # Widgets self.v_canvas = ViewCanvas(sig_move_animation_ended) # Central canvas self.view_students = ViewTeacherDeskLabel( tr("perspective_student_txt"), AssetManager.getInstance().config('colors', 'board_bg')) self.view_teacher = ViewTeacherDeskLabel( tr("perspective_teacher_txt"), AssetManager.getInstance().config('colors', 'board_bg')) # Layout self.__set_layout()
def student_attr_pick(self): """Randomly chooses a student among not selected ones""" desks_id = self.get_unselected_occupied_desks_id() list_id_attr = self.gui.sidewidget.attributes().selected_attributes() if not desks_id or len(list_id_attr) != 1: # should be always be true otherwise the hutton is disabled self.gui.status_bar.showMessage("Houston, we have a problem !") return id_attr = list_id_attr[0] attr_type = self.mod_bdd.get_attribute_type_from_id(id_attr) id_topic = self.mod_bdd.get_topic_id_by_course_id( self.main_ctrl.id_course) canditates = dict() colors = AssetManager.getInstance().config('colors', 'attr_colors').split() for d_id in desks_id: desk = self.mod_bdd.get_desk_by_id(d_id) val = self.mod_bdd.get_attribute_value(desk.id_student, id_attr, id_topic) # Determine a order key depending on the attribute type if attr_type == EAttributesTypes.TEXT.value: # for text attr, we select empty attributes first key = 1 if val else 0 elif attr_type == EAttributesTypes.MARK.value: # for mark attribute, the key is the number of marks key = len(val.split()) elif attr_type == EAttributesTypes.COLOR.value: # for color value, key is the order in config.ini if val in colors: key = colors.index(val) else: key = -1 elif attr_type == EAttributesTypes.COUNTER.value: # for counter attribute, key is the counter value key = 0 if val == "" else int(val) else: key = 0 # Update the dictionary of candidates if key in canditates: canditates[key].append(d_id) else: canditates[key] = [d_id] # Now we pick a desk by choosing in the minimum key value minkey = min(canditates.keys()) desk_id = choice(canditates[minkey]) # Make selection self.do_desk_selection_change(desk_id, True) # change selection on app self.on_desk_selection_changed_on_app(desk_id, True) # change selection on web
def export_csv(self, file_path: str) -> None: """ Saves the attributes table in a CSV format at the specified file path """ attributes, students, data = self.get_attributes_matrix() # build the attribute type dict attr_type = dict() for a in attributes: attr_type[a[0]] = self.mod_bdd.get_attribute_type_from_id(a[0]) # generate CSV file with open(file_path, 'w', newline='') as csvfile: writer = csv.writer(csvfile, delimiter=AssetManager.getInstance().config( "main", "csv_separator"), quoting=csv.QUOTE_MINIMAL) # Write first row first_row = ['Nom Prenom'] for a in attributes: first_row.append(a[1]) writer.writerow(first_row) # Write datas for s in students: row = [s[1]] for a in attributes: id_a, id_s = a[0], s[0] if (id_a, id_s) in data: # We have a data to export if attr_type[id_a] == EAttributesTypes.TEXT.value: row.append(data[(id_a, id_s)]) elif attr_type[id_a] == EAttributesTypes.MARK.value: mark_list = data[(id_a, id_s)].split() try: somme = sum(map(int, mark_list)) row.append(somme / len(mark_list)) except: row.append("") elif attr_type[id_a] == EAttributesTypes.COLOR.value: hexcol = data[(id_a, id_s)].name().lower() col_name = COLOR_DICT[ hexcol] if hexcol in COLOR_DICT else hexcol row.append(col_name) elif attr_type[id_a] == EAttributesTypes.COUNTER.value: row.append(int(data[(id_a, id_s)])) else: # No data in this cell row.append("") # write current row writer.writerow(row)
def __init__(self, parent): """ Confirm dialog for dangerous actions :param parent: gui's main window """ QDialog.__init__(self, parent) # QR Code s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.has_internet = True try: s.connect(('8.8.8.8', 1)) # connect() for UDP does not send packets, this will require an internet connection # IP and Port local_ip_address = s.getsockname()[0] s.close() port = AssetManager.getInstance().config('webapp', 'port') # get tmp folder qr_path = tempfile.mktemp() s = f"http://{local_ip_address}:{port}/mobile" # String which represents the QR code self.url = pyqrcode.create(s) # Generate QR code self.url.png(qr_path, scale=6) # Create and save the QR png file # Widget self.qr = QLabel() # Label that contains the QR pix = QPixmap(QImage(qr_path)) self.qr.setPixmap(pix) # Layout layout = QVBoxLayout() layout.setMargin(0) layout.addWidget(self.qr, alignment=Qt.AlignCenter) layout.addWidget(InfoToolTip("qr_tip"), alignment=Qt.AlignCenter) layout.addSpacing(5) self.setLayout(layout) self.setStyleSheet("background: white;") except OSError: self.has_internet = False
def __init__(self, parent, current_val): """ Colors editor :param parent: gui's main window :param current_val: current field actual value (to set by default) """ VDialogEdit.__init__(self, parent, current_val) self.colors = AssetManager.getInstance().config( "colors", "attr_colors").split() self.btns = [] layout = QHBoxLayout() for c in self.colors: b = ColorButton(c, self.__set_selection, c == current_val.upper()) self.btns.append(b) layout.addWidget(b) self.main_layout.addLayout(layout, 0, 0, 1, 2)
def import_pronote(self) -> None: groups = self.mod_bdd.get_groups() dlg = DialogImportCsv(self.gui, ["Nouveau Groupe"] + groups) if dlg.exec_(): name_group = dlg.selected_group() file_path = dlg.selected_file() if name_group == "Nouveau Groupe": f = file_path.split("/")[-1].upper() name_group = f[0:f.index(".CSV")] csv_sep = AssetManager.getInstance().config( "main", "csv_separator") names = import_csv(file_path, csv_sep) id_group = self.mod_bdd.create_group(name_group) order = 0 for std in names: self.mod_bdd.insert_student_in_group_id( std[1], std[0], order, id_group) order += 1 self.__bdd.commit() self.show_all_groups()
def auto_place(self): """Autoplacement of students on the free tiles""" max_row = int(AssetManager.getInstance().config( "size", "default_room_rows")) max_col = int(AssetManager.getInstance().config( "size", "default_room_columns")) group_name = self.mod_bdd.get_group_name_by_id(self.main_ctrl.id_group) list_idstd = self.gui.sidewidget.students().selected_students() if not list_idstd: list_students = self.mod_bdd.get_students_in_group(group_name) else: list_students = [ self.mod_bdd.get_student_by_id(i) for i in list_idstd ] students_ids = {s.id for s in list_students} to_be_placed = len(list_students) list_available_desks = [] list_to_remove = [] for row in range(max_row): for col in range(max_col): id_desk = self.mod_bdd.get_desk_id_in_course_by_coords( self.main_ctrl.id_course, row, col) if id_desk != 0: student = self.mod_bdd.get_student_by_desk_id(id_desk) if student is None: # We have a free spot list_available_desks.append((id_desk, row, col)) else: list_to_remove.append(student.id) # Adjust the number of students to place for s in list_to_remove: if s in students_ids: to_be_placed -= 1 info = None if to_be_placed > len(list_available_desks): info = str(to_be_placed - len(list_available_desks)) + tr("seats_are_missing") self.gui.status_bar.showMessage(info) else: self.gui.status_bar.showMessage(tr("seats_are_OK"), 3000) index_std = 0 for dsk in list_available_desks: while index_std < len(list_students) and list_students[ index_std].id in list_to_remove: index_std += 1 if index_std >= len(list_students): break student = list_students[index_std] # update the model self.mod_bdd.set_student_in_desk_by_id(student.id, dsk[0]) # update the view self.v_canvas.set_student(dsk[0], student.firstname, student.lastname) self.v_canvas.repaint() index_std += 1 self.__bdd.commit() if info is not None: VInfoDialog(self.gui, info).exec_()
def run(self): asset_manager = AssetManager.getInstance() socket_io.run(flask_app, port=asset_manager.config('webapp', 'port'), host='0.0.0.0')
def get_bdd_connection(): bdd_path, _ = AssetManager.getInstance().bdd_path() bdd = sqlite3.connect(bdd_path) return ModBdd(bdd)
def paintEvent(self, event): """ Draws the desks and students' names given the self.tiles list """ painter = QPainter(self) color = AssetManager.getInstance().config( 'colors', 'room_grid') if self.__is_config else "white" pen = QPen() pen.setColor(QColor(color)) pen.setWidth(2) painter.setPen(pen) # Draw the grid for r in range(self.nb_rows): painter.drawLine( QPoint(0, r * self.desk_h_size), QPoint(self.desk_w_size * self.nb_columns, r * self.desk_h_size)) for c in range(self.nb_columns): painter.drawLine( QPoint(c * self.desk_w_size, 0), QPoint(c * self.desk_w_size, self.desk_h_size * self.nb_rows)) # Update painter color and font size for tiles pen.setColor( QColor(AssetManager.getInstance().config('colors', 'tile_text'))) painter.setPen(pen) font = QFont() font.setPixelSize( int(AssetManager.getInstance().config('size', 'font_size'))) painter.setFont(font) # Current tile selected by the mouse tile_selected_pos = self.__convert_point( self.__mouse_pos[0], self.__mouse_pos[1]) if self.__mouse_pos else None tile_selected = None self.hovered = False # Drawing of all the tiles for t in list(self.__tiles.values()): y, x = self.__relative_mouse_position(t.real_position()) if self.__relative_grid_position( t.grid_position() ) == self.__click_pos and self.__is_config: # If the tile is selected tile_selected = t color = QColor(AssetManager.getInstance().config( 'colors', 'drag_selected_tile')) elif self.__relative_grid_position( t.grid_position() ) == tile_selected_pos and self.__is_config: # If the mouse is hover self.hovered = True color = QColor(AssetManager.getInstance().config( 'colors', 'hovered_tile')) elif t.is_selected(): color = QColor(AssetManager.getInstance().config( 'colors', 'selected_tile')) else: # Regular tile color = QColor(AssetManager.getInstance().config( 'colors', 'tile')) rect = self.__get_rect_at(y, x) painter.fillRect(rect, color) painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap, f"{t.lastname()}\n{t.firstname()}") # Dragged tile if self.__click_pos != self.__relative_grid_position(tile_selected_pos) \ and tile_selected and self.__mouse_pos and self.__is_config: # If the mouse is no longer hover the clicked tile we draw the dragged tile self.__draw_dragged_tile(painter, tile_selected, self.__mouse_pos[0], self.__mouse_pos[1])
def __init__(self): QDialog.__init__(self) self.setWindowTitle(tr("btn_config")) self.setFixedSize(QSize(700, 670)) # Retrieve current settings self.settings = AssetManager.getInstance().config_to_dico( AssetManager.getInstance().get_config_parser()) self.__restart_needed = False self.__restore_required = False # Version self.lab_version = QLabel(self.settings['main']['version']) # Language self.combo_language = QComboBox() self.combo_language.addItems(list(self.languages.keys())) for lang in self.languages: # Look for the current language to select it if self.languages[lang] == self.settings['main']['language']: self.combo_language.setCurrentText(lang) break # CSV separator self.csv_sep_edit = QLineEdit() self.csv_sep_edit.setMaxLength(2) self.csv_sep_edit.setFixedWidth(25) self.csv_sep_edit.setAlignment(Qt.AlignCenter) self.csv_sep_edit.setText(self.settings['main']['csv_separator']) # BDD path self.btn_bdd_path = QPushButton(self.settings['main']['bdd_path']) self.btn_bdd_path.clicked.connect(self.choose_bdd_path) # Port self.wepapp_port = QSpinBox() self.wepapp_port.setMinimum(1024) self.wepapp_port.setMaximum(65535) self.wepapp_port.setValue(int(self.settings['webapp']['port'])) # Colors self.tile_color = ColorChooser(self.settings['colors']['tile']) self.hovered_tile_color = ColorChooser( self.settings['colors']['hovered_tile']) self.hovered_empty_tile_color = ColorChooser( self.settings['colors']['hovered_empty_tile']) self.dragged_tile_color = ColorChooser( self.settings['colors']['dragged_tile']) self.drag_selected_tile_color = ColorChooser( self.settings['colors']['drag_selected_tile']) self.selected_tile_color = ColorChooser( self.settings['colors']['selected_tile']) self.tile_text_color = ColorChooser( self.settings['colors']['tile_text']) self.room_bg_color = ColorChooser(self.settings['colors']['room_bg']) self.room_grid_color = ColorChooser( self.settings['colors']['room_grid']) self.main_bg_color = ColorChooser(self.settings['colors']['main_bg']) self.board_bg_color = ColorChooser(self.settings['colors']['board_bg']) self.attr_colors = "" # Chosen colors self.attributes_colors_chooser = AttrColorsChooser( self.settings['colors']['attr_colors'].split()) # Sizes (unmodifiable) self.unmodifiable = QLabel(tr("unmodifiable_data")) self.unmodifiable.setAlignment(Qt.AlignCenter) self.desk_size = QLineEdit(self.settings['size']['desk']) self.desk_size.setEnabled(False) self.desk_size.setFixedWidth(50) self.grid_rows = QLineEdit(self.settings['size']['default_room_rows']) self.grid_rows.setEnabled(False) self.grid_rows.setFixedWidth(50) self.grid_cols = QLineEdit( self.settings['size']['default_room_columns']) self.grid_cols.setEnabled(False) self.grid_cols.setFixedWidth(50) # --- Buttons --- # Confirm button self.ok_btn = QPushButton(tr("btn_save")) self.ok_btn.clicked.connect(self.accept) self.ok_btn.setFocus() # Cancel button self.cancel_btn = QPushButton(tr("btn_cancel")) self.cancel_btn.clicked.connect(self.reject) # Restore defaults button self.restore_btn = QPushButton(tr("btn_restore")) self.restore_btn.clicked.connect(self.__restore) self.__set_layout()
def sort_desks(self, sort_type="Z"): """Sort students by their position detects clusters of students to make a clever sort""" def build_matrix(): matrix = [] for row in range(max_row): line = [] for col in range(max_col): id_desk = self.mod_bdd.get_desk_id_in_course_by_coords( self.main_ctrl.id_course, row, col) if id_desk != 0: student = self.mod_bdd.get_student_by_desk_id(id_desk) if student is not None: line.append(student.id) else: line.append(-1) # empty desk else: line.append(0) # no desk matrix.append(line) return matrix def search_cluster(coords, row, col): """search all cells in a cluster from row, col position""" if not (0 <= row < max_row and 0 <= col < max_col): return False if (row, col) in visited: return False if student_matrix[row][col] == 0: return False if coords in clusters: clusters[coords].append((row, col)) else: clusters[coords] = [(row, col)] visited.add((row, col)) search_cluster(coords, row - 1, col - 1) search_cluster(coords, row - 1, col) search_cluster(coords, row - 1, col + 1) search_cluster(coords, row, col - 1) search_cluster(coords, row, col + 1) search_cluster(coords, row + 1, col - 1) search_cluster(coords, row + 1, col) search_cluster(coords, row + 1, col + 1) return True def type_Z(): # we sort the students using the clusters in a simple row by row pattern ic, ir = 0, 0 while (ir, ic) in clusters: while (ir, ic) in clusters: for coords in clusters[(ir, ic)]: # we add all students in the cluster sortlist.append(student_matrix[coords[0]][coords[1]]) ic += 1 ic = 0 ir += 1 def type_2(): # we sort the students using the clusters in 2 pattern ic, ir = 0, 0 alt = True while (ir, ic) in clusters: # gets the number of cols in this row while (ir, ic) in clusters: ic += 1 nb_cols = ic for ic in range(nb_cols): if alt: ic_alt = ic else: ic_alt = nb_cols - ic - 1 # reverse the list when we walk right to left clusters[(ir, ic_alt)] = clusters[(ir, ic_alt)][::-1] for coords in clusters[(ir, ic_alt)]: # we add all students in the cluster sortlist.append(student_matrix[coords[0]][coords[1]]) alt = not alt ic = 0 ir += 1 def type_U(): # we sort the students using the clusters in U pattern ic, ir = 0, 0 alt = True while (ir, ic) in clusters: # gets the number of rows in this col while (ir, ic) in clusters: ir += 1 nb_rows = ir for ir in range(nb_rows): ir_alt = ir if alt else nb_rows - ir - 1 for coords in clusters[(ir_alt, ic)]: # we add all students in the cluster sortlist.append(student_matrix[coords[0]][coords[1]]) alt = not alt ir = 0 ic += 1 self.gui.status_bar.showMessage(tr("grp_action_sort_by_place"), 3000) max_row = int(AssetManager.getInstance().config( "size", "default_room_rows")) max_col = int(AssetManager.getInstance().config( "size", "default_room_columns")) infty = max_row * max_col + 1 group_name = self.mod_bdd.get_group_name_by_id(self.main_ctrl.id_group) # First we initialize the sort key for the group to infty group_students_id = self.mod_bdd.get_students_in_group( self.mod_bdd.get_group_name_by_id(self.main_ctrl.id_group)) for std in group_students_id: self.mod_bdd.update_student_order_with_id(std.id, infty) student_matrix = build_matrix() clusters = dict() visited = set() # populating clusters dictionnary ro = 0 for r in range(max_row): co = 0 empty = True for c in range(max_col): if search_cluster((ro, co), r, c): co += 1 empty = False if not empty: ro += 1 # Now we get an ordered list of students matching the desk disposition sortlist = [] # this list is modified in one of the following function if sort_type == "Z": type_Z() elif sort_type == "2": type_2() else: type_U() # At last, we update the sort key to re-order the list orderkey = 0 for s in sortlist: if s != -1: # -1 is an empty desk self.mod_bdd.update_student_order_with_id(s, orderkey) orderkey += 1 self.__bdd.commit() self.main_ctrl.on_config_changed() self.synchronize_canvas_selection_with_side_list()
def __init__(self): """ Application main controller. """ QObject.__init__(self) # Create the Views self.gui = ViewMainFrame(self.sig_quit, self.sig_config_mode_changed, self.sig_export_csv) self.v_canvas = self.gui.central_widget.classroom_tab.v_canvas # BDD connection bdd_path, bdd_exists = AssetManager.getInstance().bdd_path() if not bdd_exists: bp = QFileDialog.getExistingDirectory(self.gui, tr("select_db")) if bp == "" or not path.isdir(bp): self.mod_bdd = None return bdd_path = path.normpath(bp + "/sdc_db") if path.isfile(bdd_path): self.__bdd = sqlite3.connect(bdd_path) else: # we initialize a new BDD if not VConfirmDialog(self.gui, "confirm_db_creation").exec_(): self.mod_bdd = None return self.__bdd = self.initialize_bdd(bdd_path) config = AssetManager.getInstance().get_config_parser() config.set('main', 'bdd_path', bdd_path) AssetManager.getInstance().save_config(config) else: self.__bdd = sqlite3.connect(bdd_path) self.mod_bdd = ModBdd(self.__bdd) self.gui.set_bdd_version(self.mod_bdd.get_version()) # Create secondary controllers self.attr_ctrl = AttrController(self, self.__bdd) self.course_ctrl = CourseController(self, self.__bdd) self.group_ctrl = GroupController(self, self.__bdd) # Plugs the signals into the views self.v_canvas.sig_select_tile = self.sig_select_tile self.gui.central_widget.sig_shuffle = self.sig_shuffle self.gui.sig_quit = self.sig_quit self.v_canvas.sig_canvas_click = self.sig_canvas_click self.v_canvas.sig_desk_selected = self.sig_desk_selected self.v_canvas.sig_canvas_drag = self.sig_canvas_drag self.v_canvas.sig_tile_info = self.sig_canvas_right_click self.gui.sidewidget.courses( ).sig_course_changed = self.sig_course_changed self.gui.sidewidget.courses( ).courses_toolbar.add_widget.sig_new_element = self.sig_create_course self.gui.sidewidget.courses( ).sig_topic_changed = self.sig_topic_changed self.gui.sidewidget.students( ).students_toolbar.sig_combo_changed = self.sig_student_group_changed ViewMenuButton.sig_action = self.sig_action_triggered self.gui.maintoolbar.sig_TBbutton = self.sig_TBbutton self.gui.sidewidget.students( ).students_toolbar.create_field.sig_create = self.sig_create_grp_std self.gui.sidewidget.attributes( ).attributes_toolbar.add_widget.sig_new_element = self.sig_create_attribute self.gui.sidewidget.attributes( ).attributes_toolbar.add_widget.sig_delete = self.sig_delete_attributes self.gui.sidewidget.courses( ).courses_toolbar.sig_delete = self.sig_delete_course self.gui.sidewidget.attributes( ).sig_selection_changed = self.sig_attr_selection_changed self.gui.central_widget.attributes_tab.sig_cell_clicked = self.sig_attribute_cell_selected # Signals connection self.sig_select_tile.connect( self.attr_ctrl.on_attribute_selection_changed) self.sig_quit.connect(self.do_quit) self.sig_canvas_click.connect(self.course_ctrl.add_desk) self.sig_desk_selected.connect( self.course_ctrl.on_desk_selection_changed_on_app) self.sig_canvas_drag.connect(self.course_ctrl.move_desk) self.sig_canvas_right_click.connect( self.attr_ctrl.show_student_attributes) self.sig_shuffle.connect(self.course_ctrl.desk_shuffle) self.sig_course_changed.connect(self.course_ctrl.on_course_changed) self.sig_create_course.connect(self.course_ctrl.on_create_course) self.sig_topic_changed.connect(self.course_ctrl.on_topic_changed) self.sig_student_group_changed.connect( self.group_ctrl.on_student_group_changed) self.sig_action_triggered.connect(self.action_triggered) self.sig_TBbutton.connect(self.action_triggered) self.sig_create_grp_std.connect(self.group_ctrl.on_create_grp_std) self.sig_create_attribute.connect(self.attr_ctrl.on_create_attr) self.sig_delete_attributes.connect(self.attr_ctrl.on_delete_attributes) self.sig_delete_course.connect(self.course_ctrl.on_delete_course) self.sig_attr_selection_changed.connect( self.attr_ctrl.on_attribute_selection_changed) self.sig_attribute_cell_selected.connect( self.attr_ctrl.on_attribute_cell_selected) self.sig_flask_desk_selection_changed.connect( self.course_ctrl.on_desk_selection_changed_on_web) self.sig_close_qr.connect(self.close_qr) self.sig_config_mode_changed.connect(self.on_config_changed) self.sig_export_csv.connect(self.attr_ctrl.export_csv) self.actions_table = { # Action buttons "import_csv": self.group_ctrl.import_pronote, "auto_place": self.group_ctrl.auto_place, "sort_asc": lambda: self.group_ctrl.sort_alpha(False), "sort_desc": lambda: self.group_ctrl.sort_alpha(True), "sort_desks_Z": lambda: self.course_ctrl.sort_desks(sort_type="Z"), "sort_desks_2": lambda: self.course_ctrl.sort_desks(sort_type="2"), "sort_desks_U": lambda: self.course_ctrl.sort_desks(sort_type="U"), "killstudent": self.group_ctrl.killstudent, "delete_group": self.group_ctrl.on_delete_group, # Toolbar buttons "filter_select": self.attr_ctrl.change_filter_selection, "select": self.course_ctrl.auto_select_desks, "choice": self.course_ctrl.student_random_pick, "choice_attr": self.course_ctrl.student_attr_pick, "delete": self.course_ctrl.delete, "lot_change": self.attr_ctrl.lot_change, "print": self.course_ctrl.export_pdf, "show_qr": self.show_qr } # properties self.id_course = 0 self.id_group = 0 self.selection_mode = self.SEL_ALL self.filter_selection = False self.std_dialog_info: VStdAttributesDialog = None self.qr_dialog: VQRCode = None # initialize the views self.course_ctrl.show_all_courses() self.group_ctrl.show_all_groups() self.attr_ctrl.show_all_attributes() self.gui.on_config_mode(False) self.gui.update() # initialize connection to flask server self.flask_client = socketio.Client() self.flask_client.connect( 'http://localhost:' + AssetManager.getInstance().config('webapp', 'port')) self.flask_server = None # search for new version latest_version = AssetManager.getInstance().get_latest_version() if latest_version > AssetManager.getInstance().config( 'main', 'version'): self.gui.status_bar.showMessage(tr("new_version") + latest_version)
def _set_labels_and_layout(self): """ Creates this Widget's content and layout """ # --- Content --- # Lecluse DevCorp. Logo logo = QLabel() logo.setPixmap(QPixmap("assets/LDC-dark.png")) logo.setFixedSize(QSize( 512, 222)) # Original dimension is 2048x888, we divided by 4 logo.setScaledContents(True) # App credo lab_app = QLabel(f'<b>{tr("app_title")}</b> {tr("about_sdc")}') # Devs features_lab = QLabel(tr("about_features")) ihm_lab = QLabel(tr("about_ihm")) web_lab = QLabel(tr("about_web")) features_dev = QLabel( f'{self.links_style}<a href="https://www.lecluse.fr">Olivier Lécluse</a>' ) features_dev.setOpenExternalLinks(True) ihm_dev = QLabel( f'{self.links_style}<a href="https://www.linkedin.com/in/thomas-lécluse-62130395/">Thomas Lécluse</a>' ) ihm_dev.setOpenExternalLinks(True) web_dev = QLabel( f'{self.links_style}<a href="https://www.linkedin.com/in/nicolas-lecluse-a3488752/">Nicolas Lécluse</a>' ) web_dev.setOpenExternalLinks(True) # Documentation link lab_link_doc = QLabel( f'{tr("link_to")} {self.links_style}<a href="https://sdc.lecluse.fr">{tr("about_doc")}</a>' ) lab_link_doc.setOpenExternalLinks(True) # Github link lab_link_git = QLabel( f'{tr("link_to")} {self.links_style}<a href="https://github.com/wawachief/SalleDeClasse">{tr("about_github")}</a>' ) lab_link_git.setOpenExternalLinks(True) # Contact lab_contact = QLabel( f'{tr("about_contact")} {self.links_style}<a href="mailto:[email protected]">[email protected]</a>' ) lab_contact.setOpenExternalLinks(True) # License lab_license = QLabel( f'{self.links_style}<a href="https://www.gnu.org/licenses/gpl-3.0.fr.html">GPL-3.0 License</a>' ) lab_license.setOpenExternalLinks(True) # Version lab_app_version = QLabel(tr("app_version")) lab_bdd_version = QLabel(tr("bdd_version")) app_version = QLabel(AssetManager.getInstance().config( 'main', 'version')) bdd_version = QLabel(str(self.bdd_version)) # --- Layout --- box = QVBoxLayout() box.setMargin(0) box.setSpacing(0) # Logo box.addWidget(logo, alignment=Qt.AlignCenter) Separator(self.width(), box) # ---- # 'Salle de Classe' credo box.addWidget(lab_app, alignment=Qt.AlignCenter) box.addSpacing(SPACING_SIZE) # Devs roles dev_grid = QGridLayout() dev_grid.setContentsMargins(0, 0, 0, 0) dev_grid.addWidget(features_lab, 0, 0, alignment=Qt.AlignRight) dev_grid.addWidget(ihm_lab, 1, 0, alignment=Qt.AlignRight) dev_grid.addWidget(web_lab, 2, 0, alignment=Qt.AlignRight) dev_grid.addWidget(features_dev, 0, 1, alignment=Qt.AlignLeft) dev_grid.addWidget(ihm_dev, 1, 1, alignment=Qt.AlignLeft) dev_grid.addWidget(web_dev, 2, 1, alignment=Qt.AlignLeft) box.addLayout(dev_grid) # Contact box.addSpacing(SPACING_SIZE) box.addWidget(lab_contact, alignment=Qt.AlignCenter) Separator(self.width(), box) # ---- # Links of doc, git and license box.addWidget(lab_link_doc, alignment=Qt.AlignCenter) box.addWidget(lab_link_git, alignment=Qt.AlignCenter) box.addSpacing(SPACING_SIZE) box.addWidget(lab_license, alignment=Qt.AlignCenter) Separator(self.width(), box) # ---- # Version grid_version = QGridLayout() grid_version.addWidget(lab_app_version, 0, 0, alignment=Qt.AlignRight) grid_version.addWidget(lab_bdd_version, 1, 0, alignment=Qt.AlignRight) grid_version.addWidget(app_version, 0, 1, alignment=Qt.AlignLeft) grid_version.addWidget(bdd_version, 1, 1, alignment=Qt.AlignLeft) box.addLayout(grid_version) box.addSpacing(SPACING_SIZE) self.setLayout(box)