class ConfigWindow(QWidget): def __init__(self, current_profile): """ The ConfigWindow is a widget dedicated to reading and editing the OCI config file and provides functionality to create, edit, and switch profiles on the fly, updating the view of the main window. :param current_profile: The profile the ConfigWindow should be initialized with :type current_profile: string """ super().__init__() # self.main_window = None self.setWindowTitle("Profile Settings") self.setMinimumSize(600, 200) #Looks for the config file in '~/.oci/config' and reads it into config self.DEFAULT_LOCATION = os.path.expanduser( os.path.join('~', '.oci', 'config')) self.config = configparser.ConfigParser(interpolation=None) self.config.read(self.DEFAULT_LOCATION) self.current_profile = current_profile #Set up necessary dropdown and LineEdit widgets self.dropdown = self.get_profiles_dropdown() self.tenancy = QLineEdit() self.tenancy.setPlaceholderText("Tenancy OCID") self.region = QLineEdit() self.region.setPlaceholderText("Region") self.user = QLineEdit() self.user.setPlaceholderText("User OCID") self.fingerprint = QLineEdit() self.fingerprint.setPlaceholderText("Fingerprint") self.key_file = QLineEdit() self.key_file.setPlaceholderText("Key File Path") self.passphrase = QLineEdit() self.passphrase.setEchoMode(QLineEdit.Password) self.passphrase.setPlaceholderText("Passphrase") self.save_button = QPushButton('Save') self.save_button.clicked.connect(self.save_signal) #Set the profile to the current_profile passed in upon init self.change_profile(current_profile) self.dropdown.setCurrentText(current_profile) #Add all widgets to a vertical layout self.layout = QVBoxLayout() self.layout.addWidget(self.dropdown) self.layout.addWidget(self.tenancy) self.layout.addWidget(self.region) self.layout.addWidget(self.user) self.layout.addWidget(self.key_file) self.layout.addWidget(self.fingerprint) self.layout.addWidget(self.passphrase) self.layout.addWidget(self.save_button) self.setLayout(self.layout) def get_profiles_dropdown(self): """ :return: A dropdown menu widget that lists all profiles including the default profile from the OCI config file When index changes, it will call the change_profile signal function :rtype: :class: 'Qt.QtWidgets.QComboBox' """ dropdown = QComboBox() dropdown.addItems(['DEFAULT'] + self.config.sections()) dropdown.addItem("Add New Profile...") dropdown.currentIndexChanged.connect(self.change_profile_signal) return dropdown def change_profile_signal(self, item): """ Slot to change profile. If the item index is at 0, then it is the default profile. If it is the last index, then that means create a new profile :param item: The index of the item from the dropdown widget :type item: int """ if item > len(self.config.sections()): self.create_new_profile() elif item == 0: self.change_profile('DEFAULT') else: self.change_profile(self.config.sections()[item - 1]) def change_profile(self, profile_name): """ Changes the profile that the ConfigWindow is set for and also changes it for the MainWindow :param profile_name: the name of the profile to switch to :type profile_name: string TODO: Adhere to signal/slot convention """ self.current_profile = profile_name profile = self.config[profile_name] for line, key in zip([self.tenancy, self.region, self.user, self.fingerprint, self.key_file, self.passphrase],\ ['tenancy', 'region', 'user', 'fingerprint', 'key_file', 'pass_phrase']): if key in profile: line.setText(profile[key]) else: line.setText("") if self.main_window: self.main_window.change_profile(self.current_profile) def create_new_profile(self): """ Layout to create a new profile. Removes the dropdown widget and changes the buttons """ self.layout.removeItem(self.layout.itemAt(0)) self.dropdown.setParent(None) self.new_profile_name = QLineEdit() self.new_profile_name.setPlaceholderText("Profile Name") self.layout.insertWidget(0, self.new_profile_name) self.tenancy.setText("") self.region.setText("") self.user.setText("") self.fingerprint.setText("") self.key_file.setText("") self.passphrase.setText("") self.create_button = QPushButton('Create') self.create_button.clicked.connect(self.create_signal) self.cancel_button = QPushButton('Cancel') self.cancel_button.clicked.connect(self.cancel_signal) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.addButton(self.create_button, QDialogButtonBox.ActionRole) self.buttonBox.addButton(self.cancel_button, QDialogButtonBox.ActionRole) self.layout.removeItem(self.layout.itemAt(7)) self.save_button.setParent(None) self.layout.addWidget(self.buttonBox) def create_signal(self): """ Create a new profile with the given information in the LineEdit widgets. Saves to the OCI config file """ profile_name = self.new_profile_name.text() self.config[profile_name] = {} self.config[profile_name]['tenancy'] = self.tenancy.text() self.config[profile_name]['region'] = self.region.text() self.config[profile_name]['user'] = self.user.text() self.config[profile_name]['fingerprint'] = self.fingerprint.text() self.config[profile_name]['key_file'] = self.key_file.text() self.config[profile_name]['pass_phrase'] = self.passphrase.text() with open(self.DEFAULT_LOCATION, 'w') as configfile: self.config.write(configfile) self.current_profile = profile_name self.cancel_signal() def save_signal(self): """ Saves edits on a currently existing profile. Saves to the OCI config file """ self.config[self.current_profile]['tenancy'] = self.tenancy.text() self.config[self.current_profile]['region'] = self.region.text() self.config[self.current_profile]['user'] = self.user.text() self.config[ self.current_profile]['fingerprint'] = self.fingerprint.text() self.config[self.current_profile]['key_file'] = self.key_file.text() self.config[ self.current_profile]['pass_phrase'] = self.passphrase.text() with open(self.DEFAULT_LOCATION, 'w') as configfile: self.config.write(configfile) def cancel_signal(self): """ Cancels the creation a new profile and reverts layout to default layout """ self.layout.removeItem(self.layout.itemAt(0)) self.new_profile_name.setParent(None) self.dropdown = self.get_profiles_dropdown() self.layout.insertWidget(0, self.dropdown) self.layout.removeItem(self.layout.itemAt(7)) self.buttonBox.setParent(None) self.change_profile(self.current_profile) self.dropdown.setCurrentText(self.current_profile) self.layout.addWidget(self.save_button)
class QGroundObjectMenu(QDialog): def __init__(self, parent, ground_object: TheaterGroundObject, buildings: [], cp: ControlPoint, game: Game): super(QGroundObjectMenu, self).__init__(parent) self.setMinimumWidth(350) self.ground_object = ground_object self.buildings = buildings self.cp = cp self.game = game self.setWindowTitle("Location " + self.ground_object.obj_name) self.setWindowIcon(EVENT_ICONS["capture"]) self.intelBox = QGroupBox("Units :") self.buildingBox = QGroupBox("Buildings :") self.intelLayout = QGridLayout() self.buildingsLayout = QGridLayout() self.init_ui() def init_ui(self): self.mainLayout = QVBoxLayout() self.budget = QBudgetBox(self.game) self.budget.setGame(self.game) self.doLayout() if self.ground_object.dcs_identifier == "AA": self.mainLayout.addWidget(self.intelBox) else: self.mainLayout.addWidget(self.buildingBox) self.setLayout(self.mainLayout) def doLayout(self): self.intelBox = QGroupBox("Units :") self.intelLayout = QGridLayout() i = 0 for g in self.ground_object.groups: if not hasattr(g, "units_losts"): g.units_losts = [] for u in g.units: self.intelLayout.addWidget( QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) + "</b>"), i, 0) i = i + 1 for u in g.units_losts: utype = unit_type_of(u) if utype in PRICES: price = PRICES[utype] else: price = 6 self.intelLayout.addWidget( QLabel("<b>Unit #" + str(u.id) + " - " + str(u.type) + "</b> [DEAD]"), i, 0) if self.cp.captured: repair = QPushButton("Repair [" + str(price) + "M]") repair.setProperty("style", "btn-success") repair.clicked.connect( lambda u=u, g=g, p=price: self.repair_unit(g, u, p)) self.intelLayout.addWidget(repair, i, 1) i = i + 1 self.buildingBox = QGroupBox("Buildings :") self.buildingsLayout = QGridLayout() j = 0 for i, building in enumerate(self.buildings): if building.dcs_identifier not in FORTIFICATION_BUILDINGS: self.buildingsLayout.addWidget( QBuildingInfo(building, self.ground_object), j / 3, j % 3) j = j + 1 self.buildingBox.setLayout(self.buildingsLayout) self.intelBox.setLayout(self.intelLayout) def do_refresh_layout(self): try: for i in range(self.mainLayout.count()): self.mainLayout.removeItem(self.mainLayout.itemAt(i)) self.doLayout() if len(self.ground_object.groups) > 0: self.mainLayout.addWidget(self.intelBox) else: self.mainLayout.addWidget(self.buildingBox) except Exception as e: print(e) def repair_unit(self, group, unit, price): if self.game.budget > price: self.game.budget -= price group.units_losts = [ u for u in group.units_losts if u.id != unit.id ] group.units.append(unit) GameUpdateSignal.get_instance().updateGame(self.game) # Remove destroyed units in the vicinity destroyed_units = self.game.get_destroyed_units() for d in destroyed_units: p = Point(d["x"], d["z"]) if p.distance_to_point(unit.position) < 15: destroyed_units.remove(d) logging.info("Removed destroyed units " + str(d)) logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type)) self.do_refresh_layout() def closeEvent(self, closeEvent: QCloseEvent): GameUpdateSignal.get_instance().updateGame(self.game)
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