class ViewWidget(QWidget): exercise_name_label = None exercise_name_line = None scroll_area = None base_widget = None exercises_widget = None return_button = None add_button = None def __init__(self): QWidget.__init__(self) self.file = "" self.setup_widget() def setup_widget(self): self.exercise_name_label = QLabel("Exercise name:", self) self.exercise_name_label.move(5, 5) self.exercise_name_label.resize(125, 25) self.add_button = QPushButton("Add", self) self.add_button.resize(75, 25) self.add_button.clicked.connect(self.add_line) self.exercise_name_line = QLineEdit(self) self.exercise_name_line.move(135, 5) self.exercise_name_line.resize(125, 25) self.scroll_area = QScrollArea(self) self.base_widget = QWidget(self) self.scroll_area.setWidget(self.base_widget) self.exercises_widget = QVBoxLayout() self.exercises_widget.setAlignment(Qt.AlignTop) self.base_widget.setLayout(self.exercises_widget) self.return_button = QPushButton("Return wo save", self) def resizeEvent(self, event): self.scroll_area.move(5, 35) self.scroll_area.resize(self.width() - 165, self.height() - 40) self.add_button.move(self.width() - 160 - 75, 5) self.return_button.move(self.width() - 155, 5) self.return_button.resize(150, 40) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) def clear_widget(self): while self.exercises_widget.count() > 0: self.exercises_widget.takeAt(0) def open_exercise_file(self, file: str): self.file = file with open(self.file, "r") as json_file: json_data = json.load(json_file) name = json_data['name'] for data in json_data['exercise']: movement = data['name'] description = data['description'] time = data['time'] widget = PanelWidget() widget.set_data(movement, description, time) widget.remove_signal.connect(self.remove_panel_item) widget.move_down_signal.connect(self.move_widget_down) widget.move_up_signal.connect(self.move_widget_up) self.exercises_widget.addWidget(widget) json_file.close() self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) self.exercise_name_line.setText(name) @Slot() def add_line(self): widget = PanelWidget() self.exercises_widget.addWidget(widget) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25) @Slot(QWidget) def move_widget_down(self, widget: QWidget): ind = self.exercises_widget.indexOf(widget) self.exercises_widget.removeWidget(widget) self.exercises_widget.insertWidget((ind + 1), widget) @Slot(QWidget) def move_widget_up(self, widget: QWidget): ind = self.exercises_widget.indexOf(widget) self.exercises_widget.removeWidget(widget) self.exercises_widget.insertWidget((ind - 1), widget) @Slot(QWidget) def remove_panel_item(self, widget: QWidget): self.exercises_widget.removeWidget(widget) self.base_widget.resize(self.scroll_area.width() - 25, self.exercises_widget.count() * 25)
class RootsApp(QMainWindow): standard_deviation_threshold = 0.1 # when I receive a measurement from the sensor I check if its standard deviation; if it's too low it means the sensor is not working temporary_database_filename = "temporary.db" # the current session is stored in a temporary database. When the user saves, it is copied at the desired location def __init__(self): super().__init__(); self.setWindowTitle("Roots") self.setFixedWidth(1200) self.resize(1200, 1200) self.threadpool = QThreadPool(); self.object_list = list() self.is_training_on = False self.interaction_under_training = None self.n_measurements_collected = 0 self.n_measurements_to_collect = 3 self.sensor_not_responding = True self.sensor_not_responding_timeout = 2000 # milliseconds self.database_connection = self.create_temporary_database() self.active_object = None self.number_of_objects_added = 0 self.sensor_start_freq = 250000 self.sensor_end_freq = 3000000 # creates the plot self.plotWidget = pyqtgraph.PlotWidget(title = "Sensor Response") self.plotWidget.setFixedHeight(300) self.plotWidget.getAxis("bottom").setLabel("Excitation frequency", "Hz") self.plotWidget.getAxis("left").setLabel("Volts", "V") self.dataPlot = self.plotWidget.plot() # timer used to see if the sensor is responding self.timer = QTimer() self.timer.setInterval(self.sensor_not_responding_timeout) self.timer.timeout.connect(self.timer_timeout) self.timer_timeout() # defines the actions in the file menu with button actions iconExit = QIcon("icons/icon_exit.png") btnActionExit = QAction(iconExit, "Exit", self) btnActionExit.setStatusTip("Click to terminate the program") btnActionExit.triggered.connect(self.exit) iconSave = QIcon("icons/icon_save.ico") buttonActionSave = QAction(iconSave, "Save current set of objects", self) # buttonActionSave.setStatusTip("Click to perform action 2") buttonActionSave.triggered.connect(self.save) iconOpen = QIcon("icons/icon_load.png") buttonActionOpen = QAction(iconOpen, "Load set of objects", self) buttonActionOpen.triggered.connect(self.open) # toolbar toolBar = QToolBar("Toolbar") toolBar.addAction(buttonActionSave) toolBar.addAction(buttonActionOpen) toolBar.setIconSize(QSize(64, 64)) toolBar.setStyleSheet(styles.toolbar) self.addToolBar(toolBar) # menu menuBar = self.menuBar() menuBar.setStyleSheet(styles.menuBar) menuFile = menuBar.addMenu("File") menuOptions = menuBar.addMenu("Options") menuView = menuBar.addMenu("View") menuConnect = menuBar.addMenu("Connect") menuFile.addAction(buttonActionSave) menuFile.addAction(buttonActionOpen) menuFile.addAction(btnActionExit) # status bar self.setStatusBar(QStatusBar(self)) # creates the "My Objects" label labelMyObjects = QLabel("My Objects") labelMyObjects.setFixedHeight(100) labelMyObjects.setAlignment(Qt.AlignCenter) labelMyObjects.setStyleSheet(styles.labelMyObjects) # button "add object" icon_plus = QIcon("icons/icon_add.png") self.btn_create_object = QPushButton("Add Object") self.btn_create_object.setCheckable(False) self.btn_create_object.setIcon(icon_plus) self.btn_create_object.setFixedHeight(80) self.btn_create_object.setStyleSheet(styles.addObjectButton) self.btn_create_object.clicked.connect(self.create_object) # defines the layout of the "My Objects" section self.verticalLayout = QVBoxLayout() self.verticalLayout.setContentsMargins(0,0,0,0) self.verticalLayout.addWidget(labelMyObjects) self.verticalLayout.addWidget(self.btn_create_object) self.spacer = QSpacerItem(0,2000, QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout.setSpacing(0) self.verticalLayout.addSpacerItem(self.spacer) #adds spacer # defines the ComboBox which holds the names of the objects self.comboBox = QComboBox() self.comboBox.addItem("- no object selected") self.comboBox.currentIndexChanged.connect(self.comboBox_index_changed) self.comboBox.setFixedSize(300, 40) self.comboBox.setStyleSheet(styles.comboBox) self.update_comboBox() # defines the label "Selected Object" (above the comboBox) self.labelComboBox = QLabel() self.labelComboBox.setText("Selected Object:") self.labelComboBox.setStyleSheet(styles.labelComboBox) self.labelComboBox.adjustSize() # vertical layout for the combobox and its label self.VLayoutComboBox = QVBoxLayout() self.VLayoutComboBox.addWidget(self.labelComboBox) self.VLayoutComboBox.addWidget(self.comboBox) # label with the output text (the big one on the right) self.labelClassification = QLabel() self.labelClassification.setText("No interaction detected") self.labelClassification.setFixedHeight(80) self.labelClassification.setStyleSheet(styles.labelClassification) self.labelClassification.adjustSize() HLayoutComboBox = QHBoxLayout() HLayoutComboBox.addLayout(self.VLayoutComboBox) HLayoutComboBox.addSpacerItem(QSpacerItem(1000,0, QSizePolicy.Expanding, QSizePolicy.Expanding)); #adds spacer HLayoutComboBox.addWidget(self.labelClassification) # creates a frame that contains the combobox and the labels frame = QFrame() frame.setStyleSheet(styles.frame) frame.setLayout(HLayoutComboBox) # sets the window layout with the elements created before self.windowLayout = QVBoxLayout() self.windowLayout.addWidget(self.plotWidget) self.windowLayout.addWidget(frame) self.windowLayout.addLayout(self.verticalLayout) # puts everything into a frame and displays it on the window self.mainWindowFrame = QFrame() self.mainWindowFrame.setLayout(self.windowLayout) self.mainWindowFrame.setStyleSheet(styles.mainWindowFrame) self.setCentralWidget(self.mainWindowFrame) self.create_object() # creates one object at the beginning # ----------------------------------------------------------------------------------------------------------- # Shows a welcome message def show_welcome_msg(self): welcome_msg = QMessageBox() welcome_msg.setText("Welcome to the Roots application!") welcome_msg.setIcon(QMessageBox.Information) welcome_msg.setInformativeText(strings.welcome_text) welcome_msg.setWindowTitle("Welcome") welcome_msg.exec_() # ----------------------------------------------------------------------------------------------------------- # When the user changes the object in the combobox, updates the active object def comboBox_index_changed(self, index): object_name = self.comboBox.currentText() for object in self.object_list: if object.name == object_name: self.set_active_object(object) print("DEBUG: selected object changed. Object name: {0}".format(object.name)) return # ----------------------------------------------------------------------------------------------------------- # This function allows to save the current objects on a file def save(self): current_path = os.getcwd() directory_path = current_path + "/Saved_Workspaces" if not os.path.exists(directory_path): os.mkdir(directory_path) file_path = None [file_path, file_extension] = QFileDialog.getSaveFileName(self,"Roots", directory_path, "Roots database (*.db)") if file_path is None: return temp_database_path = current_path + "/" + RootsApp.temporary_database_filename shutil.copyfile(temp_database_path, file_path) # copies the temporary database to save the current workspace return # ----------------------------------------------------------------------------------------------------------- # this function creates a clean database where all the data of this session will be temporarily stored def create_temporary_database(self): current_path = os.getcwd() file_path = current_path + "/" + RootsApp.temporary_database_filename if os.path.exists(file_path): # if the database is already there it deletes it to reset it os.remove(file_path) print("DEBUG: removing database. (in 'RootsApp.create_temporary_database()'") database_connection = database.create_connection(RootsApp.temporary_database_filename) # creates the temporary database database.create_tables(database_connection) # initializes the database database.reset_db(database_connection) # resets the database (not needed but it doesn't cost anything to put it) return database_connection # ----------------------------------------------------------------------------------------------------------- # This function allows to load previously created objects from a file def open(self): current_path = os.getcwd() saved_files_directory = current_path + "/Saved_Workspaces" [file_path, file_extension] = QFileDialog.getOpenFileName(self,"Roots", saved_files_directory, "Roots database (*.db)"); if file_path == '': return for object in self.object_list.copy(): # deletes all the objects print("DEBUG: deleting object {0} (in 'open()')".format(object.name)) self.delete_object(object) temp_database_path = current_path + "/" + RootsApp.temporary_database_filename self.database_connection.close() os.remove(temp_database_path) shutil.copyfile(file_path, temp_database_path) # replaces the temporary database with the file to open self.database_connection = database.create_connection(temp_database_path) object_tuples = database.get_all_objects(self.database_connection) for object_tuple in object_tuples: object_ID, object_name = object_tuple location_IDs = database.get_locations_id_for_object(self.database_connection, object_ID) formatted_location_IDs = [] for location_ID in location_IDs: formatted_location_IDs.append(location_ID[0]) print("DEBUG: loading object {0} with location IDs {1}. (in 'RootsApp.open()')".format(object_name, formatted_location_IDs)) self.add_object(object_name, object_ID, formatted_location_IDs) self.train_classifiers() return # ----------------------------------------------------------------------------------------------------------- # This function updates the ComboBox whenever objects are created, destroyed or the active object has changed def update_comboBox(self): print("DEBUG: repainting ComboBox. (in 'RootsApp.update_comboBox()'") self.comboBox.clear() self.comboBox.addItem("none") for object in self.object_list: self.comboBox.addItem(object.name) self.comboBox.adjustSize() # ----------------------------------------------------------------------------------------------------------- # This is a timer which is restarted every time a measurement is received. If it elapses it means that the sesnor is not connected def timer_timeout(self): print("DEBUG: timer timeout. (in 'RootsApp.timer_timeout()'") self.sensor_not_responding = True self.statusBar().showMessage(strings.sensor_disconnected) self.statusBar().setStyleSheet(styles.statusBarError) self.plotWidget.setTitle("Sensor not connected") # ----------------------------------------------------------------------------------------------------------- # This function creates a new object in the database and then calls the "add_object" function, which adds the newly created object to the application def create_object(self): new_object_name = "Object {0}".format(self.number_of_objects_added + 1) [new_object_ID, location_IDs] = database.create_object(self.database_connection, new_object_name) self.add_object(new_object_name, new_object_ID, location_IDs) # ----------------------------------------------------------------------------------------------------------- # This function deletes an object from the database, and from the application object list. It alsos destroys the object def delete_object(self, object): print("DEBUG: deleting object {0}. (in 'RootsApp.delete_object()')".format(object.ID)) database.delete_object(self.database_connection, object.ID) self.object_list.remove(object) self.verticalLayout.removeItem(object.layout) self.update_comboBox() object.delete() # ----------------------------------------------------------------------------------------------------------- # This function adds an object to the current application. Note that if you want to create an object ex-novo you should call "create_object". This function is useful when loading existing objects from a file def add_object(self, name, object_ID, location_IDs): self.number_of_objects_added += 1 new_object = Object(name, object_ID, location_IDs, self) self.object_list.append(new_object) for ID in location_IDs: # initializes the measurements with 0 if the measurement is empty #print("DEBUG: initializing location ID {0}".format(ID)) measurements = database.get_measurements_for_location(self.database_connection, ID) print("DEBUG: location {0} of object {1} is trained: {2}. (in 'RootsApp.add_object()')".format(ID, new_object.name, database.is_location_trained(self.database_connection, ID))) if len(measurements) == 0: database.save_points(self.database_connection, [0], ID) database.set_location_trained(self.database_connection, ID, "FALSE") elif database.is_location_trained(self.database_connection, ID) == "TRUE": new_object.get_interaction_by_ID(ID).setCalibrated(True) # inserts the newly created object before the "Add Object" button index = self.verticalLayout.indexOf(self.btn_create_object) self.verticalLayout.insertLayout(index, new_object.layout) self.update_comboBox() print("DEBUG: object {0} added. (in 'RootsApp.add_object()')".format(new_object.name)) return # ----------------------------------------------------------------------------------------------------------- # This function takes as input the measurement data and formats it to plot it on the graph def update_graph(self, data): frequency_step = (self.sensor_end_freq - self.sensor_start_freq) / len(data) x_axis = numpy.arange(self.sensor_start_freq, self.sensor_end_freq, frequency_step) formatted_data = numpy.transpose(numpy.asarray([x_axis, data])) self.dataPlot.setData(formatted_data) # ----------------------------------------------------------------------------------------------------------- # This function starts the UDP server that receives the measurements def run_UDP_server(self, UDP_IP, UDP_PORT): self.UDPServer = UDPServer(UDP_IP, UDP_PORT) self.UDPServer.signals.measurementReceived.connect(self.process_measurement) self.threadpool.start(self.UDPServer) # ----------------------------------------------------------------------------------------------------------- # This function changes some global variables to tell the application to save the incoming measurements into the database. The measurements belong to the interaction passed as argument def start_collecting_measurements(self, interaction): if self.sensor_not_responding: print("DEBUG: warning! Can't start calibration, the sensor is not responding! (in 'RootsApp.start_collecting_measurements()')") error_msg = QMessageBox() error_msg.setText("Can't start calibration!") error_msg.setIcon(QMessageBox.Critical) error_msg.setInformativeText('The sensor is not responding, make sure it is connected') error_msg.setWindowTitle("Error") error_msg.exec_() else: print("starting to collect measurements into the database at location ID {0} (in 'RootsApp.start_collecting_measurements()')".format(interaction.ID)); self.is_training_on = True self.interaction_under_training = interaction database.delete_measurements_from_location(self.database_connection, interaction.ID) # resets the location measurements self.progress_dialog = QProgressDialog("Calibrating", "Abort", 0, self.n_measurements_to_collect, self) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.setWindowTitle("Calibration") self.progress_dialog.setFixedSize(400, 200) self.progress_dialog.setValue(0) self.progress_dialog.exec_() # ----------------------------------------------------------------------------------------------------------- # This function is called by the UDP thread every time that a measurement is received. It does the following: # 1. Plots the incoming measurement # 2. IF training mode IS on: # Predicts the interaction (tries to guess where the user is touching) # ELSE: # Saves the measurement and retrains the classifier with the new data def process_measurement(self, received_data): self.sensor_not_responding = False self.plotWidget.setTitle("Sensor response") self.timer.start() # starts the timer that checks if we are receiving data from the sensor measurement = received_data.split(' ') # get rid of separator measurement = [float(i) for i in measurement] # convert strings to float self.update_graph(measurement) self.predict_interaction(measurement) # checks the standard deviation of the received data to see if the sensor is working well if (numpy.std(measurement) < self.standard_deviation_threshold): self.statusBar().showMessage(strings.sensor_not_working) self.statusBar().setStyleSheet(styles.statusBarError) else: self.statusBar().setStyleSheet(styles.statusBar) if self.is_training_on: print("saving measurement {0} into database at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_collected + 1, self.interaction_under_training.ID)) database.save_points(self.database_connection, measurement, self.interaction_under_training.ID) self.n_measurements_collected += 1 self.progress_dialog.setValue(self.n_measurements_collected) if (self.n_measurements_collected >= self.n_measurements_to_collect): self.is_training_on = False self.n_measurements_collected = 0 print("DEBUG: {0} measurements were saved at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_to_collect, self.interaction_under_training.ID)) self.train_classifiers() self.interaction_under_training.setCalibrated(True) # this makes the button "Calibrate" change coulour # ----------------------------------------------------------------------------------------------------------- # This function retrains the classifiers using all the measurements present in the database and assigns to each object its classifier def train_classifiers(self): #[objects_ID, classifiers] classifiers = classifier.get_classifiers(self.database_connection) print("DEBUG: the following classifiers were created: {0}. (in 'RootsApp.train_classifiers')".format(classifiers)) for object in self.object_list: for index, tuple in enumerate(classifiers): object_ID, classif = tuple; # extracts the object ID and the classifier from the tuple if object_ID == object.ID: object.classifier = classif del classifiers[index] # ----------------------------------------------------------------------------------------------------------- # This function changes the current active object (the software tries to guess where the user is touching using the calibration data from the active object) def set_active_object(self, active_object): self.active_object = active_object for obj in self.object_list: if obj == active_object: active_object.set_highlighted(True) else: obj.set_highlighted(False) index = self.comboBox.findText(self.active_object.name) # updates the index of the ComboBox self.comboBox.setCurrentIndex(index) # ----------------------------------------------------------------------------------------------------------- # This function changes the name of an object. It updates the database AND the application data structure. def rename_object(self, object, new_name): print("DEBUG: changing name of object '{0}' (in 'RootsApp.rename_object')".format(object.name)) object.set_name(new_name) database.rename_object(self.database_connection, object.ID, new_name) self.update_comboBox() # ----------------------------------------------------------------------------------------------------------- # This function uses the classifier of the active object to guess where the user is touching, based on the incoming measurement def predict_interaction(self, measurement): if (len(self.object_list) <= 0): self.labelClassification.setText("No objects available") self.statusBar().showMessage(strings.no_objects) return if self.active_object is None: self.labelClassification.setText("No object selected") self.statusBar().showMessage(strings.no_object_selected) return if self.active_object.classifier is None: self.labelClassification.setText("The object is not calibrated") self.statusBar().showMessage(strings.object_not_calibrated) return else: predicted_interaction_id = self.active_object.classifier(measurement) interaction = self.active_object.get_interaction_by_ID(predicted_interaction_id) self.labelClassification.setText(interaction.name) self.statusBar().showMessage("") #print("DEBUG: predicted interaction ID: ", interaction.ID) # ----------------------------------------------------------------------------------------------------------- # This is a system event that gets called whenever the user tries to close the application. It calls the "exit()" # function (just below) to open a dialog to make sure the user really wants to quit. def closeEvent(self, event): if not self.exit(): event.ignore() # ----------------------------------------------------------------------------------------------------------- # This function gets called when the user cliks on the "Exit" button in the "File" menu or when it tries to close the window (indirectly) # Here we open a dialog to make sure the user really wants to quit. def exit(self): dialogWindow = DialogExit() answer = dialogWindow.exec_() if (answer == True): self.UDPServer.stop() self.close() return answer
class DBWorkspace(QWidget): def __init__(self, db, table, parent=None): super().__init__(parent) self.main_layout = QVBoxLayout() self.tab_widget = None self.handler = None self.db = db self.table = table self.table_model = None self.create_tab_widget() self.main_table = DBTableView(self.tab_widget) self.main_table.setSelectionMode(QAbstractItemView.SingleSelection) self.main_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.init_table() self.main_table.clicked.connect(self.row_selected) self.package = QVBoxLayout() self.package.addStretch(1) self.toolBar = QToolBar() self.toolBar.setMovable(True) self.up = QAction( QIcon("view/images/toolbar/baseline_expand_less_black_48dp.png"), "Up", self.main_table) self.up.setStatusTip("Move one up") self.up.triggered.connect(self.jump_up) self.down = QAction( QIcon("view/images/toolbar/baseline_expand_more_black_48dp.png"), "Down", self.main_table) self.down.setStatusTip("Move one down") self.down.triggered.connect(self.jump_down) self.first = QAction( QIcon("view/images/toolbar/baseline_first_page_black_48dp.png"), "Jump to first", self.main_table) self.first.setStatusTip("Jump to first") self.first.triggered.connect(self.jump_to_first) self.last = QAction( QIcon("view/images/toolbar/baseline_last_page_black_48dp.png"), "Jump to last", self.main_table) self.last.setStatusTip("Jump to last") self.last.triggered.connect(self.jump_to_last) self.toolBar.addAction(QIcon("view/images/toolbar/list_48px.png"), "Manage Data") self.toolBar.addAction(QIcon("view/images/toolbar/add_new_40px.png"), "Add Data") self.toolBar.addAction(self.first) self.toolBar.addAction(self.up) self.toolBar.addAction(self.down) self.toolBar.addAction(self.last) self.toolBar.addAction( QIcon("view/images/toolbar/close_window_26px.png"), "Close") self.manageData = DBManageData(self.main_table) self.addData = DBAddData(self.main_table) self.stacked_layout = QVBoxLayout() self.label = QLabel() self.label.setText("Work with data, choose state.") self.toolBar.actionTriggered.connect(self.toolbar_actions) self.package.addWidget(self.toolBar) self.package.addLayout(self.stacked_layout) self.package.addWidget(self.main_table) self.main_layout.addLayout(self.package) self.selected_row = 0 self.main_layout.addStretch(2) self.setLayout(self.main_layout) def toolbar_actions(self, action): if action.iconText() == "Manage Data": if self.stacked_layout.indexOf( self.addData) == -1 and self.stacked_layout.indexOf( self.manageData) == -1 and self.stacked_layout.indexOf( self.label) == -1: self.stacked_layout.addWidget(self.manageData) self.manageData.setVisible(True) return if self.stacked_layout.indexOf(self.addData) != -1: self.stacked_layout.removeWidget(self.addData) self.addData.setVisible(False) self.stacked_layout.addWidget(self.manageData) self.manageData.setVisible(True) return if self.stacked_layout.indexOf(self.label) != -1: self.stacked_layout.removeWidget(self.label) self.label.setVisible(False) self.stacked_layout.addWidget(self.manageData) self.manageData.setVisible(True) return return elif action.iconText() == "Add Data": if self.stacked_layout.indexOf( self.addData) == -1 and self.stacked_layout.indexOf( self.manageData) == -1 and self.stacked_layout.indexOf( self.label) == -1: self.stacked_layout.addWidget(self.addData) self.addData.setVisible(True) return if self.stacked_layout.indexOf(self.manageData) != -1: self.stacked_layout.removeWidget(self.manageData) self.manageData.setVisible(False) self.stacked_layout.addWidget(self.addData) self.addData.setVisible(True) return if self.stacked_layout.indexOf(self.label) != -1: self.stacked_layout.removeWidget(self.label) self.label.setVisible(False) self.stacked_layout.addWidget(self.addData) self.addData.setVisible(True) return return elif action.iconText() == "Close": if self.stacked_layout.indexOf( self.addData) == -1 and self.stacked_layout.indexOf( self.manageData) == -1 and self.stacked_layout.indexOf( self.label) == -1: self.stacked_layout.addWidget(self.label) self.label.setVisible(True) return if self.stacked_layout.indexOf(self.addData) != -1: self.stacked_layout.removeWidget(self.addData) self.addData.setVisible(False) self.stacked_layout.addWidget(self.label) self.label.setVisible(True) return if self.stacked_layout.indexOf(self.manageData) != -1: self.stacked_layout.removeWidget(self.manageData) self.manageData.setVisible(False) self.stacked_layout.addWidget(self.label) self.label.setVisible(True) return return def row_selected(self, index): if index.column() == len(self.main_table.model().columns): model = self.main_table.model() selected_data = model.get_element(index) self.selected_row = index.row() def create_tab_widget(self): self.tab_widget = QTabWidget() self.tab_widget.setTabsClosable(True) self.tab_widget.tabCloseRequested.connect(self.delete_tab) def delete_tab(self, index): self.tab_widget.removeTab(index) def init_table(self): self.handler = DBHandler(self.db, self.table, self) self.table_model = DBTableModel(self.handler) self.main_table.setModel(self.table_model) """ Table navigation jumps """ def jump_up(self): if self.selected_row != 0: self.selected_row -= 1 self.main_table.selectRow(self.selected_row) def jump_down(self): if self.selected_row < len(self.main_table.model().displayed_d) - 1: self.selected_row += 1 self.main_table.selectRow(self.selected_row) def jump_to_first(self): self.selected_row = 0 self.main_table.selectRow(self.selected_row) def jump_to_last(self): self.selected_row = len(self.main_table.model().displayed_d) - 1 self.main_table.selectRow(self.selected_row) def sub_up(self): if self.selected_row != 0: self.selected_row -= 1 self.subtable.selectRow(self.selected_row) def sub_down(self): if self.selected_row < len(self.subtable.model().displayed_d) - 1: self.selected_row += 1 self.subtable.selectRow(self.selected_row) def sub_first(self): self.selected_row = 0 self.subtable.selectRow(self.selected_row) def sub_last(self): self.selected_row = len(self.subtable.model().displayed_d) - 1 self.subtable.selectRow(self.selected_row)
class SubtableWidget(QWidget): def __init__(self, subtable, parent=None): super().__init__(parent) self.subtable = subtable self.subtable.setSelectionMode(QAbstractItemView.SingleSelection) self.subtable.setSelectionBehavior(QAbstractItemView.SelectRows) self.subtable.clicked.connect(self.row_selected) self.selected_row = 0 self.package = QVBoxLayout() self.toolBar = QToolBar() self.toolBar.setMovable(True) sub_up = QAction( QIcon("view/images/toolbar/baseline_expand_less_black_48dp.png"), "Up", self.subtable) sub_up.setStatusTip("Move one up") sub_up.triggered.connect(self.sub_up) sub_down = QAction( QIcon("view/images/toolbar/baseline_expand_more_black_48dp.png"), "Down", self.subtable) sub_down.setStatusTip("Move one down") sub_down.triggered.connect(self.sub_down) sub_first = QAction( QIcon("view/images/toolbar/baseline_first_page_black_48dp.png"), "Jump to first", self.subtable) sub_first.setStatusTip("Jump to first") sub_first.triggered.connect(self.sub_first) sub_last = QAction( QIcon("view/images/toolbar/baseline_last_page_black_48dp.png"), "Jump to last", self.subtable) sub_last.setStatusTip("Jump to last") sub_last.triggered.connect(self.sub_last) self.toolBar.addAction(QIcon("view/images/toolbar/list_48px.png"), "Manage Data") self.toolBar.addAction(QIcon("view/images/toolbar/add_new_40px.png"), "Add Data") self.toolBar.addAction(sub_first) self.toolBar.addAction(sub_up) self.toolBar.addAction(sub_down) self.toolBar.addAction(sub_last) self.toolBar.addAction( QIcon("view/images/toolbar/close_window_26px.png"), "Close") self.manageData2 = ManageData(self.subtable) self.addData2 = AddData(self.subtable) self.label = QLabel() self.label.setText("Work with data, choose state.") self.stacked_layout_2 = QVBoxLayout() self.stacked_layout_2.addStretch(0) self.toolBar.actionTriggered.connect(self.sub_toolbar_actions) self.package.addWidget(self.toolBar) self.package.addLayout(self.stacked_layout_2) self.package.addWidget(self.subtable) self.setLayout(self.package) def sub_toolbar_actions(self, action): if action.iconText() == "Manage Data": if self.stacked_layout_2.indexOf( self.addData2) == -1 and self.stacked_layout_2.indexOf( self.manageData2 ) == -1 and self.stacked_layout_2.indexOf( self.label) == -1: self.stacked_layout_2.addWidget(self.manageData2) self.manageData2.setVisible(True) return if self.stacked_layout_2.indexOf(self.addData2) != -1: self.stacked_layout_2.removeWidget(self.addData2) self.addData2.setVisible(False) self.stacked_layout_2.addWidget(self.manageData2) self.manageData2.setVisible(True) return if self.stacked_layout_2.indexOf(self.label) != -1: self.stacked_layout_2.removeWidget(self.label) self.label.setVisible(False) self.stacked_layout_2.addWidget(self.manageData2) self.manageData2.setVisible(True) return return elif action.iconText() == "Add Data": if self.stacked_layout_2.indexOf( self.addData2) == -1 and self.stacked_layout_2.indexOf( self.manageData2 ) == -1 and self.stacked_layout_2.indexOf( self.label) == -1: self.stacked_layout_2.addWidget(self.addData2) self.addData2.setVisible(True) return if self.stacked_layout_2.indexOf(self.manageData2) != -1: self.stacked_layout_2.removeWidget(self.manageData2) self.manageData2.setVisible(False) self.stacked_layout_2.addWidget(self.addData2) self.addData2.setVisible(True) return if self.stacked_layout_2.indexOf(self.label) != -1: self.stacked_layout_2.removeWidget(self.label) self.label.setVisible(False) self.stacked_layout_2.addWidget(self.addData2) self.addData2.setVisible(True) return return elif action.iconText() == "Close": if self.stacked_layout_2.indexOf( self.addData2) == -1 and self.stacked_layout_2.indexOf( self.manageData2 ) == -1 and self.stacked_layout_2.indexOf( self.label) == -1: self.stacked_layout_2.addWidget(self.label) self.label.setVisible(True) return if self.stacked_layout_2.indexOf(self.addData2) != -1: self.stacked_layout_2.removeWidget(self.addData2) self.addData2.setVisible(False) self.stacked_layout_2.addWidget(self.label) self.label.setVisible(True) return if self.stacked_layout_2.indexOf(self.manageData2) != -1: self.stacked_layout_2.removeWidget(self.manageData2) self.manageData2.setVisible(False) self.stacked_layout_2.addWidget(self.label) self.label.setVisible(True) return return def row_selected(self, index): self.selected_row = index.row() def sub_up(self): if self.selected_row != 0: self.selected_row -= 1 self.subtable.selectRow(self.selected_row) def sub_down(self): if self.selected_row < len(self.subtable.model().displayed_d) - 1: self.selected_row += 1 self.subtable.selectRow(self.selected_row) def sub_first(self): self.selected_row = 0 self.subtable.selectRow(self.selected_row) def sub_last(self): self.selected_row = len(self.subtable.model().displayed_d) - 1 self.subtable.selectRow(self.selected_row)