def init_box_in_cell(self, row): try: key = self.tab_action.item(row, 0).text() except AttributeError: return commands = self.keys_map.get(key, False) if not key or not commands: return self.command_box.clear() if isinstance(commands, list): if self.CUSTOM_COMMAND_ROW not in commands: commands.insert(0, self.CUSTOM_COMMAND_ROW) try: custom_command = self.tab_action.item(row, 1).text() if custom_command not in commands: commands.insert(1, custom_command) except AttributeError: pass finally: box = QComboBox() box.insertItems(0, commands) box.setCurrentIndex(1) self.tab_action.setCellWidget(row, 1, box) box.textActivated.connect( lambda command: self.close_box_in_cell(row, command)) else: self.tab_action.setItem(row, 1, QTableWidgetItem(commands))
def createEditor(self, parent: PySide2.QtWidgets.QWidget, option: PySide2.QtWidgets.QStyleOptionViewItem, index: QModelIndex) -> PySide2.QtWidgets.QWidget: item, column = index.model().item_and_column(index) vals, labels = item.values_choice(column) if vals: editor = QComboBox(parent) editor.insertItems(0, labels) else: editor = super().createEditor(parent, option, index) return editor
def update_combobox(box: QComboBox, labels: List[str]) -> None: """Update the combobox menu.""" box.blockSignals(True) box.clear() box.insertItems(0, labels) box.blockSignals(False)
class ExportFileDialog(QFileDialog): """ Create a custom Export-File Dialog with options like BOM etc. """ def __init__(self, *args, **kwargs): super(ExportFileDialog, self).__init__(*args, **kwargs) self.mainWindow = self.parent() self.setWindowTitle("Export nodes to CSV") self.setAcceptMode(QFileDialog.AcceptSave) self.setOption(QFileDialog.DontUseNativeDialog) #self.setFilter("CSV Files (*.csv)") self.setDefaultSuffix("csv") self.optionBOM = QCheckBox("Use a BOM", self) self.optionBOM.setCheckState(Qt.CheckState.Checked) self.optionLinebreaks = QCheckBox("Remove line breaks", self) self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionSeparator = QComboBox(self) self.optionSeparator.insertItems(0, [";", "\\t", ","]) self.optionSeparator.setEditable(True) #self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionWide = QCheckBox( "Convert to wide format (experimental feature)", self) self.optionWide.setCheckState(Qt.CheckState.Unchecked) # if none or all are selected, export all # if one or more are selected, export selective self.optionAll = QComboBox(self) self.optionAll.insertItems(0, [ 'All nodes (faster for large datasets, ordered by internal ID)', 'Selected nodes (ordered like shown in nodes view)' ]) if self.mainWindow.tree.noneOrAllSelected(): self.optionAll.setCurrentIndex(0) else: self.optionAll.setCurrentIndex(1) layout = self.layout() row = layout.rowCount() layout.addWidget(QLabel('Options'), row, 0) options = QHBoxLayout() options.addWidget(self.optionBOM) options.addWidget(self.optionLinebreaks) options.addWidget(QLabel('Separator')) options.addWidget(self.optionSeparator) options.addStretch(1) layout.addLayout(options, row, 1, 1, 2) layout.addWidget(QLabel('Post processing'), row + 1, 0) layout.addWidget(self.optionWide, row + 1, 1, 1, 2) layout.addWidget(QLabel('Export mode'), row + 2, 0) layout.addWidget(self.optionAll, row + 2, 1, 1, 2) self.setLayout(layout) if self.exec_(): if os.path.isfile(self.selectedFiles()[0]): os.remove(self.selectedFiles()[0]) output = open(self.selectedFiles()[0], 'w', newline='', encoding='utf8') if self.optionBOM.isChecked() and not self.optionWide.isChecked(): output.write('\ufeff') try: if self.optionAll.currentIndex() == 0: self.exportAllNodes(output) else: self.exportSelectedNodes(output) finally: output.close() if self.optionWide.isChecked(): self.convertToWideFormat(self.selectedFiles()[0]) def exportSelectedNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) #indexes = self.mainWindow.tree.selectionModel().selectedRows() #if child nodes should be exported as well, uncomment this line an comment the previous one indexes = self.mainWindow.tree.selectedIndexesAndChildren() indexes = list(indexes) progress.setMaximum(len(indexes)) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [ str(val) for val in self.mainWindow.tree.treemodel.getRowHeader() ] if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) #rows for no in range(len(indexes)): if progress.wasCanceled: break row = [ str(val) for val in self.mainWindow.tree.treemodel.getRowData(indexes[no]) ] if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) progress.step() finally: progress.close() def exportAllNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) progress.setMaximum(Node.query.count()) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [ "level", "id", "parent_id", "object_id", "object_type", "query_status", "query_time", "query_type" ] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(key) if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) #rows page = 0 while True: allnodes = Node.query.offset(page * 5000).limit(5000) if allnodes.count() == 0: break for node in allnodes: if progress.wasCanceled: break row = [ node.level, node.id, node.parent_id, node.objectid, node.objecttype, node.querystatus, node.querytime, node.querytype ] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(node.getResponseValue(key)) if self.optionLinebreaks.isChecked(): row = [ str(val).replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) # step the Bar progress.step() if progress.wasCanceled: break else: page += 1 finally: progress.close() def convertToWideFormat(self, filename): progress = ProgressBar("Converting data...", self.mainWindow) try: #Separate levels def flattenTable(fulltable, levelcol, idcol, parentidcol, countchildren, removeempty): fulltable[[levelcol]] = fulltable[[levelcol]].astype(int) levels = dict(list(fulltable.groupby(levelcol))) minlevel = fulltable.level.min() for level, data in sorted(levels.items()): #First level is the starting point for the following merges if level == minlevel: #data = data[[idcol,'object_id','object_type']] data = data.add_prefix('level_{}-'.format(level)) flattable = data else: #Aggregate object types and join them for col_countchildren in countchildren: children = data[parentidcol].groupby( [data[parentidcol], data[col_countchildren]]).count() children = children.unstack(col_countchildren) children['total'] = children.sum(axis=1) children = children.add_prefix( 'level_{}-children-{}-'.format( level - 1, col_countchildren)) leftkey = 'level_{}-id'.format(level - 1) flattable = merge(flattable, children, how='left', left_on=leftkey, right_index=True) flattable[children.columns.values.tolist( )] = flattable[children.columns.values.tolist( )].fillna(0).astype(int) #Join data data['childnumber'] = data.groupby( parentidcol).cumcount() leftkey = 'level_{}-{}'.format(level - 1, idcol) rightkey = 'level_{}-{}'.format(level, parentidcol) data = data.drop([levelcol], axis=1) data = data.add_prefix('level_{}-'.format(level)) flattable = merge(flattable, data, how="outer", left_on=leftkey, right_on=rightkey) if removeempty: flattable = flattable.dropna(axis=1, how='all') return flattable try: #delimiter delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') #open data = read_csv(filename, sep=delimiter, encoding='utf-8', dtype=str) #convert newdata = flattenTable( data, 'level', 'id', 'parent_id', ['object_type', 'query_status', 'query_type'], False) #save outfile = open(filename, 'w', newline='', encoding='utf8') try: if self.optionBOM.isChecked(): outfile.write('\ufeff') #UTF8 BOM newdata.to_csv(outfile, sep=delimiter, index=False, encoding="utf-8") finally: outfile.close() except Exception as e: self.mainWindow.logmessage(e) finally: progress.close()
class ExportFileDialog(QFileDialog): """ Create a custom Export-File Dialog with options like BOM etc. """ def __init__(self, *args, **kwargs): super(ExportFileDialog, self).__init__(*args, **kwargs) self.mainWindow = self.parent() self.setWindowTitle("Export nodes to CSV") self.setAcceptMode(QFileDialog.AcceptSave) self.setOption(QFileDialog.DontUseNativeDialog) #self.setFilter("CSV Files (*.csv)") self.setDefaultSuffix("csv") self.optionBOM = QCheckBox("Use a BOM", self) self.optionBOM.setCheckState(Qt.CheckState.Checked) self.optionLinebreaks = QCheckBox("Remove line breaks", self) self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionSeparator = QComboBox(self) self.optionSeparator.insertItems(0, [";", "\\t", ","]) self.optionSeparator.setEditable(True) # if none or all are selected, export all # if one or more are selected, export selective self.optionAll = QComboBox(self) self.optionAll.insertItems(0, [ 'All nodes (faster for large datasets, ordered by internal ID)', 'Selected nodes (ordered like shown in nodes view)' ]) if self.mainWindow.tree.noneOrAllSelected(): self.optionAll.setCurrentIndex(0) else: self.optionAll.setCurrentIndex(1) layout = self.layout() row = layout.rowCount() layout.addWidget(QLabel('Options'), row, 0) options = QHBoxLayout() options.addWidget(self.optionBOM) options.addWidget(self.optionLinebreaks) options.addWidget(QLabel('Separator')) options.addWidget(self.optionSeparator) options.addStretch(1) layout.addLayout(options, row, 1, 1, 2) layout.addWidget(QLabel('Export mode'), row + 2, 0) layout.addWidget(self.optionAll, row + 2, 1, 1, 2) self.setLayout(layout) if self.exec_(): if os.path.isfile(self.selectedFiles()[0]): os.remove(self.selectedFiles()[0]) output = open(self.selectedFiles()[0], 'w', newline='', encoding='utf8') if self.optionBOM.isChecked(): output.write('\ufeff') try: if self.optionAll.currentIndex() == 0: self.exportAllNodes(output) else: self.exportSelectedNodes(output) finally: output.close() def exportSelectedNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) #indexes = self.mainWindow.tree.selectionModel().selectedRows() #if child nodes should be exported as well, uncomment this line an comment the previous one indexes = self.mainWindow.tree.selectedIndexesAndChildren() indexes = list(indexes) progress.setMaximum(len(indexes)) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [ str(val) for val in self.mainWindow.tree.treemodel.getRowHeader() ] if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) #rows for no in range(len(indexes)): if progress.wasCanceled: break row = [ str(val) for val in self.mainWindow.tree.treemodel.getRowData(indexes[no]) ] if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) progress.step() finally: progress.close() def exportAllNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) progress.setMaximum(Node.query.count()) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [ "level", "id", "parent_id", "object_id", "object_type", "query_status", "query_time", "query_type" ] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(key) if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) #rows page = 0 while True: allnodes = Node.query.offset(page * 5000).limit(5000) if allnodes.count() == 0: break for node in allnodes: if progress.wasCanceled: break row = [ node.level, node.id, node.parent_id, node.objectid, node.objecttype, node.querystatus, node.querytime, node.querytype ] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(node.getResponseValue(key)) if self.optionLinebreaks.isChecked(): row = [ str(val).replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) # step the Bar progress.step() if progress.wasCanceled: break else: page += 1 finally: progress.close()
class MainWindow(QMainWindow): DIMENSION = 560 placement_aleatoire = True nb_robots = 0 def __init__(self, game): """ La fenêtre principale est initialisée avec l'organisation suivante : +--------------------------------------------------------------------------+ | self.menu = self.menuBar() | | | +--------------------------------------------------------------------------+ | toolbar = QToolBar() (déplacement/sélection des robots, | | boutons annuler, indice et solution) | +------------------------layout0 = QHBoxLayout()---------------------------+ | layout2 = QHBoxLayout() + moves_label | | l + | | | a grid_choice | nb_robots_choice | (affichage des | +-y--------------------+----------------------+ L | | o | a mouvements effectués) | | u | y | | t +--o-------------------------+ | = label = QLabel() | u | | Q | t tip_label | | V contient la grille de jeu | 3 | | B | (Affichage de l'indice| | o | = | | x | si demandé) | | L | Q | | a +--V+------------------------+ | y | B solution_label | | o | o | | u | x (Affichage de la | | t | | | | solution si demandée) | +---------------------------------------------+----------------------------+ """ super().__init__() self.game = game self.initial_game_state = self.game.get_state() self.number_moves = 0 self.setWindowTitle("Robot Ricochet") self.resize(self.DIMENSION + 150, self.DIMENSION + 100) # label contient la grille de jeu self.label = QLabel() canvas = QPixmap(self.DIMENSION, self.DIMENSION) canvas.fill(Qt.white) self.label.setPixmap(canvas) # layout0 contient, à gauche, les barres d'outils et la grille, # et à droite, la liste des états et l'indice affiché par le solveur layout0 = QHBoxLayout() layout0.setContentsMargins(0, 0, 0, 0) layout0.setSpacing(0) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout2 = QHBoxLayout() # Choix de la grille self.choice_of_grid_menu() # choix du nombre de robots self.nb_robots_choice_menu() # CheckBox placement aléatoire widget3 = QCheckBox("Placement aléatoire des robots et de l'objectif") widget3.setCheckState(Qt.Checked) widget3.stateChanged.connect(self.placer_aleatoirement) # layout2 contient les 3 widgets horizontaux de choix de grille, robots et aléa layout2.addWidget(self.grid_choice) layout2.addWidget(self.nb_robots_choice) #layout2.addWidget(widget3) layout2.setContentsMargins(0, 0, 0, 0) layout2.setSpacing(0) widget2 = QWidget() widget2.setLayout(layout2) layout.addWidget(widget2) layout.addWidget(self.label) # liste des mouvement effectués, indice et solution: layout3 = QVBoxLayout() layout3.setContentsMargins(0, 0, 0, 0) layout3.setSpacing(0) self.moves_label = QLabel() self.print_moves_list() self.tip_label = QLabel() self.solution_label = QLabel() layout3.addWidget(self.moves_label) layout3.addWidget(self.tip_label) layout3.addWidget(self.solution_label) layout0.addLayout(layout) layout0.addLayout(layout3) widget = QWidget() widget.setLayout(layout0) self.setCentralWidget(widget) # Menu self.menu = self.menuBar() self.file_menu = self.menu.addMenu("File") self.help_menu = self.menu.addMenu("Aide et instructions") # A faire self.size_fenetre = self.geometry() # Play QAction play_action = QAction("Réinitialiser !", self) play_action.triggered.connect(self.replay) self.file_menu.addAction(play_action) # Open_grid QAction open_grid_action = QAction("Ouvrir une grille", self) open_grid_action.setShortcut('Ctrl+O') open_grid_action.triggered.connect(self.open_grid) self.file_menu.addAction(open_grid_action) # Open_game QAction open_game_action = QAction("Ouvrir un jeu", self) open_game_action.triggered.connect(self.open_game) self.file_menu.addAction(open_game_action) # Save_grid QAction save_grid_action = QAction("Enregistrer cette grille", self) save_grid_action.triggered.connect(self.save_grid) self.file_menu.addAction(save_grid_action) # Save_game QAction save_game_action = QAction("Enregistrer ce jeu", self) save_game_action.triggered.connect(self.save_game) self.file_menu.addAction(save_game_action) # Exit QAction exit_action = QAction("Quitter", self) exit_action.setShortcut(QKeySequence.Quit) exit_action.triggered.connect(self.close) self.file_menu.addAction(exit_action) # Help QAction help_action = QAction("Aide", self) help_action.triggered.connect(self.help) self.help_menu.addAction(help_action) self.toolbar_menus() #Le robot rouge est sélectionné par défaut self.selected_robot = 'R' self.draw_robots_and_goal() def replay(self): """ on remet l'état initial du jeu """ self.game.set_state(self.initial_game_state) self.game.moves_list = [] self.game.states_list = deque([self.game.state_start], maxlen=100) self.unprint_moves_list() self.number_moves = 0 self.draw_robots_and_goal() def help(self): """ Ouvre une fenêtre d'aide""" self.help_windows = Help_window() self.help_windows.exec_() def toolbar_menus(self): """ Affiche la barre d'icônes permettant de diriger les robots et de les sélectionner""" # Toolbar toolbar = QToolBar("Game toolbar") self.addToolBar(toolbar) # Flèche gauche button_West = QAction(QIcon(ICON_PATH + "arrow-180.png"), "West", self) button_West.setStatusTip("Aller à gauche") button_West.triggered.connect(self.onButtonWestClick) button_West.setCheckable(False) button_West.setShortcut(QKeySequence("Left")) toolbar.addAction(button_West) # Flèche droite button_East = QAction(QIcon(ICON_PATH + "arrow.png"), "Est", self) button_East.setStatusTip("Aller à droite") button_East.triggered.connect(self.onButtonEastClick) button_East.setCheckable(False) button_East.setShortcut(QKeySequence("Right")) toolbar.addAction(button_East) # Flèche Haut button_North = QAction(QIcon(ICON_PATH + "arrow-090.png"), "North", self) button_North.setStatusTip("Aller vers le haut") button_North.triggered.connect(self.onButtonNorthClick) button_North.setCheckable(False) button_North.setShortcut(QKeySequence("Up")) toolbar.addAction(button_North) # Flèche Bas button_South = QAction(QIcon(ICON_PATH + "arrow-270.png"), "South", self) button_South.setStatusTip("Aller vers le Bas") button_South.triggered.connect(self.onButtonSouthClick) button_South.setCheckable(False) button_South.setShortcut(QKeySequence("Down")) toolbar.addAction(button_South) # Selection robot actif button_Red = QPushButton("&Red") button_Red.setIcon(QIcon(ICON_PATH + "icon_R.png")) button_Red.setAutoExclusive(True) button_Red.setCheckable(True) button_Red.setShortcut(QKeySequence("R")) button_Red.toggled.connect(self.onButtonRedClick) button_Green = QPushButton("&Green") button_Green.setIcon(QIcon(ICON_PATH + "icon_G.png")) button_Green.setAutoExclusive(True) button_Green.setCheckable(True) button_Green.setShortcut(QKeySequence("G")) button_Green.toggled.connect(self.onButtonGreenClick) button_Blue = QPushButton("&Blue") button_Blue.setIcon(QIcon(ICON_PATH + "icon_B.png")) button_Blue.setAutoExclusive(True) button_Blue.setCheckable(True) button_Blue.setShortcut(QKeySequence("B")) button_Blue.toggled.connect(self.onButtonBlueClick) button_Yellow = QPushButton("&Yellow") button_Yellow.setIcon(QIcon(ICON_PATH + "icon_Y.png")) button_Yellow.setAutoExclusive(True) button_Yellow.setCheckable(True) button_Yellow.setShortcut(QKeySequence("Y")) button_Yellow.toggled.connect(self.onButtonYellowClick) # Boutton d'annulation (revient en arrière d'un coup) button_undo = QPushButton("&Undo") button_undo.setIcon(QIcon(ICON_PATH + "undo.jpg")) button_undo.setAutoExclusive(False) button_undo.setCheckable(False) button_undo.setShortcut(QKeySequence("U")) button_undo.clicked.connect(self.onButtonUndoClick) # Boutton d'indice : lance le solveur pour donner l'indice du prochain coup à effectuer button_tip = QPushButton("&Tip") button_tip.setIcon(QIcon(ICON_PATH + "icon_tip.png")) button_tip.setAutoExclusive(False) button_tip.setCheckable(False) button_tip.setShortcut(QKeySequence("T")) button_tip.clicked.connect(self.onButtonTipClick) # Boutton Solution : lance le solveur pour afficher une liste d'actions à effectuer pour résoudre le jeu button_solution = QPushButton("&Solution") button_solution.setIcon(QIcon(ICON_PATH + "icon_solution.png")) button_solution.setAutoExclusive(False) button_solution.setCheckable(False) button_solution.setShortcut(QKeySequence("S")) button_solution.clicked.connect(self.onButtonSolutionClick) toolbar.addWidget(button_Red) toolbar.addWidget(button_Green) toolbar.addWidget(button_Blue) toolbar.addWidget(button_Yellow) toolbar.addWidget(button_undo) toolbar.addWidget(button_tip) toolbar.addWidget(button_solution) def open_grid(self): """ Ouvre une boîte de dialogue permettant de charger une grille existante sur le disque dur""" filename, filter = QFileDialog.getOpenFileName( self, 'selectionner un fichier contenant une grille', './grids', '*.json') board, = Board.load_from_json(filename) self.game.add_board(board) self.number_moves = 0 self.group = Robot_group() self.game = Game(self.game.board, self.group, self.game.goal) self.unprint_moves_list() self.draw_grid() def open_game(self): """ Ouvre une boîte de dialogue permettant de charger un jeu existant sur le disque dur""" filename, filter = QFileDialog.getOpenFileName( self, 'selectionner un fichier contenant un jeu', './games', '*.json') self.game = Game.load_from_json(filename) self.initial_game_state = self.game.get_state() self.number_moves = 0 self.unprint_moves_list() self.draw_robots_and_goal() def save_grid(self): """ Ouvre une boîte de dialogue permettant d'enregistrer la grille affichée sur le disque dur""" filename, _ = QFileDialog.getSaveFileName( self, "Save Grid As", "", "JSON (*.JSON *.json);;" "All files(*.*)", ) if filename: self.game.board.save_as_json(filename) def save_game(self): """ Ouvre une boîte de dialogue permettant d'enregistrer le jeu actuel sur le disque dur Si le joueur entre ou sélectionne un nom de fichier, le eju est sauvegardé dans ce fichier """ filename, _ = QFileDialog.getSaveFileName( self, "Save game As", "", "JSON (*.JSON *.json);;" "All files(*.*)", ) if filename: self.game.save_to_json(filename) def print_moves_list(self): """ Affichage de la liste des mouvements effectués dans le label "moves_label" """ self.moves_label.setText("Mouvements effectués : \n" + str(self.game.moves_list).replace(', ', '\n')) self.moves_label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) def unprint_moves_list(self): """ réinitialisation du label "moves_label" pour cacher la liste des mouvements effectués. """ self.moves_label.setText(" ") self.moves_label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) def print_tip(self): """ Affichage du conseil généré par le solveur dans le label "tip_label" """ self.tip_label.setText("Et si vous essayiez ce mouvement : \n" + str(self.tip) + " ?") self.tip_label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) def unprint_tip(self): """ réinitialisation du label "tip_label" pour cacher le conseil généré par le solveur. """ self.tip_label.setText(" ") self.tip_label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) self.solution_label.setText(" ") def choice_of_grid_menu(self): self.grid_choice = QComboBox() self.grid_choice.insertItems( 0, ("Grille 6x6", "Grille 8x8", "Grille 10x10", "Grille 12x12", "Grille 14x14", "Grille 16x16", "Grille aléatoire 16x16")) self.grid_choice.setGeometry(0, 0, 180, 40) self.grid_choice.activated.connect(self.choix_grille) def choix_grille(self, i): """ Lors du choix d'une nouvelle grille, le jeu est réinitialisé et redessiné, les robots et l'objectif sont masqués. """ # pour ouvrir les vieux .txt # name_grid = './test' + str(i + 1) + '.txt' # fd = open(name_grid,'r') # A = Board.load_from_json(fd) if i == 0: A, = Board.load_from_json(GRIDS_PATH + 'grid 6x6.json') elif i == 1: A, = Board.load_from_json(GRIDS_PATH + 'grid 8x8.json') elif i == 2: A, = Board.load_from_json(GRIDS_PATH + 'grid 10x10.json') elif i == 3: A, = Board.load_from_json(GRIDS_PATH + 'grid 12x12.json') elif i == 4: A, = Board.load_from_json(GRIDS_PATH + 'grid 14x14.json') elif i == 5: A, = Board.load_from_json(GRIDS_PATH + 'grid 16x16.json') else: # Pour ouvrir une grille aléatoire classique A = Board.new_classic() self.game.add_board(A) self.number_moves = 0 self.group = Robot_group() self.game = Game(self.game.board, self.group, self.game.goal) self.draw_grid() # choix du nombre de robots def nb_robots_choice_menu(self): self.nb_robots_choice = QComboBox() self.nb_robots_choice.insertItems( 0, ("1 robot", "2 robots", "3 robots", "4 robots")) self.nb_robots_choice.setGeometry(0, 0, 40, 40) self.nb_robots_choice.activated.connect(self.choix_nb_robots) def choix_nb_robots(self, i): """ Les robots et l'objectif sont placés aléatoirement. L'extension """ self.group = Robot_group() self.game = Game(self.game.board, self.group, self.game.goal) self.nb_robots = i + 1 robots_pos = [0] * self.nb_robots robots_list = [0] * self.nb_robots robots_colors = [i for i in RColors] if self.placement_aleatoire: for i in range(self.nb_robots): x = randint(0, self.game.board.width - 1) y = randint(0, self.game.board.height - 1) while ((x, y) in robots_pos): x = randint(0, self.game.board.width - 1) y = randint(0, self.game.board.height - 1) robots_pos[i] = (x, y) robots_list[i] = Robot(self.game.robots, robots_colors[i], (x, y)) x = randint(0, self.game.board.width - 1) y = randint(0, self.game.board.height - 1) goal = Goal(RColors(randint(1, self.nb_robots)), (x, y)) self.game = Game(self.game.board, self.group, self.game.goal) self.game.add_goal(goal) self.initial_game_state = self.game.get_state() self.draw_robots_and_goal() else: fp = open(GAMES_PATH + DEFAULT_GAME, 'r') self.game = Game.load_from_json(fp) fp.close() def draw_grid(self): """ Dessine la grille de jeu en juxtaposant les images contenant chaque case. Chaque image est redimensionnée et ajustée à la taille de la grille. """ painter = QPainter(self.label.pixmap()) names = [ "Empty", "N", "E", "EN", "S", "NS", "ES", "ENS", "W", "NW", "EW", "ENW", "SW", "NSW", "ESW", "ENSW" ] images = [ QPixmap(IMAGES_PATH + name + ".bmp", format="bmp") for name in names ] for x in range(0, self.game.board.width): for y in range(0, self.game.board.height): painter.drawPixmap( QPoint(self.DIMENSION / self.game.board.height * y, self.DIMENSION / self.game.board.width * x), images[int(str(self.game.board.grid[x][y]))].scaled( self.DIMENSION / self.game.board.width, self.DIMENSION / self.game.board.height)) self.update() painter.end() def draw_robots_and_goal(self): self.draw_grid() painter = QPainter(self.label.pixmap()) goal_img_name = ICON_PATH + "/goal_" + str( self.game.goal.color) + ".png" painter.drawPixmap( QPoint( self.DIMENSION / self.game.board.height * self.game.goal.position[1], self.DIMENSION / self.game.board.width * self.game.goal.position[0]), QPixmap(goal_img_name, format="png").scaled( self.DIMENSION / self.game.board.width * 0.9, self.DIMENSION / self.game.board.height * 0.9)) images = [ QPixmap(ICON_PATH + "robot_" + str(color) + ".png", format="png") for color in self.game.color_keys ] for i, robot in enumerate(self.game.robots): painter.drawPixmap( QPoint( self.DIMENSION / self.game.board.height * self.game.robots[robot].position[1], self.DIMENSION / self.game.board.width * self.game.robots[robot].position[0]), images[i].scaled(self.DIMENSION / self.game.board.width * 0.8, self.DIMENSION / self.game.board.height)) self.update() painter.end() def onButtonEastClick(self, s): self.game.do_action(self.selected_robot + 'E') self.draw_robots_and_goal() self.number_moves += 1 self.print_moves_list() self.unprint_tip() if self.game.is_won(): self.game_is_won() def onButtonWestClick(self, s): self.game.do_action(self.selected_robot + 'W') self.draw_robots_and_goal() self.number_moves += 1 self.print_moves_list() self.unprint_tip() if self.game.is_won(): self.game_is_won() def onButtonNorthClick(self, s): self.game.do_action(self.selected_robot + 'N') self.draw_robots_and_goal() self.number_moves += 1 self.print_moves_list() self.unprint_tip() if self.game.is_won(): self.game_is_won() def onButtonSouthClick(self, s): self.game.do_action(self.selected_robot + 'S') self.draw_robots_and_goal() self.number_moves += 1 self.print_moves_list() self.unprint_tip() if self.game.is_won(): self.game_is_won() def placer_aleatoirement(self): """ inverse la sélection de la checkBox """ self.placement_aleatoire = not (self.placement_aleatoire) def onButtonRedClick(self, s): if s: self.selected_robot = 'R' def onButtonGreenClick(self, s): if s: self.selected_robot = 'G' def onButtonBlueClick(self, s): if s: self.selected_robot = 'B' def onButtonYellowClick(self, s): if s: self.selected_robot = 'Y' def onButtonUndoClick(self, s): """ Annule le dernier coup effectué """ if self.number_moves != 0: self.game.undo() self.number_moves -= 1 self.print_moves_list() self.unprint_tip() self.draw_robots_and_goal() def solve(self): """tip_game contient une copie du jeu courant, pour l'utiliser par le solveur""" self.game.save_to_json('tip_game.json') self.tip_game = Game.load_from_json('tip_game.json') return (solveur(self.tip_game).find_solution()) def onButtonTipClick(self, s): """ La solution renvoyée par le solveur est de la forme (True/False, liste d'actions à effectuer). On récupère ici la première action.""" self.tip = self.solve()[1][0] self.print_tip() def onButtonSolutionClick(self, s): self.solution = self.solve()[1] self.solution_label.setText( "Pour gagner, vous auriez pu effectuer cette suite de mouvements : \n" + str(self.solution) + ".") self.solution_label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter) def game_is_won(self): """ Fonction lancée quand l'objectif est atteint. On génère une fonction exit_windows dont le code de retour 1, 2 ou 3 valide le choix : 1 : replay : on remet l'état initial du jeu 2 : new game : on choisit une grille aleatoire 3 : exit : on quitte le jeu. """ self.exit_windows = Exit_window(self.number_moves) self.exit_windows.exec_() if self.exit_windows.retStatus == 1: #replay : on remet l'état initial du jeu self.replay() elif self.exit_windows.retStatus == 2: #new game : on choisit une grille aleatoire self.choix_grille(1) self.choix_nb_robots(3) self.unprint_moves_list() elif self.exit_windows.retStatus == 3: #exit : on quitte le jeu exit()
class ExportFileDialog(QFileDialog): """ Create a custom Export-File Dialog with options like BOM etc. """ def __init__(self, *args, **kwargs): super(ExportFileDialog, self).__init__(*args, **kwargs) self.mainWindow = self.parent() self.setWindowTitle("Export nodes to CSV") self.setAcceptMode(QFileDialog.AcceptSave) self.setOption(QFileDialog.DontUseNativeDialog) #self.setFilter("CSV Files (*.csv)") self.setDefaultSuffix("csv") self.optionBOM = QCheckBox("Use a BOM", self) self.optionBOM.setCheckState(Qt.CheckState.Checked) self.optionLinebreaks = QCheckBox("Remove line breaks", self) self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionSeparator = QComboBox(self) self.optionSeparator.insertItems(0, [";", "\\t", ","]) self.optionSeparator.setEditable(True) # if none or all are selected, export all # if one or more are selected, export selective self.optionAll = QComboBox(self) self.optionAll.insertItems(0, [ 'All nodes (faster for large datasets, ordered by internal ID)', 'Selected nodes (ordered like shown in nodes view)' ]) if self.mainWindow.tree.noneOrAllSelected(): self.optionAll.setCurrentIndex(0) else: self.optionAll.setCurrentIndex(1) layout = self.layout() row = layout.rowCount() layout.addWidget(QLabel('Options'), row, 0) options = QHBoxLayout() options.addWidget(self.optionBOM) options.addWidget(self.optionLinebreaks) options.addWidget(QLabel('Separator')) options.addWidget(self.optionSeparator) options.addStretch(1) layout.addLayout(options, row, 1, 1, 2) layout.addWidget(QLabel('Export mode'), row + 2, 0) layout.addWidget(self.optionAll, row + 2, 1, 1, 2) self.setLayout(layout) dbfilename = self.mainWindow.database.filename #self.setDirectory(os.path.dirname(dbfilename)) filename, ext = os.path.splitext(dbfilename) self.selectFile(filename + '.csv') # if not os.path.exists(dbfilename): # dbfilename = self.mainWindow.settings.value("lastpath", os.path.expanduser("~")) # if not os.path.exists(dbfilename): # dbfilename = os.path.expanduser("~") if self.exec_(): try: if os.path.isfile(self.selectedFiles()[0]): os.remove(self.selectedFiles()[0]) except Exception as e: QMessageBox.information(self, "Facepager", "Could not overwrite file:" + str(e)) return False output = open(self.selectedFiles()[0], 'w', newline='', encoding='utf8') try: if self.optionBOM.isChecked(): output.write('\ufeff') if self.optionAll.currentIndex() == 0: self.exportAllNodes(output) else: self.exportSelectedNodes(output) finally: output.close() def exportSelectedNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) #indexes = self.mainWindow.tree.selectionModel().selectedRows() #if child nodes should be exported as well, uncomment this line an comment the previous one indexes = self.mainWindow.tree.selectedIndexesAndChildren() indexes = list(indexes) progress.setMaximum(len(indexes)) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [ str(val) for val in self.mainWindow.tree.treemodel.getRowHeader() ] if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] row = ['path'] + row writer.writerow(row) #rows path = [] for index in indexes: if progress.wasCanceled: break # data rowdata = self.mainWindow.tree.treemodel.getRowData(index) # path of parents (#2=level;#3=object ID) while rowdata[2] < len(path): path.pop() path.append(rowdata[3]) # values row = [str(val) for val in rowdata] row = ["/".join(path)] + row if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) progress.step() finally: progress.close() def exportAllNodes(self, output): progress = ProgressBar("Exporting data...", self.mainWindow) progress.setMaximum(Node.query.count()) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') # Headers row = [ "level", "id", "parent_id", "object_id", "object_type", "object_key", "query_status", "query_time", "query_type" ] for key in extractNames( self.mainWindow.tree.treemodel.customcolumns): row.append(key) if self.optionLinebreaks.isChecked(): row = [ val.replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) # Rows page = 0 while not progress.wasCanceled: allnodes = Node.query.offset(page * 5000).limit(5000) if allnodes.count() == 0: break for node in allnodes: if progress.wasCanceled: break row = [ node.level, node.id, node.parent_id, node.objectid, node.objecttype, getDictValue(node.queryparams, 'nodedata'), node.querystatus, node.querytime, node.querytype ] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(node.getResponseValue(key)[1]) if self.optionLinebreaks.isChecked(): row = [ str(val).replace('\n', ' ').replace('\r', ' ') for val in row ] writer.writerow(row) # Step the bar progress.step() page += 1 finally: progress.close()
class ExportFileDialog(QFileDialog): """ Create a custom Export-File Dialog with options like BOM etc. """ def __init__(self,*args,**kwargs): super(ExportFileDialog,self).__init__(*args,**kwargs) self.mainWindow = self.parent() self.setWindowTitle("Export nodes to CSV") self.setAcceptMode(QFileDialog.AcceptSave) self.setOption(QFileDialog.DontUseNativeDialog) #self.setFilter("CSV Files (*.csv)") self.setDefaultSuffix("csv") self.optionBOM = QCheckBox("Use a BOM",self) self.optionBOM.setCheckState(Qt.CheckState.Checked) self.optionLinebreaks = QCheckBox("Remove line breaks",self) self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionSeparator = QComboBox(self) self.optionSeparator.insertItems(0, [";","\\t",","]) self.optionSeparator.setEditable(True) #self.optionLinebreaks.setCheckState(Qt.CheckState.Checked) self.optionWide = QCheckBox("Convert to wide format (experimental feature)",self) self.optionWide.setCheckState(Qt.CheckState.Unchecked) # if none or all are selected, export all # if one or more are selected, export selective self.optionAll = QComboBox(self) self.optionAll.insertItems(0, ['All nodes (faster for large datasets, ordered by internal ID)','Selected nodes (ordered like shown in nodes view)']) if self.mainWindow.tree.noneOrAllSelected(): self.optionAll.setCurrentIndex(0) else: self.optionAll.setCurrentIndex(1) layout = self.layout() row = layout.rowCount() layout.addWidget(QLabel('Options'),row,0) options = QHBoxLayout() options.addWidget(self.optionBOM) options.addWidget(self.optionLinebreaks) options.addWidget(QLabel('Separator')) options.addWidget(self.optionSeparator) options.addStretch(1) layout.addLayout(options,row,1,1,2) layout.addWidget(QLabel('Post processing'),row+1,0) layout.addWidget(self.optionWide,row+1,1,1,2) layout.addWidget(QLabel('Export mode'),row+2,0) layout.addWidget(self.optionAll,row+2,1,1,2) self.setLayout(layout) if self.exec_(): if os.path.isfile(self.selectedFiles()[0]): os.remove(self.selectedFiles()[0]) output = open(self.selectedFiles()[0], 'w', newline='', encoding='utf8') if self.optionBOM.isChecked() and not self.optionWide.isChecked(): output.write('\ufeff') try: if self.optionAll.currentIndex() == 0: self.exportAllNodes(output) else: self.exportSelectedNodes(output) finally: output.close() if self.optionWide.isChecked(): self.convertToWideFormat(self.selectedFiles()[0]) def exportSelectedNodes(self,output): progress = ProgressBar("Exporting data...", self.mainWindow) #indexes = self.mainWindow.tree.selectionModel().selectedRows() #if child nodes should be exported as well, uncomment this line an comment the previous one indexes = self.mainWindow.tree.selectedIndexesAndChildren() progress.setMaximum(len(indexes)) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = [str(val) for val in self.mainWindow.tree.treemodel.getRowHeader()] if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) #rows for no in range(len(indexes)): if progress.wasCanceled: break row = [str(val) for val in self.mainWindow.tree.treemodel.getRowData(indexes[no])] if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) progress.step() finally: progress.close() def exportAllNodes(self,output): progress = ProgressBar("Exporting data...", self.mainWindow) progress.setMaximum(Node.query.count()) try: delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') writer = csv.writer(output, delimiter=delimiter, quotechar='"', quoting=csv.QUOTE_ALL, doublequote=True, lineterminator='\r\n') #headers row = ["level", "id", "parent_id", "object_id", "object_type", "query_status", "query_time", "query_type"] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(key) if self.optionLinebreaks.isChecked(): row = [val.replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) #rows page = 0 while True: allnodes = Node.query.offset(page * 5000).limit(5000) if allnodes.count() == 0: break for node in allnodes: if progress.wasCanceled: break row = [node.level, node.id, node.parent_id, node.objectid, node.objecttype, node.querystatus, node.querytime, node.querytype] for key in self.mainWindow.tree.treemodel.customcolumns: row.append(node.getResponseValue(key)) if self.optionLinebreaks.isChecked(): row = [str(val).replace('\n', ' ').replace('\r',' ') for val in row] writer.writerow(row) # step the Bar progress.step() if progress.wasCanceled: break else: page += 1 finally: progress.close() def convertToWideFormat(self,filename): progress = ProgressBar("Converting data...", self.mainWindow) try: #Separate levels def flattenTable(fulltable,levelcol,idcol,parentidcol,countchildren,removeempty): fulltable[[levelcol]] = fulltable[[levelcol]].astype(int) levels = dict(list(fulltable.groupby(levelcol))) minlevel = fulltable.level.min() for level, data in sorted(levels.items()): #First level is the starting point for the following merges if level == minlevel: #data = data[[idcol,'object_id','object_type']] data = data.add_prefix('level_{}-'.format(level)) flattable = data else: #Aggregate object types and join them for col_countchildren in countchildren: children = data[parentidcol].groupby([data[parentidcol],data[col_countchildren]]).count() children = children.unstack(col_countchildren) children['total'] = children.sum(axis=1) children = children.add_prefix('level_{}-children-{}-'.format(level-1,col_countchildren)) leftkey = 'level_{}-id'.format(level-1) flattable = merge(flattable,children,how='left',left_on=leftkey,right_index=True) flattable[children.columns.values.tolist()] = flattable[children.columns.values.tolist()].fillna(0).astype(int) #Join data data['childnumber'] = data.groupby(parentidcol).cumcount() leftkey = 'level_{}-{}'.format(level-1,idcol) rightkey = 'level_{}-{}'.format(level,parentidcol) data = data.drop([levelcol],axis=1) data = data.add_prefix('level_{}-'.format(level)) flattable = merge(flattable,data,how="outer",left_on=leftkey,right_on=rightkey) if removeempty: flattable = flattable.dropna(axis=1,how='all') return flattable try: #delimiter delimiter = self.optionSeparator.currentText() delimiter = delimiter.encode('utf-8').decode('unicode_escape') #open data = read_csv(filename, sep=delimiter,encoding='utf-8',dtype=str) #convert newdata = flattenTable(data,'level','id','parent_id',['object_type','query_status','query_type'],False) #save outfile = open(filename, 'w',newline='',encoding='utf8') try: if self.optionBOM.isChecked(): outfile.write('\ufeff') #UTF8 BOM newdata.to_csv(outfile,sep=delimiter,index=False,encoding="utf-8") finally: outfile.close() except Exception as e: self.mainWindow.logmessage(e) finally: progress.close()
class EditCommentBankWindow(QMainWindow): def __init__(self, subject_name): QMainWindow.__init__(self) self.setWindowTitle("Edit Comment Bank: {} - {} {}".format( subject_name, config.APP_NAME, config.APP_VERSION)) self.setMinimumWidth(1200) self.setStyleSheet(config.STYLESHEET) self.subject = could_try_harder.load(subject_name) self.saved_list = could_try_harder.get_saved_list() # Widgets self.intro_comment_label = QLabel("Introductory Comment:") self.intro_comment_label.setProperty("styleClass", "heading") self.intro_comment_textedit = QTextEdit() self.comment_bank_label = QLabel("Comment Bank") self.comment_bank_label.setProperty("styleClass", "heading") self.comment_bank_listwidget = QListWidget() self.placeholder_instructions_label = QLabel( config.PLACEHOLDER_INSTRUCTIONS) self.add_comment_label = QLabel("Add Comment:") self.add_comment_entry = QLineEdit() self.add_comment_button = QPushButton("Add") self.update_comment_label = QLabel("Update Comment:") self.update_comment_entry = QLineEdit() self.update_comment_button = QPushButton("Update") self.delete_comment_button = QPushButton("Delete Comment") self.import_comments_combo = QComboBox() self.import_comments_button = QPushButton("Import...") self.cancel_button = QPushButton("Cancel") self.save_button = QPushButton("Save") # Layout self.layout = QVBoxLayout() self.top_layout = QHBoxLayout() self.intro_comment_layout = QVBoxLayout() self.intro_comment_layout.addWidget(self.intro_comment_label) self.intro_comment_layout.addWidget(self.intro_comment_textedit) self.top_layout.addLayout(self.intro_comment_layout) self.top_layout.addWidget(self.placeholder_instructions_label) self.layout.addLayout(self.top_layout) self.middle_layout = QVBoxLayout() self.middle_layout.addWidget(self.comment_bank_label) self.middle_layout.addWidget(self.comment_bank_listwidget) self.comment_actions_layout = QHBoxLayout() self.comment_actions_layout.addWidget(self.delete_comment_button, 0, Qt.AlignLeft) self.comment_actions_layout.addWidget(self.import_comments_combo, 1, Qt.AlignRight) self.comment_actions_layout.addWidget(self.import_comments_button, 0, Qt.AlignRight) self.middle_layout.addLayout(self.comment_actions_layout) self.update_comment_layout = QGridLayout() self.update_comment_layout.addWidget(self.update_comment_label, 0, 0) self.update_comment_layout.addWidget(self.update_comment_entry, 0, 1) self.update_comment_layout.addWidget(self.update_comment_button, 0, 2) self.update_comment_layout.addWidget(self.add_comment_label, 1, 0) self.update_comment_layout.addWidget(self.add_comment_entry, 1, 1) self.update_comment_layout.addWidget(self.add_comment_button, 1, 2) self.middle_layout.addLayout(self.update_comment_layout) self.layout.addLayout(self.middle_layout) self.bottom_layout = QHBoxLayout() self.bottom_layout.addWidget(self.cancel_button, 0, Qt.AlignLeft) self.bottom_layout.addWidget(self.save_button, 0, Qt.AlignRight) self.layout.addLayout(self.bottom_layout) # Slot connections self.comment_bank_listwidget.itemSelectionChanged.connect( self.do_update_comment_bank_selection) self.import_comments_button.clicked.connect(self.do_import_comments) self.update_comment_button.clicked.connect(self.do_update_comment) self.update_comment_entry.returnPressed.connect(self.do_update_comment) self.add_comment_button.clicked.connect(self.do_add_comment) self.add_comment_entry.returnPressed.connect(self.do_add_comment) self.delete_comment_button.clicked.connect(self.do_delete_comment) self.cancel_button.clicked.connect(self.do_cancel) self.save_button.clicked.connect(self.do_save) # Initial UI update self.update_ui() self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) def update_ui(self): self.update_import_comments_list() self.update_intro_comment() self.update_comment_bank() def update_import_comments_list(self): self.import_comments_combo.clear() self.import_comments_combo.insertItems(0, self.saved_list) def update_intro_comment(self): self.intro_comment_textedit.clear() self.intro_comment_textedit.insertPlainText( self.subject['intro_comment']) def update_comment_bank(self): self.comment_bank_listwidget.clear() self.comment_bank_listwidget.addItems(self.subject['comment_bank']) self.do_update_comment_bank_selection() @Slot() def do_import_comments(self): # TODO confirm dialog first confirm_msg = QMessageBox(self) confirm_msg.setWindowTitle("Confirm") confirm_msg.setText("This will override current comments.") confirm_msg.setInformativeText("Do you want to continue?") confirm_msg.setStandardButtons(QMessageBox.No | QMessageBox.Yes) confirm_msg.setDefaultButton(QMessageBox.Yes) confirm = confirm_msg.exec() if confirm == QMessageBox.Yes: if self.import_comments_combo.count() > 0: new_subject = could_try_harder.load( self.import_comments_combo.currentText()) if new_subject: self.subject['intro_comment'] = new_subject[ 'intro_comment'] self.subject['comment_bank'] = new_subject['comment_bank'] self.update_ui() else: # TODO better error handling here print('Tried to import empty subject.') return return @Slot() def do_update_comment_bank_selection(self): if self.comment_bank_listwidget.selectedItems(): state = True else: state = False self.delete_comment_button.setEnabled(state) self.update_comment_button.setEnabled(state) # Update the text in the update comment line edit self.update_comment_entry.clear() if self.comment_bank_listwidget.currentItem(): self.update_comment_entry.insert( self.comment_bank_listwidget.currentItem().text()) @Slot() def do_update_comment(self): if self.update_comment_entry.text(): self.comment_bank_listwidget.currentItem().setText( could_try_harder.do_style( self.update_comment_entry.text().strip())) self.do_update_comment_bank_selection() @Slot() def do_add_comment(self): if self.add_comment_entry.text(): self.comment_bank_listwidget.addItem( could_try_harder.do_style( self.add_comment_entry.text().strip())) self.add_comment_entry.clear() self.do_update_comment_bank_selection() @Slot() def do_delete_comment(self): self.comment_bank_listwidget.takeItem( self.comment_bank_listwidget.currentRow()) self.do_update_comment_bank_selection() @Slot() def do_cancel(self): self.close() @Slot() def do_save(self): self.subject['intro_comment'] = could_try_harder.do_style( self.intro_comment_textedit.toPlainText().strip()) self.subject['comment_bank'] = [] for i in range(self.comment_bank_listwidget.count()): self.subject['comment_bank'].append( self.comment_bank_listwidget.item(i).text()) if could_try_harder.save(self.subject): self.close() else: # TODO better error handling here print("Save failed.")