class MainWindow(QWidget): def __init__(self): super().__init__() self.new_graph_dialog_text = NewGraphWindowText() self.new_graph_dialog_graphic = NewGraphWindowGraphic() self.mv, self.xdif, self.ydif, self.start_vertice = None, None, None, None self.from_file, self.graph_present = False, False self.used, self.con_points, self.tin, self.tout, self.lines = [], [], [], [], [] self.algo_starts, self.timer = 0, 0 self.paths, self.coords = [], [] self.setGeometry(300, 300, 1000, 500) self.move(QApplication.desktop().screen().rect().center() - self.rect().center()) self.setWindowTitle("Articulation Points") self.setWindowIcon(QIcon("icon.jpeg")) self.menubar = QMenuBar(self) self.filemenu = self.menubar.addMenu("File") self.newfile = self.filemenu.addMenu("New") self.newtext = self.newfile.addAction("New Graph(Text)") self.newgraphic = self.newfile.addAction("New Graph(Graphical)") self.openfile = self.filemenu.addAction("Open") self.savefile = self.filemenu.addAction("Save") self.exitfile = self.filemenu.addAction("Exit") self.filemenu.triggered[QAction].connect(self.ProcessTrigger) self.startalgobtn = QPushButton("Start", self) self.startalgobtn.setGeometry(self.width() - self.startalgobtn.width(), self.menubar.height(), self.startalgobtn.width(), self.startalgobtn.height()) self.startalgobtn.setVisible(False) self.startalgobtn.clicked.connect(self.Algo) self.resetgraphbtn = QPushButton("Reset Graph", self) self.resetgraphbtn.setGeometry( self.width() - self.startalgobtn.width(), self.menubar.height() + self.resetgraphbtn.height(), self.startalgobtn.width(), self.startalgobtn.height()) self.resetgraphbtn.setVisible(False) self.resetgraphbtn.clicked.connect(self.Reset) self.exitbtn = QPushButton("Exit", self) self.exitbtn.setGeometry( self.width() - self.startalgobtn.width(), self.menubar.height() + self.resetgraphbtn.height() + self.exitbtn.height(), self.startalgobtn.width(), self.startalgobtn.height()) self.exitbtn.setVisible(False) self.exitbtn.clicked.connect(self.Exit) self.con_points_lbl = QLabel("meh", self) self.con_points_lbl.setVisible(False) self.sleepslider = QSlider(Qt.Horizontal, self) self.sleepslider.setMinimum(1) self.sleepslider.setMaximum(20) self.sleepslider.setValue(10) self.sleepslider.setTickPosition(QSlider.TicksBelow) self.sleepslider.setTickInterval(1) self.sleepslider.setGeometry( 0, self.height() - self.con_points_lbl.height(), 500, self.sleepslider.height()) self.modelbl = QLabel("Mode:", self) self.modelbl.setGeometry( 0, self.height() - self.sleepslider.height() - 40, 50, self.modelbl.height()) self.editmode = QRadioButton("Edit", self) self.editmode.setGeometry( 52, self.height() - self.sleepslider.height() - 40, 50, self.editmode.height()) self.editmode.setChecked(True) self.choosemode = QRadioButton("Choose starting vertice", self) self.choosemode.setGeometry( 104, self.height() - self.sleepslider.height() - 40, 160, self.choosemode.height()) self.editmode.setVisible(False) self.modelbl.setVisible(False) self.choosemode.setVisible(False) self.sleepslider.setVisible(False) self.show() def ProcessTrigger(self, q): if q == self.newtext: self.NewFile("t") if q == self.newgraphic: self.NewFile("g") if q == self.openfile: self.OpenFile() if q == self.savefile: self.SaveFile() if q == self.exitfile: self.Exit() def mousePressEvent(self, e: QMouseEvent): QApplication.processEvents() if self.editmode.isChecked(): try: if self.mv == None and self.graph_present: for x in self.coords: if (x[0] - 20 <= e.pos().x() <= x[0] + 20) and ( x[1] - 20 <= e.pos().y() <= x[1] + 20): self.mv = self.coords.index(x) self.xdif = e.pos().x() - x[0] self.ydif = e.pos().y() - x[1] self.UpdatePath() self.update() break else: self.mv, self.xdif, self.ydif = None, None, None except: return elif self.choosemode.isChecked(): for coord in self.coords: if (coord[0] - 20 <= e.pos().x() <= coord[0] + 20) and ( coord[1] - 20 <= e.pos().y() <= coord[1] + 20): coord[2] = Qt.darkMagenta self.coords[self.start_vertice][2] = Qt.red self.start_vertice = self.coords.index(coord) self.update() def mouseMoveEvent(self, e: QMouseEvent): QApplication.processEvents() if self.editmode.isChecked(): try: if self.mv != None: self.coords[self.mv] = [ e.pos().x() - self.xdif, e.pos().y() - self.ydif, self.coords[self.mv][2] ] self.UpdatePath() self.update() except: return def mouseReleaseEvent(self, e: QMouseEvent): QApplication.processEvents() if self.editmode.isChecked(): try: if self.mv != None: self.coords[self.mv] = [ e.pos().x() - self.xdif, e.pos().y() - self.ydif, self.coords[self.mv][2] ] self.UpdatePath() self.update() self.mv, self.xdif = None, None except: return def paintEvent(self, a0): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setBackground(Qt.white) painter.setPen(QPen(Qt.black, 2, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) for path in self.paths: painter.drawPath(path) for coord in self.coords: painter.setBrush(coord[2]) painter.drawEllipse(coord[0] - 20, coord[1] - 20, 40, 40) painter.drawText(coord[0] - 10, coord[1] - 10, 20, 20, Qt.AlignCenter, str(self.coords.index(coord) + 1)) painter.setPen(QPen(Qt.darkYellow, 4, Qt.SolidLine)) for i in range(len(self.tin)): painter.drawText(self.coords[i][0] + 20, self.coords[i][1] - 40 // 3, 40, 10, Qt.AlignCenter, str(self.tin[i]) + "/" + str(self.tout[i])) def UpdatePath(self): self.paths = [] for line in self.lines: path = QPainterPath() path.moveTo(QPointF(*self.coords[line[0]][:2])) path.lineTo(QPointF(*self.coords[line[1]][:2])) self.paths.append(path) def ResetGraph(self): self.paths, self.coords, self.tin, self.tout, self.con_points, self.used = [], [], [], [], [], [] self.con_points_lbl.setVisible(False) self.start_vertice = None self.editmode.setVisible(False) self.modelbl.setVisible(False) self.choosemode.setVisible(False) self.sleepslider.setVisible(False) self.update() def Reset(self, e): self.coords = [[*coord[:2], Qt.red] for coord in self.coords] self.coords[0][2] = Qt.darkMagenta self.start_vertice = 0 self.tin = self.tout = [] self.update() def NewFile(self, mode): self.ResetGraph() global graph_config self.from_file = False if mode == "t": self.new_graph_dialog_text.exec_() else: self.new_graph_dialog_graphic.exec_() if graph_config == None: return self.ReadGraph() def OpenFile(self): self.ResetGraph() try: self.from_file = True global graph_config graph_config = QFileDialog.getOpenFileName(self, "Open File", os.getcwd(), "Text Files (*.txt)")[0] self.ReadGraph() except: return def SaveFile(self): global graph_config if graph_config == None: return else: name = list( QFileDialog.getSaveFileName(self, "Save File", os.getcwd(), "Text Files (*.txt)")) if ".txt" not in name[0]: name[0] += ".txt" with open(name[0], "w") as f: f.write(str(self.num_of_peaks) + "\n") f.write("\n".join(self.info)) def Exit(self): QApplication.instance().quit() def ReadGraph(self): try: global graph_config movetopoint = None startlinevertice = None if self.from_file: with open(graph_config) as f: data = f.readlines() else: data = graph_config self.num_of_peaks, *self.info = data self.num_of_peaks = int(self.num_of_peaks) self.used = [False for x in range(self.num_of_peaks)] try: ix = self.info.index("COORDS\n") except: ix = self.info.index("COORDS") vertices_raw = self.info[:ix] coords_raw = self.info[ix + 1:] vertices = [ list(map(int, vertice.split())) for vertice in vertices_raw ] coords = [list(map(int, coord.split())) for coord in coords_raw] self.graph = [vertice[1::] for vertice in vertices] for vertice in vertices: path = QPainterPath() for i, p in enumerate(vertice): point = QPointF(*coords[p - 1]) if i == 0: movetopoint = point startlinevertice = p - 1 path.moveTo(point) else: path.lineTo(point) path.moveTo(movetopoint) self.lines.append([startlinevertice, p - 1]) self.paths.append(path) for i in range(len(coords)): self.coords.append([*coords[i], Qt.red]) self.coords[0] = [*self.coords[0][:2], Qt.darkMagenta] self.start_vertice = 0 self.update() self.startalgobtn.setVisible(True) self.resetgraphbtn.setVisible(True) self.exitbtn.setVisible(True) self.graph_present = True self.editmode.setVisible(True) self.modelbl.setVisible(True) self.choosemode.setVisible(True) self.sleepslider.setVisible(True) except: return def UpdateSleep(self): self.update() QApplication.processEvents() sleep(self.sleepslider.value() / 10) def Algo(self, e): QApplication.processEvents() self.modelbl.setVisible(False) self.editmode.setVisible(False) self.choosemode.setVisible(False) self.algo_starts += 1 if self.algo_starts > 1: QApplication.processEvents() self.con_points_lbl.setVisible(False) for coord in self.coords: coord[2] = Qt.red self.timer = 0 self.tin = self.tout = [0 for x in range(self.num_of_peaks)] self.update() self.used = [False for i in range(self.num_of_peaks)] self.tin = [0 for i in range(self.num_of_peaks)] self.tout = [0 for i in range(self.num_of_peaks)] def dfs(v, p=-1): self.used[v] = True self.coords[v][2] = Qt.yellow self.tin[v] = self.tout[v] = self.timer + 1 QApplication.processEvents() self.UpdateSleep() self.timer += 1 children = 0 QApplication.processEvents() for i in range(0, len(self.graph[v])): to = self.graph[v][i] - 1 if to == p: continue if self.used[to]: self.tout[v] = min(self.tout[v], self.tin[to]) QApplication.processEvents() self.UpdateSleep() else: QApplication.processEvents() dfs(to, v) self.tout[v] = min(self.tout[v], self.tout[to]) if self.tout[to] >= self.tin[v] and p != -1: self.con_points.append(v + 1) self.coords[v][2] = Qt.green children += 1 QApplication.processEvents() self.UpdateSleep() if (p == -1 and children > 1 and (v + 1) not in self.con_points): self.con_points.append(v + 1) self.coords[v][2] = Qt.green QApplication.processEvents() self.UpdateSleep() dfs(self.start_vertice) for i in range(self.num_of_peaks): QApplication.processEvents() if not self.used[i]: QApplication.processEvents() dfs(i) if len(self.con_points) == 0: self.con_points_lbl.setText("Connection point(-s) is(are): None") else: self.con_points_lbl.setText( "Connection point(-s) is(are): " + " ".join(list(map(str, set(self.con_points))))) self.con_points_lbl.setGeometry( self.width() - 400, self.height() - self.con_points_lbl.height(), 400, self.con_points_lbl.height()) self.con_points_lbl.setVisible(True) self.modelbl.setVisible(True) self.editmode.setVisible(True) self.choosemode.setVisible(True)
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.get_main_window_attributes() self.add_menubar() self.add_tracking_options_to_menubar() self.add_plotting_options_to_menubar() self.main_tab = MainTab(self.main_window_width, self.main_window_height) self.setCentralWidget(self.main_tab) self.setMenuBar(self.menubar) self.setWindowTitle('Zebrafish Behaviour Tracking') self.setWindowState(Qt.WindowMaximized) self.show() def get_main_window_attributes(self): self.main_window_width = QDesktopWidget().availableGeometry().width() self.main_window_height = QDesktopWidget().availableGeometry().height() def add_menubar(self): self.menubar = QMenuBar() self.menubar.resize(self.main_window_width, self.menubar.height()) def add_tracking_options_to_menubar(self): self.tracking_options_menu = self.menubar.addMenu('&Tracking Options') self.open_video_action = QAction('&Open Video', self) self.open_video_action.setShortcut('Ctrl+O') self.open_video_action.setStatusTip('Open Video') self.open_video_action.triggered.connect(self.trigger_open_video) self.tracking_options_menu.addAction(self.open_video_action) self.select_save_path_action = QAction('&Select Save Path', self) self.select_save_path_action.setShortcut('Ctrl+P') self.select_save_path_action.setStatusTip('Select Save Path') self.select_save_path_action.triggered.connect( self.trigger_select_save_path) self.tracking_options_menu.addAction(self.select_save_path_action) self.load_background_action = QAction('&Load Background', self) self.load_background_action.setShortcut('Ctrl+L') self.load_background_action.setStatusTip('Load Background') self.load_background_action.triggered.connect( self.trigger_load_background) self.tracking_options_menu.addAction(self.load_background_action) self.calculate_background_action = QAction('&Calculate Background', self) self.calculate_background_action.setShortcut('Ctrl+B') self.calculate_background_action.setStatusTip('Calculate Background') self.calculate_background_action.triggered.connect( self.trigger_calculate_background) self.tracking_options_menu.addAction(self.calculate_background_action) self.save_background_action = QAction('&Save Background', self) self.save_background_action.setShortcut('Ctrl+S') self.save_background_action.setStatusTip('Save Background') self.save_background_action.triggered.connect( self.trigger_save_background) self.tracking_options_menu.addAction(self.save_background_action) self.unload_all_tracking_action = QAction('&Unload All Tracking', self) self.unload_all_tracking_action.setShortcut('Ctrl+U') self.unload_all_tracking_action.setStatusTip( 'Unload All Tracking From Memory') self.unload_all_tracking_action.triggered.connect( self.trigger_unload_all_tracking) self.tracking_options_menu.addAction(self.unload_all_tracking_action) def add_plotting_options_to_menubar(self): self.plotting_options_menu = self.menubar.addMenu('&Plotting Options') self.load_tracking_results_action = QAction('&Load Tracking Results') self.load_tracking_results_action.setStatusTip('Load Tracking Results') self.load_tracking_results_action.triggered.connect( self.trigger_load_tracking_results) self.plotting_options_menu.addAction(self.load_tracking_results_action) self.open_tracked_video_action = QAction('&Open Tracked Video', self) self.open_tracked_video_action.setShortcut('Ctrl+T') self.open_tracked_video_action.setStatusTip('Open Tracked Video') self.open_tracked_video_action.triggered.connect( self.trigger_open_tracked_video) self.plotting_options_menu.addAction(self.open_tracked_video_action) self.unload_all_plotting_action = QAction('&Unload All Plotting', self) self.unload_all_plotting_action.setStatusTip('Unload All Plotting') self.unload_all_plotting_action.triggered.connect( self.trigger_unload_all_plotting) self.plotting_options_menu.addAction(self.unload_all_plotting_action) def trigger_save_background(self): self.main_tab.tracking_window.tracking_content.trigger_save_background( ) def trigger_calculate_background(self): self.main_tab.tracking_window.tracking_content.trigger_calculate_background( ) def trigger_select_save_path(self): self.main_tab.tracking_window.tracking_content.trigger_select_save_path( ) def trigger_load_background(self): self.main_tab.tracking_window.tracking_content.trigger_load_background( ) def trigger_open_video(self): self.main_tab.tracking_window.tracking_content.trigger_open_video() def trigger_open_tracked_video(self): self.main_tab.plotting_window.plotting_content.trigger_open_video() def trigger_unload_all_tracking(self): self.main_tab.tracking_window.tracking_content.trigger_unload_all_tracking( ) def trigger_load_tracking_results(self): self.main_tab.plotting_window.plotting_content.trigger_load_tracking_results( ) def trigger_unload_all_plotting(self): self.main_tab.plotting_window.plotting_content.trigger_unload_all_plotting( ) # Defining Event Functions def closeEvent(self, event): if self.main_tab.tracking_window.tracking_content.calculate_background_progress_window is not None: if self.main_tab.tracking_window.tracking_content.calculate_background_progress_window.isVisible( ): self.main_tab.tracking_window.tracking_content.calculate_background_progress_window.close( ) if self.main_tab.tracking_window.tracking_content.track_video_progress_window is not None: if self.main_tab.tracking_window.tracking_content.track_video_progress_window.isVisible( ): self.main_tab.tracking_window.tracking_content.track_video_progress_window.close( ) if self.main_tab.tracking_window.tracking_content.track_all_videos_progress_window is not None: if self.main_tab.tracking_window.tracking_content.track_all_videos_progress_window.isVisible( ): self.main_tab.tracking_window.tracking_content.track_all_videos_progress_window.close( ) event.accept()