예제 #1
0
파일: main.py 프로젝트: dushenda/proMDS
 def show_psg(self, target):
     """
     显示进度条窗口
     :param target:线程处理函数
     :return:None
     """
     # 进度条
     progress = QProgressDialog(parent=self.ui,
                                maximum=0,
                                minimum=0,
                                cancelButtonText="取消",
                                labelText="正在提取...",
                                flags=Qt.WindowFlags())
     progress.setWindowTitle("提取数据")
     run_psg_thd = Thread(target=target, args=(progress, ))
     run_psg_thd.start()
     progress.exec_()
     self.show_finish()
예제 #2
0
파일: store.py 프로젝트: whs/runekit
class AppStore(QObject):
    app_change = Signal()

    def __init__(self):
        super().__init__()
        self.settings = QSettings(self)

        qt_write_base = Path(
            QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation)
        )
        qt_write_base.mkdir(parents=True, exist_ok=True)

        self.icon_write_dir = qt_write_base / "app_icons"
        self.icon_write_dir.mkdir(exist_ok=True)

    def has_default_apps(self) -> bool:
        return self.settings.value("apps/_meta/isDefaultLoaded")

    def load_default_apps(self):
        def on_progress(value):
            if value != self.app_progress.maximum():
                return

            self.settings.setValue("apps/_meta/isDefaultLoaded", True)
            logger.info("Default apps loaded")
            self.add_app_thread.progress.disconnect(on_progress)
            del self.app_progress
            del self.add_app_thread

        self.app_progress = QProgressDialog("Loading default apps", "Cancel", 0, 100)

        self.add_app_thread = _FetchRegistryThread(self, apps_manifest=REGISTRY_URL)
        self.add_app_thread.progress.connect(self.app_progress.setValue)
        self.add_app_thread.progress.connect(on_progress)
        self.add_app_thread.items.connect(self.app_progress.setMaximum)
        self.add_app_thread.label.connect(self.app_progress.setLabelText)
        self.app_progress.canceled.connect(self.add_app_thread.cancel)
        self.add_app_thread.start()

        self.app_progress.show()

    def add_app_ui(self, manifests: List[str]):
        def on_progress(value):
            if value != self.app_progress.maximum():
                return

            self.add_app_thread.progress.disconnect(on_progress)
            del self.app_progress
            del self.add_app_thread

        def on_failed(exc_info):
            msg = QMessageBox(
                QMessageBox.Critical,
                "Fail to add application",
                f"Failed to add application: \n\n{exc_info[0].__name__}: {exc_info[1]}",
            )
            msg.setDetailedText("".join(traceback.format_exception(*exc_info)))
            msg.exec_()

        self.app_progress = QProgressDialog("Installing app", "Cancel", 0, 100)

        self.add_app_thread = _FetchRegistryThread(self, apps=manifests)
        self.add_app_thread.progress.connect(self.app_progress.setValue)
        self.add_app_thread.progress.connect(on_progress)
        self.add_app_thread.items.connect(self.app_progress.setMaximum)
        self.add_app_thread.label.connect(self.app_progress.setLabelText)
        self.add_app_thread.failed.connect(on_failed)
        self.app_progress.canceled.connect(self.add_app_thread.cancel)
        self.add_app_thread.start()

        self.app_progress.exec_()

    def add_app(self, manifest_url: str, manifest: AppManifest):
        appid = app_id(manifest_url)

        try:
            manifest["appUrl"] = urljoin(manifest_url, manifest["appUrl"])
            manifest["iconUrl"] = (
                urljoin(manifest_url, manifest["iconUrl"])
                if "iconUrl" in manifest and manifest["iconUrl"]
                else ""
            )
            manifest["configUrl"] = urljoin(manifest_url, manifest["configUrl"])
        except KeyError:
            raise AddAppError(manifest_url)

        if manifest["iconUrl"]:
            self.download_app_icon(appid, manifest["iconUrl"])

        self.settings.setValue(f"apps/{appid}", json.dumps(manifest))
        self.app_change.emit()
        logger.info(
            "Application %s (%s:%s) installed", manifest["appName"], appid, manifest_url
        )

    def mkdir(self, folder: str):
        assert folder
        assert "/" not in folder

        settings = QSettings()
        settings.beginGroup(f"apps/_folder/{folder}")
        settings.setValue("_dir", "true")
        self.app_change.emit()

    def add_app_to_folder(self, appid: str, folder: str = "", _emit=True):
        assert "/" not in folder
        settings = QSettings()
        settings.beginGroup(f"apps/_folder/{folder}")

        last_id = 0
        for key in settings.childKeys():
            if "_" in key:
                continue

            last_id = max(int(key), last_id)
            if appid == settings.value(key):
                settings.endGroup()
                return

        keys = [int(x) for x in settings.childKeys() if "_" not in x]
        last_id = max(keys) if keys else 0
        settings.endGroup()

        settings.setValue(f"apps/_folder/{folder}/{last_id + 1}", appid)

        if _emit:
            self.app_change.emit()

    def delete_app_from_folder(self, appid: str, folder: str):
        assert "/" not in folder
        settings = QSettings()
        settings.beginGroup(f"apps/_folder/{folder}")

        for key in settings.childKeys():
            if appid == settings.value(key):
                settings.remove(key)
                self.app_change.emit()
                return

    def remove_app(self, appid: str):
        settings = QSettings()
        settings.beginGroup("apps/_folder/")

        def recurse():
            for key in settings.childKeys():
                if settings.value(key) == appid:
                    settings.remove(key)

            for childGroup in settings.childGroups():
                settings.beginGroup(childGroup)
                recurse()
                settings.endGroup()

        recurse()

        self.settings.remove(f"apps/{appid}")

        icon_file = self.icon_write_dir / (appid + ".png")
        icon_file.unlink(True)

        self.app_change.emit()

    def rmdir(self, folder: str):
        assert folder
        assert "/" not in folder

        settings = QSettings()
        settings.beginGroup(f"apps/_folder/{folder}")

        for key in settings.childKeys():
            if "_" in key:
                continue

            try:
                int(key)
            except ValueError:
                continue

            self.add_app_to_folder(settings.value(key), "", _emit=False)

        settings.remove("")
        self.app_change.emit()

    def download_app_icon(self, appid: str, url: str):
        dest = self.icon_write_dir / (appid + ".png")
        req = requests.get(url)
        with dest.open("wb") as fp:
            fp.write(req.content)

        logger.info("App icon %s wrote to %s", appid, str(dest))

    def icon(self, appid: str) -> Optional[QIcon]:
        fn = QStandardPaths.locate(
            QStandardPaths.AppConfigLocation, "app_icons/" + appid + ".png"
        )
        if fn == "":
            return None

        return QIcon(QPixmap(fn))

    def all_apps(self) -> Iterator[Tuple[str, AppManifest]]:
        settings = QSettings()
        settings.beginGroup("apps")
        for appid in settings.childKeys():
            if appid.startswith("_"):
                continue

            manifest = json.loads(settings.value(appid))
            yield appid, manifest

        settings.endGroup()

    def list_app(self, root: str) -> Iterator[Tuple[str, Union[AppManifest, None]]]:
        settings = QSettings()

        settings.beginGroup(f"apps/_folder/" + root)
        for key in settings.childGroups():
            yield key, None
        for key in settings.childKeys():
            try:
                int(key)
            except ValueError:
                continue

            appid = settings.value(key)
            yield appid, self[appid]

    def __iter__(self):
        yield from self.all_apps()

    def __getitem__(self, item: str) -> AppManifest:
        assert not item.startswith("_")
        return json.loads(self.settings.value(f"apps/{item}"))
예제 #3
0
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
예제 #4
0
class Loader(QWidget):
    def __init__(self, parent=None):
        super(Loader, self).__init__(parent=parent)
        self.ui = Ui_Loader()
        self.ui.setupUi(self)
        self.dir = QDir(QDir.currentPath() + '/programs/')
        self.dir.setFilter(QDir.Files or QDir.NoDotAndDotDot)
        self.fs_watcher = QFileSystemWatcher(self.dir.path())
        self.fs_watcher.addPath(self.dir.path())
        self.fs_watcher.directoryChanged.connect(self.update_program_list)
        self.send_status = QProgressDialog
        self.sender = Sender
        self.serialpropertiesvalues = \
            {
                'baudrate': Serial.BAUDRATES,
                'parity': Serial.PARITIES,
                'databits': Serial.BYTESIZES,
                'stopbits': Serial.STOPBITS,
                'flowcontrol': ['NoControl', 'SoftwareControl', 'HardwareControl']
            }

        self.update_program_list()
        self.update_serial_port_list()
        # self.set_serial_port_options()

        self.ui.updateProgramListButton.clicked.connect(self.refresh)
        self.ui.programListWidget.itemSelectionChanged.connect(
            self.selection_changed)
        self.ui.sendButton.clicked.connect(self.send_program)
        self.ui.serialPortChooser.currentTextChanged.connect(
            self.selection_changed)
        self.ui.serialPortChooser.currentTextChanged.connect(save_port)
        # self.ui.baudRateInput.textChanged.connect(save_baud)
        # self.ui.parityChooser.currentTextChanged.connect(save_parity)
        # self.ui.dataBitsChooser.currentTextChanged.connect(save_databits)
        # self.ui.stopBitsChooser.currentTextChanged.connect(save_stopbits)
        # self.ui.flowControlChooser.currentTextChanged.connect(save_flowcontrol)
        self.thread_pool = QThreadPool()

    def set_serial_port_options(self):
        for key in parities.keys():
            self.ui.parityChooser.addItem(key)
        for key in bytesize.keys():
            self.ui.dataBitsChooser.addItem(key)
        for key in stopbits.keys():
            self.ui.stopBitsChooser.addItem(key)
        self.ui.flowControlChooser.addItems(flowcontrol)
        if globalSettings.contains('serialport/port'):
            self.selectpreviousvalues()
        else:
            self.saveconfig()

    def selectpreviousvalues(self):
        self.ui.serialPortChooser.setCurrentText(
            globalSettings.value('serialport/port'))
        self.ui.baudRateInput.setText(
            globalSettings.value('serialport/baudrate'))
        self.ui.parityChooser.setCurrentText(
            globalSettings.value('serialport/parity'))
        self.ui.dataBitsChooser.setCurrentText(
            globalSettings.value('serialport/databits'))
        self.ui.stopBitsChooser.setCurrentText(
            globalSettings.value('serialport/stopbits'))
        self.ui.flowControlChooser.setCurrentText(
            globalSettings.value('serialport/flowcontrol'))

    def saveconfig(self):
        save_port(self.ui.serialPortChooser.currentText())
        save_baud(self.ui.baudRateInput.text())
        save_parity(self.ui.parityChooser.currentText())
        save_databits(self.ui.dataBitsChooser.currentText())
        save_stopbits(self.ui.stopBitsChooser.currentText())
        save_flowcontrol(self.ui.flowControlChooser.currentText())

    def update_serial_port_list(self):
        self.ui.serialPortChooser.clear()
        for port in list_ports.comports():
            self.ui.serialPortChooser.addItem(port.device)

    def update_program_list(self):
        self.ui.programListWidget.clear()
        self.dir.refresh()
        self.ui.programListWidget.addItems(self.dir.entryList())
        self.ui.programListWidget.clearSelection()

    def selection_changed(self):
        if self.ui.serialPortChooser.currentText() is not None \
                and self.ui.programListWidget.currentItem() is not None:
            self.ui.sendButton.setEnabled(True)
        else:
            self.ui.sendButton.setDisabled(True)

    def refresh(self):
        self.update_program_list()
        self.update_serial_port_list()

    def send_program(self):
        selections = self.ui.programListWidget.selectedItems()
        for selection in selections:
            filename = selection.text()
            filepath = self.dir.path() + '/' + filename
            port_chosen = self.ui.serialPortChooser.currentText()
            confirm = ConfirmSend(self)
            confirm.ui.dialogLabel.setText(f'Send program \'{filename}\'?')
            confirm.exec()
            if confirm.result() == QDialog.Accepted:
                self.send_status = QProgressDialog(self)
                self.sender = Sender(
                    port_chosen, filepath,
                    globalSettings.value('serialport/baudrate'),
                    globalSettings.value('serialport/databits'),
                    globalSettings.value('serialport/parity'),
                    globalSettings.value('serialport/stopbits'),
                    globalSettings.value('serialport/flowcontrol'), self)
                self.send_status.setMaximum(self.sender.file.size())
                self.send_status.canceled.connect(self.sender.cancel)
                self.sender.signals.update_status.connect(
                    self.send_status.setValue)
                self.thread_pool.start(self.sender)
                self.send_status.exec_()
                self.send_status.deleteLater()