class LogicRadioButtonGroup(QGroupBox):
    chosen = Signal(str)

    def __init__(self, direction, default, title='', **kwarg):
        super().__init__(title)

        if direction == 'vertical':
            buttonLayout = QVBoxLayout()
        else:  # direction == 'horizontal'
            buttonLayout = QHBoxLayout()

        self.buttonGroup = QButtonGroup()
        self.setLayout(buttonLayout)

        for short_name, text in kwarg.items():
            button = QRadioButton(text)
            button.clicked.connect(self.selected)
            if short_name == default:
                button.setChecked(True)
            self.buttonGroup.addButton(button)
            buttonLayout.addWidget(button)

    def setToDefault(self, default_option):
        for option in self.buttonGroup.buttons():
            if option.text() == default_option:
                option.setChecked(True)
            else:
                option.setChecked(False)

    def value(self):
        checked = self.buttonGroup.checkedButton()
        return checked.text()

    def selected(self):
        self.chosen.emit(self.buttonGroup.checkedButton().text())
Ejemplo n.º 2
0
class TranscriptionsSearchOptionsDialog(QDialog):
    def __init__(self, blankOptionSelection=None, wildcard=None):
        super().__init__()

        self.blankOptionSelection = blankOptionSelection
        self.wildcard = wildcard

        self.setWindowTitle('Search Options')
        layout = QVBoxLayout()

        blankOptionsLabel = QLabel(
            'How should blank spaces be interpreted in your search?')
        layout.addWidget(blankOptionsLabel)

        blankOptionsLayout = QVBoxLayout()
        self.blankOptionsGroup = QButtonGroup()

        asBlankOption = QRadioButton(
            'Interpret as literal blanks, and only match blank slots')
        blankOptionsLayout.addWidget(asBlankOption)
        self.blankOptionsGroup.addButton(asBlankOption)
        self.blankOptionsGroup.setId(asBlankOption, 0)
        if self.blankOptionSelection == 'literal':
            asBlankOption.setChecked(True)

        asWildcardOption = QRadioButton(
            'Interpret as wildcards, and match anything')
        blankOptionsLayout.addWidget(asWildcardOption)
        self.blankOptionsGroup.addButton(asWildcardOption)
        self.blankOptionsGroup.setId(asWildcardOption, 1)
        if self.blankOptionSelection == 'wildcard' or self.blankOptionSelection is None:
            asWildcardOption.setChecked(True)

        miniLayout = QHBoxLayout()
        asBlankWithWildcard = QRadioButton(
            'Interpret as literal blanks, and use this character for wildcards: '
        )
        self.wildcardLineEdit = QLineEdit()
        self.wildcardLineEdit.setMaxLength(1)
        self.wildcardLineEdit.setMaximumWidth(30)
        self.blankOptionsGroup.addButton(asBlankWithWildcard)
        self.blankOptionsGroup.setId(asBlankWithWildcard, 2)
        if self.blankOptionSelection == 'both':
            asBlankWithWildcard.setChecked(True)
            self.wildcardLineEdit.setText(self.wildcard)
        miniLayout.addWidget(asBlankWithWildcard)
        miniLayout.addWidget(self.wildcardLineEdit)
        blankOptionsLayout.addLayout(miniLayout)

        layout.addLayout(blankOptionsLayout)

        buttonLayout = QHBoxLayout()
        ok = QPushButton('OK')
        ok.clicked.connect(self.accept)
        buttonLayout.addWidget(ok)
        cancel = QPushButton('Cancel')
        cancel.clicked.connect(self.reject)
        buttonLayout.addWidget(cancel)
        layout.addLayout(buttonLayout)

        self.setLayout(layout)

    def accept(self):
        selectedButton = self.blankOptionsGroup.checkedButton()
        id_ = self.blankOptionsGroup.id(selectedButton)
        if id_ == 0:
            self.blankOptionSelection = 'literal'
            self.wildcard = None
        elif id_ == 1:
            self.blankOptionSelection = 'wildcard'
            self.wildcard = '_'
        elif id_ == 2:
            self.blankOptionSelection = 'both'
            self.wildcard = self.wildcardLineEdit.text()
        super().accept()
Ejemplo n.º 3
0
class FingerOptionGroup(QGroupBox):
    fingerOptionDict_with_i = {
        'thumb': {'Extended': r'(?P<thumb>.(?P<thumb_opposition>[LU]).(?<thumb_mcp>[HEei]).).+.\u2205/.+.....',
                  'Not extended': r'(?P<thumb>.(?P<thumb_opposition_1>[^LU]).(?<thumb_mcp_1>[HEei]).|_(?P<thumb_opposition_2>[LU]).(?<thumb_mcp_2>[^HEei]).|_(?P<thumb_opposition_3>[^LU]).(?<thumb_mcp_3>[^HEei]).).+.\u2205/.+.....',
                  'Either': r'(?P<thumb>.(?P<thumb_opposition>.).(?<thumb_mcp>.).).+.\u2205/.+.....'},
        'index': {'Extended': r'(?P<index>1(?P<index_mcp>[HEei])..)',
                  'Not extended': r'(?P<index>1(?P<index_mcp>[^HEei])..)',
                  'Either': r'(?P<index>1(?P<index_mcp>.)..)'},
        'middle': {'Extended': r'(?P<middle>.+2(?P<middle_mcp>[HEei])..)',
                   'Not extended': r'(?P<middle>.+2(?P<middle_mcp>[^HEei])..)',
                   'Either': r'(?P<middle>.+2(?P<middle_mcp>.)..)'},
        'ring': {'Extended': r'(?P<ring>.+3(?P<ring_mcp>[HEei])..)',
                 'Not extended': r'(?P<ring>.+3(?P<ring_mcp>[^HEei])..)',
                 'Either': r'(?P<ring>.+3(?P<ring_mcp>.)..)'},
        'pinky': {'Extended': r'(?P<pinky>.+4(?P<pinky_mcp>[HEei])..)',
                  'Not extended': r'(?P<pinky>.+4(?P<pinky_mcp>[^HEei])..)',
                  'Either': r'(?P<pinky>.+4(?P<pinky_mcp>.)..)'}
    }

    fingerOptionDict_without_i = {
        'thumb': {'Extended': r'(?P<thumb>.(?P<thumb_opposition>[LU]).(?<thumb_mcp>[HEe]).).+.\u2205/.+.....',
                  'Not extended': r'(?P<thumb>.(?P<thumb_opposition_1>[^LU]).(?<thumb_mcp_1>[HEe]).|_(?P<thumb_opposition_2>[LU]).(?<thumb_mcp_2>[^HEe]).|_(?P<thumb_opposition_3>[^LU]).(?<thumb_mcp_3>[^HEe]).).+.\u2205/.+.....',
                  'Either': r'(?P<thumb>.(?P<thumb_opposition>.).(?<thumb_mcp>.).).+.\u2205/.+.....'},
        'index': {'Extended': r'(?P<index>1(?P<index_mcp>[HEe])..)',
                  'Not extended': r'(?P<index>1(?P<index_mcp>[^HEe])..)',
                  'Either': r'(?P<index>1(?P<index_mcp>.)..)'},
        'middle': {'Extended': r'(?P<middle>.+2(?P<middle_mcp>[HEe])..)',
                   'Not extended': r'(?P<middle>.+2(?P<middle_mcp>[^HEe])..)',
                   'Either': r'(?P<middle>.+2(?P<middle_mcp>.)..)'},
        'ring': {'Extended': r'(?P<ring>.+3(?P<ring_mcp>[HEe])..)',
                 'Not extended': r'(?P<ring>.+3(?P<ring_mcp>[^HEe])..)',
                 'Either': r'(?P<ring>.+3(?P<ring_mcp>.)..)'},
        'pinky': {'Extended': r'(?P<pinky>.+4(?P<pinky_mcp>[HEe])..)',
                  'Not extended': r'(?P<pinky>.+4(?P<pinky_mcp>[^HEe])..)',
                  'Either': r'(?P<pinky>.+4(?P<pinky_mcp>.)..)'}
    }

    def __init__(self, groupName):
        super().__init__(groupName)

        self.buttonGroup = QButtonGroup()
        self.extended = QRadioButton('Extended')
        self.notExtended = QRadioButton('Not extended')
        self.either = QRadioButton('Either')
        self.either.setChecked(True)

        self.buttonGroup.addButton(self.extended)
        self.buttonGroup.addButton(self.notExtended)
        self.buttonGroup.addButton(self.either)

        buttonLayout = QVBoxLayout()
        self.setLayout(buttonLayout)
        buttonLayout.addWidget(self.extended)
        buttonLayout.addWidget(self.notExtended)
        buttonLayout.addWidget(self.either)

    def setToDefault(self):
        self.extended.setChecked(False)
        self.notExtended.setChecked(False)
        self.either.setChecked(True)

    def getExtendedRegExp(self, includeI):
        finger = self.title().lower()
        if includeI:
            regExp = self.fingerOptionDict_with_i[finger]['Extended']
        else:
            regExp = self.fingerOptionDict_without_i[finger]['Extended']
        return regExp

    def getEitherRegExp(self, includeI):
        finger = self.title().lower()
        if includeI:
            regExp = self.fingerOptionDict_with_i[finger]['Either']
        else:
            regExp = self.fingerOptionDict_without_i[finger]['Either']
        return regExp

    def getNotExtendedRegExp(self, includeI):
        finger = self.title().lower()
        if includeI:
            regExp = self.fingerOptionDict_with_i[finger]['Not extended']
        else:
            regExp = self.fingerOptionDict_without_i[finger]['Not extended']
        return regExp

    def generateRegExp(self, includeI):
        finger = self.title()
        chosen = self.buttonGroup.checkedButton().text()

        if includeI:
            if finger == 'Thumb':
                regExp = self.fingerOptionDict_with_i['thumb'][chosen]
            elif finger == 'Index':
                regExp = self.fingerOptionDict_with_i['index'][chosen]
            elif finger == 'Middle':
                regExp = self.fingerOptionDict_with_i['middle'][chosen]
            elif finger == 'Ring':
                regExp = self.fingerOptionDict_with_i['ring'][chosen]
            else:
                regExp = self.fingerOptionDict_with_i['pinky'][chosen]
        else:
            if finger == 'Thumb':
                regExp = self.fingerOptionDict_without_i['thumb'][chosen]
            elif finger == 'Index':
                regExp = self.fingerOptionDict_without_i['index'][chosen]
            elif finger == 'Middle':
                regExp = self.fingerOptionDict_without_i['middle'][chosen]
            elif finger == 'Ring':
                regExp = self.fingerOptionDict_without_i['ring'][chosen]
            else:
                regExp = self.fingerOptionDict_without_i['pinky'][chosen]

        return regExp

    def value(self):
        checked = self.buttonGroup.checkedButton()
        return checked.text()
class FunctionalLoadDialog(QDialog):
    def __init__(self, corpus):
        super().__init__()
        self.corpus = corpus
        self.results = list()

        self.setWindowTitle('Functional Load')
        layout = QVBoxLayout()

        #Set up top row of radio button options
        contrastBox = QGroupBox('Contrast')
        contrastLayout = QHBoxLayout()
        self.contrastGroup = QButtonGroup()
        flexionOption = QRadioButton('Degrees of flexion')
        flexionOption.click()
        ductionOption = QRadioButton('Degree of duction')
        oppositionOption = QRadioButton('Thumb opposition')
        contactOption = QRadioButton('Thumb/finger contact')
        customOption = QRadioButton('Custom options')
        self.contrastGroup.addButton(flexionOption, id=0)
        self.contrastGroup.addButton(ductionOption, id=1)
        self.contrastGroup.addButton(oppositionOption, id=2)
        self.contrastGroup.addButton(contactOption, id=3)
        self.contrastGroup.addButton(customOption, id=4)
        contrastLayout.addWidget(flexionOption)
        contrastLayout.addWidget(ductionOption)
        contrastLayout.addWidget(oppositionOption)
        contrastLayout.addWidget(contactOption)
        contrastLayout.addWidget(customOption)
        contrastBox.setLayout(contrastLayout)

        #set up stacked widgets
        self.middleWidget = QStackedWidget()

        #Collapse degress of flexion
        flexionWidget = QWidget()
        flexionLayout = QHBoxLayout()
        self.flexionFingerSelection = QComboBox()
        self.flexionFingerSelection.addItems(
            ['Thumb', 'Index', 'Middle', 'Pinky', 'Ring', 'All'])
        self.flexionJointSelection = QComboBox()
        self.flexionJointSelection.addItems(
            ['Proximal', 'Medial', 'Distal', 'All'])
        #note: Thumb+Proximal not possible, and there's an alert window that will pop up if this combination is chosen
        flexionLayout.addWidget(self.flexionFingerSelection)
        flexionLayout.addWidget(self.flexionJointSelection)
        flexionWidget.setLayout(flexionLayout)

        #Collapse degrees of duction
        ductionWidget = QWidget()
        ductionLayout = QHBoxLayout()
        self.ductionFingerSelection = QComboBox()
        self.ductionFingerSelection.addItems([
            'Thumb/Finger', 'Index/Middle', 'Middle/Ring', 'Ring/Pinky', 'All'
        ])
        ductionLayout.addWidget(self.ductionFingerSelection)
        ductionWidget.setLayout(ductionLayout)

        #Collapse thumb opposition
        oppositionWidget = QWidget()
        oppositionLayout = QHBoxLayout()
        oppositionWidget.setLayout(oppositionLayout)

        #Collapse thumb/finger contact
        contactWidget = QWidget()
        contactLayout = QHBoxLayout()
        contactWidget.setLayout(contactLayout)

        #Collapse custom slots
        customWidget = QWidget()
        customLayout = QHBoxLayout()
        customLayout.addWidget(QLabel('Merge this symbol: '))
        self.customSymbo1A = QComboBox()
        self.customSymbo1A.addItem('')
        self.customSymbo1A.addItems(STANDARD_SYMBOLS)
        self.customSymbo1A.setEditable(True)
        customLayout.addWidget(self.customSymbo1A)
        customLayout.addWidget(QLabel('with this symbol: '))
        self.customSymbolB = QComboBox()
        self.customSymbolB.addItem('')
        self.customSymbolB.addItems(STANDARD_SYMBOLS)
        self.customSymbolB.setEditable(True)
        customLayout.addWidget(self.customSymbolB)
        customLayout.addWidget(QLabel('in these slots: '))
        self.customSlots = QLineEdit()
        customLayout.addWidget(self.customSlots)
        customLayout.addWidget(
            QLabel(
                '(separate numbers with commas, leave blank to merge symbols everywhere)'
            ))
        customWidget.setLayout(customLayout)

        #Build up middle widget
        self.middleWidget.addWidget(flexionWidget)
        self.middleWidget.addWidget(ductionWidget)
        self.middleWidget.addWidget(oppositionWidget)
        self.middleWidget.addWidget(contactWidget)
        self.middleWidget.addWidget(customWidget)

        #Connect slots and signals
        flexionOption.clicked.connect(self.changeMiddleWidget)
        ductionOption.clicked.connect(self.changeMiddleWidget)
        oppositionOption.clicked.connect(self.changeMiddleWidget)
        contactOption.clicked.connect(self.changeMiddleWidget)
        customOption.clicked.connect(self.changeMiddleWidget)

        #Bottom buttons (OK/Cancel)
        buttonLayout = QHBoxLayout()
        ok = QPushButton('OK')
        ok.clicked.connect(self.accept)
        cancel = QPushButton('Cancel')
        cancel.clicked.connect(self.reject)
        buttonLayout.addWidget(ok)
        buttonLayout.addWidget(cancel)

        layout.addWidget(contrastBox)
        layout.addWidget(self.middleWidget)
        layout.addLayout(buttonLayout)

        self.setLayout(layout)

    def changeMiddleWidget(self, e):
        self.middleWidget.setCurrentIndex(self.contrastGroup.id(self.sender()))

    def accept(self):
        index = self.middleWidget.currentIndex()
        if index == 0:
            if (self.flexionFingerSelection.currentText() == 'Thumb' and
                    self.flexionJointSelection.currentText() == 'Proximal'):
                alert = QMessageBox()
                alert.setWindowTitle('Incompatible Options')
                alert.setText(
                    'Thumbs cannot be selected for proximal joint. Choose either "Medial" or "Distal"'
                )
                alert.exec_()
                return
            self.calcByFlexion()
        elif index == 1:
            self.calcByDuction()

        elif index == 4:
            slots = self.customSlots.text()
            alert = QMessageBox()
            alert.setWindowTitle('Invalid slot numbers')
            alert.setText('Slot numbers must be between 1 and 34 (inclusive)')

            try:
                slots = [int(x.strip()) for x in slots.split(',')]
            except ValueError:
                alert.exec_()
                return

            if any(n > 34 or n < 1 for n in slots):
                alert.exec_()
                return
            self.calcCustom(slots)

        super().accept()

    def calculateEntropy(self, corpus=None):
        corpus_size = len(corpus) if corpus is not None else len(self.corpus)
        return corpus_size, sum([
            1 / corpus_size * log(1 / corpus_size) for n in range(corpus_size)
        ]) * -1

    def calcByDuction(self):
        corpus_size, starting_h = self.calculateEntropy()
        duction = self.ductionFingerSelection.currentText()
        if duction == 'Thumb/Finger':
            slot = 3
        elif duction == 'Index/Middle':
            slot = 19
        elif duction == 'Middle/Ring':
            slot = 24
        elif duction == 'Ring/Pinky':
            slot = 29
        elif duction == 'All':
            slot = -1

        if slot > 1:
            print('{} DUCTION'.format(duction.upper()))
            print('Starting size = {}\nStarting entropy = {}'.format(
                corpus_size, starting_h))
            new_corpus = defaultdict(int)
            for word in self.corpus:
                ch = word.config1hand1.copy()
                ch[slot] = 'X'
                new_corpus[''.join(ch)] += 1

            new_corpus_size, ending_h = self.calculateEntropy(new_corpus)
            print('After merging size = {}\nAfter merging entropy = {}'.format(
                len(new_corpus), ending_h))
            print('Change in entropy = {}\n'.format(starting_h - ending_h))
        else:
            print('{} DUCTION'.format(duction.upper()))
            print('Starting size = {}\nStarting entropy = {}'.format(
                corpus_size, starting_h))
            new_corpus = defaultdict(int)
            for word in self.corpus:
                ch = word.config1hand1.copy()
                ch[2] = 'X'
                ch[19] = 'X'
                ch[24] = 'X'
                ch[29] = 'X'
                new_corpus[''.join(ch)] += 1
            new_corpus_size, ending_h = self.calculateEntropy(new_corpus)
            print('After merging size = {}\nAfter merging entropy = {}'.format(
                len(new_corpus), ending_h))
            print('Change in entropy = {}\n'.format(starting_h - ending_h))

        result = [
            corpus_size, starting_h, new_corpus_size, ending_h,
            starting_h - ending_h
        ]
        self.results = [result]

    def calcCustom(self, slots):
        corpus_size, starting_h = self.calculateEntropy()

        slots = [n - 1 for n in slots]
        # minus 1 because slot numbers starts at 1 but list indices start at 0

        symbolA = self.customSymbo1A.currentText()
        symbolB = self.customSymbolB.currentText()

        print('Merging {} and {}'.format(symbolA, symbolB))
        print('Starting size = {}\nStarting entropy = {}'.format(
            corpus_size, starting_h))
        new_corpus = defaultdict(int)
        for word in self.corpus:
            ch = word.config1hand1.copy()
            for slot in slots:
                if ch[slot] in [symbolA, symbolB]:
                    ch[slot] = 'X'

            new_corpus[''.join(ch)] += 1

        new_corpus_size, ending_h = self.calculateEntropy(new_corpus)
        print('After merging size = {}\nAfter merging entropy = {}'.format(
            len(new_corpus), ending_h))
        print('Change in entropy = {}\n'.format(starting_h - ending_h))
        result = [
            corpus_size, starting_h, new_corpus_size, ending_h,
            starting_h - ending_h
        ]
        self.results = [result]

    def calcByFlexion(self):
        corpus_size, starting_h = self.calculateEntropy()

        finger = self.flexionFingerSelection.currentText()
        joint = self.flexionJointSelection.currentText()

        jointDict = {'Proximal': 0, 'Medial': 1, 'Distal': 2, 'All': -1}

        fingerDict = {
            'Thumb': 2,
            'Index': 16,
            'Middle': 21,
            'Ring': 26,
            'Pinky': 31,
            'All': -1
        }

        offset = jointDict[joint]
        slot = fingerDict[finger]
        slot += offset

        if slot > 0:  #user chose particular fingers

            print('{} {} JOINTS'.format(finger.upper(), joint.upper()))
            print('Starting size = {}\nStarting entropy = {}'.format(
                corpus_size, starting_h))
            new_corpus = defaultdict(int)
            for word in self.corpus:
                ch = word.config1hand1.copy()
                ch[slot] = 'X'
                new_corpus[''.join(ch)] += 1

            new_corpus_size, ending_h = self.calculateEntropy(new_corpus)
            print('After merging size = {}\nAfter merging entropy = {}'.format(
                len(new_corpus), ending_h))
            print('Change in entropy = {}\n'.format(starting_h - ending_h))
            self.results = [[
                corpus_size, starting_h, new_corpus_size, ending_h,
                starting_h - ending_h
            ]]

        else:  #user chose an "All" option

            if joint == 'All' and finger != 'All':
                #all the joints on a particular finger

                slot = fingerDict[finger]

                print('ALL {} JOINTS'.format(finger.upper()))
                print('Starting size = {}\nStarting entropy = {}'.format(
                    corpus_size, starting_h))
                new_corpus = defaultdict(int)
                for word in self.corpus:
                    ch = word.config1hand1.copy()
                    ch[slot] = 'X'  #proximal
                    ch[slot + 1] = 'X'  #medial
                    if not finger == 'Thumb':
                        ch[slot + 2] = 'X'  #distal
                    new_corpus[''.join(ch)] += 1

                new_corpus_size, ending_h = self.calculateEntropy(new_corpus)
                print('After merging size = {}\nAfter merging entropy = {}'.
                      format(len(new_corpus), ending_h))
                print('Change in entropy = {}\n'.format(starting_h - ending_h))
                self.results = [[
                    corpus_size, starting_h, new_corpus_size, ending_h,
                    starting_h - ending_h
                ]]

            elif finger == 'All' and joint != 'All':
                #a particular joint on all the fingers

                if joint == 'Proximal':
                    slot = 17
                elif joint == 'Medial':
                    slot = 18
                elif joint == 'Distal':
                    slot = 19

                print('ALL {} JOINTS'.format(joint.upper()))
                print('Starting size = {}\nStarting entropy = {}'.format(
                    corpus_size, starting_h))
                # for finger,slot in [('INDEX', 17), ('MIDDLE',22), ('RING',27), ('PINKY',32)]:
                new_corpus = defaultdict(int)
                for word in self.corpus:
                    ch = word.config1hand1.copy()
                    ch[slot] = 'X'
                    ch[slot + 5] = 'X'
                    ch[slot + 10] = 'X'
                    ch[slot + 15] = 'X'
                    new_corpus[''.join(ch)] += 1

                    new_corpus_size, ending_h = self.calculateEntropy(
                        new_corpus)
                print('After merging size = {}\nAfter merging entropy = {}'.
                      format(len(new_corpus), ending_h))
                print('Change in entropy = {}\n'.format(starting_h - ending_h))
                self.results = [[
                    corpus_size, starting_h, new_corpus_size, ending_h,
                    starting_h - ending_h
                ]]

            elif finger == 'All' and joint == 'All':
                results = list()
                for finger, slot in [('THUMB', 2), ('INDEX', 17),
                                     ('MIDDLE', 22), ('RING', 27),
                                     ('PINKY', 31)]:
                    print('ALL {} JOINTS'.format(joint.upper()))
                    print('Starting size = {}\nStarting entropy = {}'.format(
                        corpus_size, starting_h))
                    new_corpus = defaultdict(int)
                    for word in self.corpus:
                        ch = word.config1hand1.copy()
                        ch[slot] = 'X'
                        ch[slot + 1] = 'X'
                        if not finger == 'Thumb':
                            ch[slot + 2] = 'X'
                        new_corpus[''.join(ch)] += 1

                    new_corpus_size, ending_h = self.calculateEntropy(
                        new_corpus)
                    print(
                        'After merging size = {}\nAfter merging entropy = {}'.
                        format(len(new_corpus), ending_h))
                    print('Change in entropy = {}\n'.format(starting_h -
                                                            ending_h))
                    results.append([
                        corpus_size, starting_h, new_corpus_size, ending_h,
                        starting_h - ending_h
                    ])
                self.results = results