class MorseChallenger(QMainWindow):
    
    def __init__(self):
        super(MorseChallenger,self).__init__();
        
        self.PIXELS_TO_MOVE_PER_TIMEOUT = 3
        self.ABSOLUTE_MAX_NUM_FLOATERS  = 10;
        self.FLOATER_POINT_SIZE = 20;
        self.LETTER_CHECKBOX_NUM_COLS = 6;
        # Number of timeout cycles that detonated
        # letters stay visible:
        self.DETONATION_VISIBLE_CYCLES = 4;

        self.maxNumFloaters = 1;        
        self.lettersToUse = set();
        self.floatersAvailable = set();
        self.floatersInUse = set();
        self.bookeepingAccessLock = threading.Lock();
        # Floaters that detonated. Values are number of timeouts
        # the detonation was visible:
        self.floatersDetonated = {};
        # Used to launch new floaters at random times:
        self.cyclesSinceLastLaunch = 0;
        
        self.dialogService = DialogService();        
        self.optionsFilePath = os.path.join(os.getenv('HOME'), '.morser/morseChallenger.cfg');

        # Load UI for Morse Challenger:
        relPathQtCreatorFileMainWin = "qt_files/morseChallenger/morseChallenger.ui";
        qtCreatorXMLFilePath = self.findFile(relPathQtCreatorFileMainWin);
        if qtCreatorXMLFilePath is None:
            raise ValueError("Can't find QtCreator user interface file %s" % relPathQtCreatorFileMainWin);
        # Make QtCreator generated UI a child of this instance:
        loadUi(qtCreatorXMLFilePath, self);
        self.windowTitle = "Morse Challenger";
        self.setWindowTitle(self.windowTitle);

        self.iconDir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'icons')
        self.explosionPixMap = QPixmap(os.path.join(self.iconDir, 'explosion.png'));        

        CommChannel.registerSignals(MorseChallengerSignals);

        self.connectWidgets();
        
        self.generateLetterCheckBoxes();
        self.simultaneousLettersComboBox.addItems(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
        self.generateFloaters();
        
        self.setFocusPolicy(Qt.ClickFocus);
        
        self.letterMoveTimer = QTimer();
        self.letterMoveTimer.setInterval(self.timerIntervalFromSpeedSlider()); # milliseconds
        self.letterMoveTimer.setSingleShot(False);
        self.letterMoveTimer.timeout.connect(self.moveLetters);
        
        # Bring options from config file:
        self.setOptions();
        
        # Styling:
        self.createColors();
        self.setStyleSheet("QWidget{background-color: %s}" % self.lightBlueColor.name());

        self.show();
        
    def connectWidgets(self):
        self.speedHSlider.valueChanged.connect(self.speedChangedAction);
        self.startPushButton.clicked.connect(self.startAction);
        self.stopPushButton.clicked.connect(self.stopAction);
        self.simultaneousLettersComboBox.currentIndexChanged.connect(self.maxNumSimultaneousFloatersAction);
        
        CommChannel.getSignal('MorseChallengerSignals.focusLostInadvertently').connect(self.reassertWindowFocus);
        
    def createColors(self):
        self.grayBlueColor = QColor(89,120,137);  # Letter buttons
        self.lightBlueColor = QColor(206,230,243); # Background
        self.darkGray      = QColor(65,88,101);   # Central buttons
        self.wordListFontColor = QColor(62,143,185); # Darkish blue.
        self.purple        = QColor(147,124,195); # Gesture button pressed
        
    def generateLetterCheckBoxes(self):
        self.lettersToCheckBoxes = {};
        self.checkBoxesToLetters = {};
        letters = sorted(codeKey.values());
        numRows = (len(letters) % self.LETTER_CHECKBOX_NUM_COLS) + 1;
        col = 0;
        row = 0;
        letterIndex = 0;
        while(True):
            letter = letters[letterIndex];
            checkbox = QCheckBox();
            self.lettersToCheckBoxes[letter] = checkbox;
            self.checkBoxesToLetters[checkbox] = letter;
            checkbox.setText(str(letter));
            checkbox.toggled.connect(partial(self.letterCheckAction, checkbox));
            self.letterCheckboxGridLayout.addWidget(checkbox, row, col);
            col += 1;
            if (col >= self.LETTER_CHECKBOX_NUM_COLS):
                col = 0;
                row += 1;
            letterIndex += 1;
            if letterIndex >= len(letters):
                break;
        
    def generateFloaters(self):
        for i in range(self.ABSOLUTE_MAX_NUM_FLOATERS):
            floaterLabel = QLabel();
            font = QtGui.QFont()
            font.setFamily("Courier")
            font.setFixedPitch(True)
            font.setPointSize(self.FLOATER_POINT_SIZE);
            floaterLabel.setFont(font);

            self.floatersAvailable.add(floaterLabel);
            
    def letterCheckAction(self, checkbox, checkedOrNot):
        
        changedLetter = self.checkBoxesToLetters[checkbox];
        configedLetters = self.cfgParser.get('Main', 'letters');
        if checkedOrNot:
            self.lettersToUse.add(self.checkBoxesToLetters[checkbox]);
            configedLetters += changedLetter;
        else:
            self.lettersToUse.discard(self.checkBoxesToLetters[checkbox]);
            configedLetters.replace(changedLetter, "");
            
        self.cfgParser.set('Main', 'letters', configedLetters);
        self.saveOptions();
        
    def startAction(self):
        self.launchFloaters(1);
        self.letterMoveTimer.start();
        
    def stopAction(self):
        self.letterMoveTimer.stop();

        floatersInUseCopy = self.floatersInUse.copy();
        for floater in floatersInUseCopy:
            self.decommissionFloater(floater);
            
    def maxNumSimultaneousFloatersAction(self, newNum):
        # New Combo box picked: 0-based:
        self.maxNumFloaters = newNum + 1;
        try:
            self.cfgParser.set('Main','numLettersTogether', str(newNum));
            self.saveOptions();
        except AttributeError:
            # At startup cfgParser won't exist yet. Ignore:
            pass
        
    def speedChangedAction(self, newSpeed):
        self.letterMoveTimer.setInterval(self.timerIntervalFromSpeedSlider(newSpeed)); # msec.
        self.cfgParser.set('Main','letterSpeed', str(newSpeed));
        self.saveOptions();
        
    def keyPressEvent(self, keyEvent):
        letter = keyEvent.text();
        self.letterLineEdit.setText(letter);
        matchingFloaters = {};
        # Find all active floaters that have the pressed key's letter:
        for floater in self.floatersInUse: 
            if floater.text() == letter:
                floaterY = floater.y();
                try:
                    matchingFloaters[floaterY].append(floater);
                except KeyError:
                    matchingFloaters[floaterY] = [floater];
        if len(matchingFloaters) == 0:
            self.activateWindow();
            self.setFocus();
            return;
        # Find the lowest ones:
        lowestY = self.projectionScreenWidget.y();
        yPositions = matchingFloaters.keys();
        for floater in matchingFloaters[max(yPositions)]:
            self.decommissionFloater(floater);
        self.activateWindow();
        self.setFocus();

            
    
    def focusInEvent(self, event):
        '''
        Ensure that floaters are always on top of the app window,
        even if user changes speed slider, checkmarks, etc.:
        @param event:
        @type event:
        '''
        self.raiseAllFloaters();

    def resizeEvent(self, event):
        newWinRect = self.geometry();
        self.cfgParser.set('Appearance', 
                           'winGeometry', 
                           str(newWinRect.x()) 	+ ',' +
                           str(newWinRect.y()) 	+ ',' +
                           str(newWinRect.width()) + ',' +
                           str(newWinRect.height()));
        self.saveOptions();
        
    def raiseAllFloaters(self):
        for floater in self.floatersInUse:
            floater.raise_();
        for floater in self.floatersDetonated.keys():
            floater.raise_();
        
    def decommissionFloater(self, floaterLabel):
        floaterLabel.setHidden(True);
        # Just in case: protect removal, in case caller 
        # passes in an already decommissioned floater:
        try:
            self.floatersInUse.remove(floaterLabel);
        except KeyError:
            pass
        # Adding a floater twice is not an issue,
        # b/c floatersAvailable is a set:
        self.floatersAvailable.add(floaterLabel);
    
    def timerIntervalFromSpeedSlider(self, newSpeed=None):
        if newSpeed is None:
            newSpeed = self.speedHSlider.value();
        return max(500 - newSpeed, 20); #msec
            
    def randomLetters(self, numLetters):
        if len(self.lettersToUse) == 0:
            self.dialogService.showErrorMsg("You must turn on a checkmark for at least one letter.");
            return None;
        lettersToDeploy = [];
        for i in range(numLetters):
            letter = random.sample(self.lettersToUse, 1);
            lettersToDeploy.extend(letter);
        return lettersToDeploy;
    
    def moveLetters(self):
        self.cyclesSinceLastLaunch += 1;
        # Did floater detonate during previous timeout?:
        detonatedFloaters = self.floatersDetonated.keys();
        for detonatedFloater in detonatedFloaters:
            self.floatersDetonated[detonatedFloater] += 1;
            if self.floatersDetonated[detonatedFloater] > self.DETONATION_VISIBLE_CYCLES:
                self.decommissionFloater(detonatedFloater);
                del self.floatersDetonated[detonatedFloater];
        
        remainingFloaters = self.floatersDetonated.keys();
        for floaterLabel in self.floatersInUse:
            if floaterLabel in remainingFloaters:
                continue;
            geo = floaterLabel.geometry();
            newY = geo.y() + self.PIXELS_TO_MOVE_PER_TIMEOUT;
            savedHeight = geo.height();
            if newY > self.height():
                newY = self.height(); #********
                self.detonate(floaterLabel);
            geo.setY(newY)
            geo.setHeight(savedHeight);
            floaterLabel.setGeometry(geo);
            
        # Done advancing each floater. Is it time to start a new floater?
        numFloatersToLaunch = self.maxNumFloaters - len(self.floatersInUse) + len(self.floatersDetonated);   
        if numFloatersToLaunch > 0:
             # Use the following commented lines if you want the launching of
             # new floaters to be random, e.g. between 2 and 10 timeout intervals:
#            if self.cyclesSinceLastLaunch > random.randint(2,10):
#                self.launchFloaters(self.maxNumFloaters - len(self.floatersInUse));
             self.launchFloaters(numFloatersToLaunch);
        # Launching floaters deactivates the main window, thereby losing the keyboard focus.
        # We can't seem to reassert that focus within this timer interrupt service routine.
        # So, issue a signal that will do it after return:
        CommChannel.getSignal('MorseChallengerSignals.focusLostInadvertently').emit();
                
    def launchFloaters(self, numFloaters):
        newLetters = self.randomLetters(numFloaters);
        if newLetters is None:
            return;
        for letter in newLetters:
            # Pick a random horizontal location, at least 3 pixels in
            # from the left edge, and at most 3 pixels back from the right edge:
            screenGeo = self.projectionScreenWidget.geometry();
            appWinGeo = self.geometry();
            appWinX   = appWinGeo.x();
            # Random number among all the application window's X coordinates:
            xLoc = random.randint(appWinX + 3, appWinX + screenGeo.width() - 3);
            yLoc = appWinGeo.y();
            self.getFloaterLabel(letter, xLoc, yLoc);
        self.cyclesSinceLastLaunch = 0;

    def getFloaterLabel(self, letter, x, y):
        try:
            label = self.floatersAvailable.pop();
            label.clear();
        except KeyError:
            return None;
        self.floatersInUse.add(label);
        label.setText(letter);
        label.move(x,y);
        label.setHidden(False);
    
    def detonate(self, floaterLabel):
        # Floater was detonated zero cycles ago:
        self.floatersDetonated[floaterLabel] = 0;
        floaterLabel.clear();
        floaterLabel.setPixmap(self.explosionPixMap);

    def findFile(self, path, matchFunc=os.path.isfile):
        if path is None:
            return None
        for dirname in sys.path:
            candidate = os.path.join(dirname, path)
            if matchFunc(candidate):
                return candidate
        return None;

    def reassertWindowFocus(self):
        self.window().activateWindow();
        self.raise_();
        self.raiseAllFloaters();

    def setOptions(self):
        
        self.optionsDefaultDict = {
                    'letters'             : "",
                    'letterSpeed'         : str(10),
                    'numLettersTogether'  : str(0),
                    'winGeometry'         : '100,100,700,560',
                    }

        self.cfgParser = ConfigParser.SafeConfigParser(self.optionsDefaultDict);
        self.cfgParser.add_section('Main');
        self.cfgParser.add_section('Appearance');
        self.cfgParser.read(self.optionsFilePath);
        
        mainWinGeometry = self.cfgParser.get('Appearance', 'winGeometry');
        # Get four ints from the comma-separated string of upperLeftX, upperLeftY,
        # Width,Height numbers:
        try:
            nums = mainWinGeometry.split(',');
            self.setGeometry(QRect(int(nums[0].strip()),int(nums[1].strip()),int(nums[2].strip()),int(nums[3].strip())));
        except Exception as e:
            self.dialogService.showErrorMsg("Could not set window size; config file spec not grammatical: %s. (%s" % (mainWinGeometry, `e`));
        
        letterSpeed = self.cfgParser.getint('Main', 'letterSpeed');
        self.speedHSlider.setValue(letterSpeed);
        #self.speedChangedAction(letterSpeed);
        self.letterMoveTimer.setInterval(self.timerIntervalFromSpeedSlider()); # milliseconds
        
        numLettersTogether = self.cfgParser.getint('Main', 'numLettersTogether');
        self.simultaneousLettersComboBox.setCurrentIndex(numLettersTogether);
        
        lettersToUse = self.cfgParser.get('Main', 'letters');
        for letter in lettersToUse:
            self.lettersToCheckBoxes[letter].setChecked(True);
  
    def saveOptions(self):
        try:
            # Does the config dir already exist? If not
            # create it:
            optionsDir = os.path.dirname(self.optionsFilePath);
            if not os.path.isdir(optionsDir):
                os.makedirs(optionsDir, 0777);
            with open(self.optionsFilePath, 'wb') as outFd:
                self.cfgParser.write(outFd);
        except IOError as e:
            self.dialogService.showErrorMsg("Could not save options: %s" % `e`);

        
    def exit(self):
        QApplication.quit();

    def closeEvent(self, event):
        QApplication.quit();
        # Bubble event up:
        event.ignore();
class Foo(QObject):
    current_values_changed = Signal(str)
    current_duration_changed = Signal(float)
    _update_current_value_signal = Signal()
    _show_input_notification_signal = Signal()
    _hide_input_notification_signal = Signal()
    _show_scene_notification_signal = Signal()
    _hide_scene_notification_signal = Signal()
    _update_color_of_line_edit_signal = Signal()

    def __init__(self):
        super(Foo, self).__init__()
        self._action_set = None
        self._update_current_value_signal.connect(self._update_current_value)

        self._scene_notification = QDialog(main_window)
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                               '..', 'src', 'wrong_scene_dialog.ui')
        loadUi(ui_file, self._scene_notification)
        self._scene_notification_timer = QTimer(main_window)
        self._scene_notification_timer.setInterval(5000)
        self._scene_notification_timer.setSingleShot(True)
        self._scene_notification_timer.timeout.connect(
            self._hide_scene_notification)
        self._scene_notification.finished.connect(
            self._hide_scene_notification)

        self._input_notification = QDialog(main_window)
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                               '..', 'src', 'wrong_input_method_dialog.ui')
        loadUi(ui_file, self._input_notification)
        self._input_notification_timer = QTimer(main_window)
        self._input_notification_timer.setInterval(5000)
        self._input_notification_timer.setSingleShot(True)
        self._input_notification_timer.timeout.connect(
            self._hide_input_method_notification)
        self._input_notification.finished.connect(
            self._hide_input_method_notification)

        self._show_input_notification_signal.connect(
            self._show_input_notification)
        self._hide_input_notification_signal.connect(
            self._hide_input_method_notification)
        self._show_input_notification_signal.connect(
            self._show_scene_notification)
        self._hide_input_notification_signal.connect(
            self._hide_scene_notification)
        self._update_color_of_line_edit_signal.connect(
            self._update_color_of_line_edit)

    def _hide_scene_notification(self, result=None):
        self._scene_notification_timer.stop()
        self._scene_notification.hide()

    def _hide_input_method_notification(self, result=None):
        self._input_notification_timer.stop()
        self._input_notification.hide()

    # this method is call by ROS, all UI interaction is delayed via signals
    def update_current_value(self):
        global check_collisions
        global currently_in_collision

        if self._action_set is not None:
            self._action_set.stop()
        self._action_set = kontrol_subscriber.get_action_set()
        if self._action_set is not None:
            self.current_duration_changed.emit(self._action_set.get_duration())

        if not is_in_slider_mode():
            currently_in_collision = False
            self._show_input_notification_signal.emit()
            return
        if self._input_notification_timer.isActive():
            self._hide_input_notification_signal.emit()

        #print('update_current_value()')
        if not kontrol_subscriber.is_valid_action_set():
            self._show_scene_notification_signal.emit()
            return
        if self._scene_notification_timer.isActive():
            self._hide_scene_notification_signal.emit()

        joint_values = kontrol_subscriber.get_joint_values()
        if check_collisions:
            in_collision = collision_checker.is_in_collision(joint_values)
        else:
            in_collision = False

        collision_toggled = (currently_in_collision != in_collision)
        currently_in_collision = in_collision

        if collision_toggled:
            self._update_color_of_line_edit_signal.emit()

        self._update_current_value_signal.emit()

    def _show_input_notification(self):
        # open dialog which closes after some s_hide_scene_notification_hide_scene_notificationeconds or when confirmed manually
        self._input_notification.show()
        self._input_notification_timer.start()

    def _show_scene_notification(self):
        # open dialog which closes after some seconds or when confirmed manually
        self._scene_notification.show()
        self._scene_notification_timer.start()

    def _update_color_of_line_edit(self):
        global currently_in_collision
        palette = main_window.lineEdit.palette()
        if currently_in_collision:
            palette.setColor(QPalette.Text, QColor(255, 0, 0))
        else:
            palette.setColor(QPalette.Text, default_color)
        main_window.lineEdit.setPalette(palette)

    def _update_current_value(self):
        global currently_in_collision
        main_window.append_pushButton.setEnabled(not currently_in_collision)
        main_window.insert_before_pushButton.setEnabled(
            not currently_in_collision)

        value = self._action_set.to_string()
        self.current_values_changed.emit(value)
        for action in self._action_set._actions:
            action._duration = 0.1 if use_sim_time else 0.5
        self._action_set.execute()
Пример #3
0
class Foo(QObject):
    current_values_changed = Signal(str)
    current_duration_changed = Signal(float)
    _update_current_value_signal = Signal()
    _show_input_notification_signal = Signal()
    _hide_input_notification_signal = Signal()
    _show_scene_notification_signal = Signal()
    _hide_scene_notification_signal = Signal()
    _update_color_of_line_edit_signal = Signal()
    def __init__(self):
        super(Foo, self).__init__()
        self._action_set = None
        self._update_current_value_signal.connect(self._update_current_value)

        self._scene_notification = QDialog(main_window)
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src', 'wrong_scene_dialog.ui')
        loadUi(ui_file, self._scene_notification)
        self._scene_notification_timer = QTimer(main_window)
        self._scene_notification_timer.setInterval(5000)
        self._scene_notification_timer.setSingleShot(True)
        self._scene_notification_timer.timeout.connect(self._hide_scene_notification)
        self._scene_notification.finished.connect(self._hide_scene_notification)

        self._input_notification = QDialog(main_window)
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src', 'wrong_input_method_dialog.ui')
        loadUi(ui_file, self._input_notification)
        self._input_notification_timer = QTimer(main_window)
        self._input_notification_timer.setInterval(5000)
        self._input_notification_timer.setSingleShot(True)
        self._input_notification_timer.timeout.connect(self._hide_input_method_notification)
        self._input_notification.finished.connect(self._hide_input_method_notification)

        self._show_input_notification_signal.connect(self._show_input_notification)
        self._hide_input_notification_signal.connect(self._hide_input_method_notification)
        self._show_input_notification_signal.connect(self._show_scene_notification)
        self._hide_input_notification_signal.connect(self._hide_scene_notification)
        self._update_color_of_line_edit_signal.connect(self._update_color_of_line_edit)

    def _hide_scene_notification(self, result=None):
        self._scene_notification_timer.stop()
        self._scene_notification.hide()

    def _hide_input_method_notification(self, result=None):
        self._input_notification_timer.stop()
        self._input_notification.hide()

    # this method is call by ROS, all UI interaction is delayed via signals
    def update_current_value(self):
        global check_collisions
        global currently_in_collision

        if self._action_set is not None:
            self._action_set.stop()
        self._action_set = kontrol_subscriber.get_action_set()
        if self._action_set is not None:
            self.current_duration_changed.emit(self._action_set.get_duration())

        if not is_in_slider_mode():
            currently_in_collision = False
            self._show_input_notification_signal.emit()
            return
        if self._input_notification_timer.isActive():
            self._hide_input_notification_signal.emit()

        #print('update_current_value()')
        if not kontrol_subscriber.is_valid_action_set():
            self._show_scene_notification_signal.emit()
            return
        if self._scene_notification_timer.isActive():
            self._hide_scene_notification_signal.emit()

        joint_values = kontrol_subscriber.get_joint_values()
        if check_collisions:
            in_collision = collision_checker.is_in_collision(joint_values)
        else:
            in_collision = False

        collision_toggled = (currently_in_collision != in_collision)
        currently_in_collision = in_collision

        if collision_toggled:
            self._update_color_of_line_edit_signal.emit()

        self._update_current_value_signal.emit()

    def _show_input_notification(self):
        # open dialog which closes after some s_hide_scene_notification_hide_scene_notificationeconds or when confirmed manually
        self._input_notification.show()
        self._input_notification_timer.start()

    def _show_scene_notification(self):
        # open dialog which closes after some seconds or when confirmed manually
        self._scene_notification.show()
        self._scene_notification_timer.start()

    def _update_color_of_line_edit(self):
        global currently_in_collision
        palette = main_window.lineEdit.palette()
        if currently_in_collision:
            palette.setColor(QPalette.Text, QColor(255, 0, 0))
        else:
            palette.setColor(QPalette.Text, default_color)
        main_window.lineEdit.setPalette(palette)

    def _update_current_value(self):
        global currently_in_collision
        main_window.append_pushButton.setEnabled(not currently_in_collision)
        main_window.insert_before_pushButton.setEnabled(not currently_in_collision)

        value = self._action_set.to_string()
        self.current_values_changed.emit(value)
        for action in self._action_set._actions:
            action._duration = 0.1 if use_sim_time else 0.5
        self._action_set.execute()
        row = None
    execute_sequence(get_current_tab_index(), row)


main_window.run_sequence_from_selection_pushButton.clicked.connect(
    execute_current_sequence_from_selection)

select_row_signal = RelaySignalInt()
select_row_signal.relay_signal.connect(set_selected_row)

collision_notification = QDialog(main_window)
ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..',
                       'src', 'collision_dialog.ui')
loadUi(ui_file, collision_notification)
collision_notification_timer = QTimer(main_window)
collision_notification_timer.setInterval(3000)
collision_notification_timer.setSingleShot(True)


def hide_collision_notification(result=None):
    collision_notification_timer.stop()
    collision_notification.hide()


collision_notification_timer.timeout.connect(hide_collision_notification)
collision_notification.finished.connect(hide_collision_notification)


def check_buttons():
    triggered_buttons = kontrol_subscriber.get_triggered_buttons()
Пример #5
0
    row = get_selected_row()
    if row == get_row_count() - 1:
        row = None
    execute_sequence(get_current_tab_index(), row)

main_window.run_sequence_from_selection_pushButton.clicked.connect(execute_current_sequence_from_selection)


select_row_signal = RelaySignalInt()
select_row_signal.relay_signal.connect(set_selected_row)

collision_notification = QDialog(main_window)
ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'src', 'collision_dialog.ui')
loadUi(ui_file, collision_notification)
collision_notification_timer = QTimer(main_window)
collision_notification_timer.setInterval(3000)
collision_notification_timer.setSingleShot(True)
def hide_collision_notification(result=None):
    collision_notification_timer.stop()
    collision_notification.hide()
collision_notification_timer.timeout.connect(hide_collision_notification)
collision_notification.finished.connect(hide_collision_notification)

def check_buttons():
    triggered_buttons = kontrol_subscriber.get_triggered_buttons()

    if KontrolSubscriber.previous_button in triggered_buttons:
        if get_selected_row() is not None:
            select_row_signal.emit(get_selected_row() - 1)
        else:
            select_row_signal.emit(0)