Exemple #1
0
    def _setupGUI(self):
        self._class_buttons = {}
        self._class_shortcuts = {}
        self._label_editor = None

        # Label class buttons
        self._classbox = QGroupBox("Labels", self)
        self._classbox_layout = FloatingLayout()
        self._classbox.setLayout(self._classbox_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 0)
        self._layout.addStretch(1)
    def _setupGUI(self, _groupBoxName):
        self._widgets_dict = {}
        self._class_shortcuts = {}

        # Label class buttons
        qss = "QGroupBox::title {subcontrol-origin: margin; subcontrol-position: top left; padding: 0 3px; color : red; font-weight:bold; }"
        self._inserterButtonGroupbox = QGroupBox(_groupBoxName, self)
        self._inserterButtonGroupbox.setStyleSheet(qss)
        self._inserterButtonGroup_layout = FloatingLayout()
        self._inserterButtonGroupbox.setLayout(
            self._inserterButtonGroup_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._inserterButtonGroupbox, 0)
        self._layout.addStretch(1)
Exemple #3
0
    def _setupGUI(self):
        self._class_buttons = {}
        self._class_shortcuts = {}
        self._label_editor  = None

        # Label class buttons
        self._classbox = QGroupBox("Labels", self)
        self._classbox_layout = FloatingLayout()
        self._classbox.setLayout(self._classbox_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 0)
        self._layout.addStretch(1)
    def __init__(self,
                 annotation_scene,
                 stateChangedSignalSlot=None,
                 buttonWithOptionsProperty=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)
        # print "buttonWithOptionsProperty = {}".format(comboPannelProperty)
        self.buttonWithOptionsProperty = buttonWithOptionsProperty

        self.name = displayName

        self.vlayout = QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)
        self.vlayout.addWidget(self.propertyEditorWidget)
        self.vlayout.addWidget(self.attrAreaWidget)
        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return
Exemple #5
0
class PropertyEditor(QWidget):
    # Signals
    insertionModeStarted = pyqtSignal(str)
    insertionModeEnded = pyqtSignal()
    insertionPropertiesChanged = pyqtSignal(object)
    editPropertiesChanged = pyqtSignal(object)

    def __init__(self, config, parent=None):
        QWidget.__init__(self, parent)
        self._class_config = {}
        self._class_items = {}
        self._class_prototypes = {}
        self._attribute_handlers = {}
        self._handler_factory = AttributeHandlerFactory()

        self._setupGUI()

        # Add label classes from config
        for label in config:
            self.addLabelClass(label)

    def onModelChanged(self, new_model):
        attrs = set([
            k for k, v in self._attribute_handlers.items()
            if v.autoAddEnabled()
        ])
        if len(attrs) > 0:
            start = time.time()
            attr2vals = {}
            for item in new_model.iterator(AnnotationModelItem):
                for attr in attrs:
                    if attr in item:
                        if attr not in attr2vals:
                            attr2vals[attr] = set((item[attr], ))
                        else:
                            attr2vals[attr] |= set((item[attr], ))
            diff = time.time() - start
            LOG.info("Extracted annotation values from model in %.2fs" % diff)
            for attr, vals in attr2vals.items():
                h = self._attribute_handlers[attr]
                for val in vals:
                    h.addValue(val, True)

    def addLabelClass(self, label_config):
        # Check label configuration
        if 'attributes' not in label_config:
            raise ImproperlyConfigured("Label with no 'attributes' dict found")
        attrs = label_config['attributes']
        if 'class' not in attrs:
            raise ImproperlyConfigured("Labels must have an attribute 'class'")
        label_class = attrs['class']
        if label_class in self._class_config:
            raise ImproperlyConfigured(
                "Label with class '%s' defined more than once" % label_class)

        # Store config
        self._class_config[label_class] = label_config

        # Parse configuration and create handlers and item
        self.parseConfiguration(label_class, label_config)

        # Add label class button
        button_text = label_config['text']
        button = QPushButton(button_text, self)
        button.setCheckable(True)
        button.setFlat(True)
        button.clicked.connect(bind(self.onClassButtonPressed, label_class))
        self._class_buttons[label_class] = button
        self._classbox_layout.addWidget(button)

        # Add hotkey
        if 'hotkey' in label_config:
            hotkey = QShortcut(QKeySequence(label_config['hotkey']), self)
            hotkey.activated.connect(button.click)
            self._class_shortcuts[label_class] = hotkey

    def parseConfiguration(self, label_class, label_config):
        attrs = label_config['attributes']

        # Add prototype item for insertion
        self._class_items[label_class] = AnnotationModelItem(
            {'class': label_class})

        # Create attribute handler widgets or update their values
        for attr, vals in attrs.items():
            if attr in self._attribute_handlers:
                self._attribute_handlers[attr].updateValues(vals)
            else:
                handler = self._handler_factory.create(attr, vals)
                if handler is None:
                    self._class_items[label_class][attr] = vals
                else:
                    self._attribute_handlers[attr] = handler

        for attr in attrs:
            if attr in self._attribute_handlers:
                self._class_items[label_class].update(
                    self._attribute_handlers[attr].defaults())

    def getHandler(self, attribute):
        if attribute in self._attribute_handlers:
            return self._attribute_handlers[attribute]
        else:
            return None

    def getLabelClassAttributes(self, label_class):
        return self._class_config[label_class]['attributes'].keys()

    def onClassButtonPressed(self, label_class):
        if self._class_buttons[label_class].isChecked():
            self.startInsertionMode(label_class)
        else:
            self.endInsertionMode()

    def startInsertionMode(self, label_class):
        self.endInsertionMode(False)
        for lc, button in self._class_buttons.items():
            button.setChecked(lc == label_class)
        LOG.debug("Starting insertion mode for %s" % label_class)
        self._label_editor = LabelEditor([self._class_items[label_class]],
                                         self, True)
        self._layout.insertWidget(1, self._label_editor, 0)
        self.insertionModeStarted.emit(label_class)

    def endInsertionMode(self, uncheck_buttons=True):
        if self._label_editor is not None:
            LOG.debug("Ending insertion/edit mode")
            self._label_editor.hide()
            self._layout.removeWidget(self._label_editor)
            self._label_editor = None
            if uncheck_buttons:
                self.uncheckAllButtons()
            self.insertionModeEnded.emit()

    def uncheckAllButtons(self):
        for lc, button in self._class_buttons.items():
            button.setChecked(False)

    def markEditButtons(self, label_classes):
        for lc, button in self._class_buttons.items():
            button.setFlat(lc not in label_classes)

    def currentEditorProperties(self):
        if self._label_editor is None:
            return None
        else:
            return self._label_editor.currentProperties()

    def startEditMode(self, model_items):
        # If we're in insertion mode, ignore empty edit requests
        if self._label_editor is not None and self._label_editor.insertionMode() \
                and len(model_items) == 0:
            return

        self.endInsertionMode()
        LOG.debug("Starting edit mode for items: %s" % model_items)
        self._label_editor = LabelEditor(model_items, self)
        self.markEditButtons(self._label_editor.labelClasses())
        self._layout.insertWidget(1, self._label_editor, 0)

    def _setupGUI(self):
        self._class_buttons = {}
        self._class_shortcuts = {}
        self._label_editor = None

        # Label class buttons
        self._classbox = QGroupBox("Labels", self)
        self._classbox_layout = FloatingLayout()
        self._classbox.setLayout(self._classbox_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 0)
        self._layout.addStretch(1)
Exemple #6
0
class PropertyEditor(QWidget):
    # Signals
    insertionModeStarted       = pyqtSignal(str)
    insertionModeEnded         = pyqtSignal()
    insertionPropertiesChanged = pyqtSignal(object)
    editPropertiesChanged      = pyqtSignal(object)

    def __init__(self, config, parent=None):
        QWidget.__init__(self, parent)
        self._class_config       = {}
        self._class_items        = {}
        self._class_prototypes   = {}
        self._attribute_handlers = {}
        self._handler_factory    = AttributeHandlerFactory()

        self._setupGUI()

        # Add label classes from config
        for label in config:
            self.addLabelClass(label)

    def onModelChanged(self, new_model):
        attrs = set([k for k, v in self._attribute_handlers.items() if v.autoAddEnabled()])
        if len(attrs) > 0:
            start = time.time()
            attr2vals = {}
            for item in new_model.iterator(AnnotationModelItem):
                for attr in attrs:
                    if attr in item:
                        if attr not in attr2vals:
                            attr2vals[attr] = set((item[attr], ))
                        else:
                            attr2vals[attr] |= set((item[attr], ))
            diff = time.time() - start
            LOG.info("Extracted annotation values from model in %.2fs" % diff)
            for attr, vals in attr2vals.items():
                h = self._attribute_handlers[attr]
                for val in vals:
                    h.addValue(val, True)

    def addLabelClass(self, label_config):
        # Check label configuration
        if 'attributes' not in label_config:
            raise ImproperlyConfigured("Label with no 'attributes' dict found")
        attrs = label_config['attributes']
        if 'class' not in attrs:
            raise ImproperlyConfigured("Labels must have an attribute 'class'")
        label_class = attrs['class']
        if label_class in self._class_config:
            raise ImproperlyConfigured("Label with class '%s' defined more than once" % label_class)

        # Store config
        self._class_config[label_class] = label_config

        # Parse configuration and create handlers and item
        self.parseConfiguration(label_class, label_config)

        # Add label class button
        button = QPushButton(label_class, self)
        button.setCheckable(True)
        button.setFlat(True)
        button.clicked.connect(bind(self.onClassButtonPressed, label_class))
        self._class_buttons[label_class] = button
        self._classbox_layout.addWidget(button)

        # Add hotkey
        if 'hotkey' in label_config:
            hotkey = QShortcut(QKeySequence(label_config['hotkey']), self)
            hotkey.activated.connect(button.click)
            self._class_shortcuts[label_class] = hotkey

    def parseConfiguration(self, label_class, label_config):
        attrs = label_config['attributes']

        # Add prototype item for insertion
        self._class_items[label_class] = AnnotationModelItem({ 'class': label_class })

        # Create attribute handler widgets or update their values
        for attr, vals in attrs.items():
            if attr in self._attribute_handlers:
                self._attribute_handlers[attr].updateValues(vals)
            else:
                handler = self._handler_factory.create(attr, vals)
                if handler is None:
                    self._class_items[label_class][attr] = vals
                else:
                    self._attribute_handlers[attr] = handler

        for attr in attrs:
            if attr in self._attribute_handlers:
                self._class_items[label_class].update(self._attribute_handlers[attr].defaults())

    def getHandler(self, attribute):
        if attribute in self._attribute_handlers:
            return self._attribute_handlers[attribute]
        else:
            return None

    def getLabelClassAttributes(self, label_class):
        return self._class_config[label_class]['attributes'].keys()

    def onClassButtonPressed(self, label_class):
        if self._class_buttons[label_class].isChecked():
            self.startInsertionMode(label_class)
        else:
            self.endInsertionMode()

    def startInsertionMode(self, label_class):
        self.endInsertionMode(False)
        for lc, button in self._class_buttons.items():
            button.setChecked(lc == label_class)
        LOG.debug("Starting insertion mode for %s" % label_class)
        self._label_editor = LabelEditor([self._class_items[label_class]], self, True)
        self._layout.insertWidget(1, self._label_editor, 0)
        self.insertionModeStarted.emit(label_class)

    def endInsertionMode(self, uncheck_buttons=True):
        if self._label_editor is not None:
            LOG.debug("Ending insertion/edit mode")
            self._label_editor.hide()
            self._layout.removeWidget(self._label_editor)
            self._label_editor = None
            if uncheck_buttons:
                self.uncheckAllButtons()
            self.insertionModeEnded.emit()

    def uncheckAllButtons(self):
        for lc, button in self._class_buttons.items():
            button.setChecked(False)

    def markEditButtons(self, label_classes):
        for lc, button in self._class_buttons.items():
            button.setFlat(lc not in label_classes)

    def currentEditorProperties(self):
        if self._label_editor is None:
            return None
        else:
            return self._label_editor.currentProperties()

    def startEditMode(self, model_items):
        # If we're in insertion mode, ignore empty edit requests
        if self._label_editor is not None and self._label_editor.insertionMode() \
                and len(model_items) == 0:
            return

        self.endInsertionMode()
        LOG.debug("Starting edit mode for items: %s" % model_items)
        self._label_editor = LabelEditor(model_items, self)
        self.markEditButtons(self._label_editor.labelClasses())
        self._layout.insertWidget(1, self._label_editor, 0)

    def _setupGUI(self):
        self._class_buttons = {}
        self._class_shortcuts = {}
        self._label_editor  = None

        # Label class buttons
        self._classbox = QGroupBox("Labels", self)
        self._classbox_layout = FloatingLayout()
        self._classbox.setLayout(self._classbox_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 0)
        self._layout.addStretch(1)
Exemple #7
0
    def __init__(self,
                 property=None,
                 idName=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)

        self.hotkeys = []
        self.displayName = displayName
        self.idName = idName

        self.vlayout = MyVBoxLayout()  #QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)

        # LOG.info("AttrAreaWidget constructor : property {} ...".format(property))
        self.numGroups = len(property) if property is not None else 0

        self.checkboxGroupsDescDict = {}
        self.checkboxGroupWidgets = {}

        for i in xrange(self.numGroups):
            thisGroupProperty = property[i]  # thisGroupProperty is a dict
            thisGroupName = thisGroupProperty.get(
                'name')  # thisGroupName is a string
            thisGroupOption = thisGroupProperty.get(
                'option'
            )  # thisGroupOption is a tuple of dicts. each dict describes a checkbox
            thisGroupText = thisGroupProperty.get('displaytext')

            isExclusive = False if thisGroupName in config.NON_EXCLUSIVE_ATTRS_TAG_LIST else True
            if thisGroupName == config.ANNOTATION_PERSONBIKE_TYPE_GROUP_TOKEN:
                checkboxWidgetMinWidthInPixels = 52
            else:
                checkboxWidgetMinWidthInPixels = 38

            if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_UPPER_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_UPPER_COLOR_WIDGET_NUMBER
            elif thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_LOWER_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_LOWER_COLOR_WIDGET_NUMBER
            elif thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_VEHICLE_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_VEHICLE_COLOR_WIDGET_NUMBER
            else:
                thisGroup_maxCheckedNum = config.MAX_CHECKED_OPTIONS_NUMBER
                thisGroup_maxSeperateWidgetNum = config.MAX_OPTIONS_NUMBER

            thisGroupWidget = CheckboxListWidget(
                thisGroupName, thisGroupText, isExclusive, thisGroupOption,
                self.checkboxStateChangedSignal, thisGroup_maxCheckedNum,
                thisGroup_maxSeperateWidgetNum)

            # add checkbox record to this checkbox group record
            thisGroupCheckboxs = {}
            for checkboxOption in thisGroupOption:
                thisCheckboxName = checkboxOption[
                    config.METADATA_ATTR_VALUE_TOKEN]
                thisCheckboxProperty = checkboxOption.copy()
                thisGroupCheckboxs[thisCheckboxName] = thisCheckboxProperty

            self.vlayout.addWidget(thisGroupWidget)

            self.checkboxGroupsDescDict[
                thisGroupName] = thisGroupCheckboxs  # add this checkbox group record to checkbox groups recrod
            self.checkboxGroupWidgets[thisGroupName] = thisGroupWidget

        # LOG.info("checkboxGroupWidgets = {} checkboxGroupsDescDict = {}".format(self.checkboxGroupWidgets, self.checkboxGroupsDescDict)

        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return
Exemple #8
0
class AttrAreaWidget(QWidget):
    checkboxStateChangedSignal = pyqtSignal(str, object)

    def setAnnotationScene(self, annotationScene):
        self._scene = annotationScene

    def enableAllGroups(self, enabled=True):
        for gn, gw in self.checkboxGroupWidgets.items():
            gw.setEnabled(enabled)
            gw.enableAll(enabled)

    def enableCheckboxGroup(self, checkboxGroupName, enabled=True):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.setEnabled(enabled)
            groupWidget.enableAll(enabled)

    def enableCheckbox(self, checkboxGroupName, checkboxName, enabled=True):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.enable(checkboxName, enabled)

    # return (names_list, texts_list, displaycolors_list)
    def get_group_config(self, checkboxGroupName):
        names = []
        texts = []
        displaycolors = []
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            names, texts, displaycolors = groupWidget.get_config()

        return names, texts, displaycolors

    # return names_list
    def get_group_names(self):
        groupNames = []
        for groupName in self.checkboxGroupWidgets.keys():
            groupNames.append(groupName)
        return groupNames

    def setCheckedAllGroups(self, checked=True):
        for groupName, groupWidget in self.checkboxGroupWidgets.items():
            groupWidget.setCheckedAll(checked)

    def setCheckedGroup(self, checkboxGroupName, checked=True):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.setCheckedAll(checked)

    def setCheckedCheckbox(self,
                           checkboxGroupName,
                           checkboxName,
                           checked=True):
        # print "checkboxGroupName {} checkboxName {} checked {} ...".format(checkboxGroupName, checkboxName, checked)
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        # print "checkboxGroupWidgets = {}...".format(self.checkboxGroupWidgets)
        if groupWidget is not None:
            # LOG.info("AttrAreaWidget.setCheckedCheckbox for group {} checked {} ..".format(checkboxGroupName, checked))
            # print ("AttrAreaWidget.setCheckedCheckbox for group {} checked {} ..".format(checkboxGroupName, checked))
            groupWidget.setCheckedCheckbox(checkboxName, checked)

    def setCheckedCheckboxs(self,
                            checkboxGroupName,
                            checkboxNamesList,
                            checked=True):
        if checkboxNamesList is None:
            return
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            # LOG.info(u"{} AttrAreaWidget.setCheckedCheckbox for group {} box {} checked {} ..".format(self.groupName, checkboxGroupName, checkboxNamesList, checked))
            groupWidget.setCheckedCheckboxs(checkboxNamesList, checked)

    def sendCheckedSignal(self, checkboxGroupName):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.sendCheckedSignal()

    def getCheckedWidgetsOptionInfo(self, checkboxGroupName):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            return groupWidget.getCheckedWidgetsOptionInfo()
        return None, None

    def setOptionStateChangedSignalSlot(self, checkboxStateChangedSignalSlot):
        if checkboxStateChangedSignalSlot is not None:
            # print u"pannel {}: checkboxStateChangedSignal{} checkboxStateChangedSignalSlot {} is connected...".format(self.idName, self.checkboxStateChangedSignal, checkboxStateChangedSignalSlot)
            self.checkboxStateChangedSignal.connect(
                checkboxStateChangedSignalSlot)

    def hideGroup(self, checkboxGroupName):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            return groupWidget.hide()

    def __init__(self,
                 property=None,
                 idName=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)

        self.hotkeys = []
        self.displayName = displayName
        self.idName = idName

        self.vlayout = MyVBoxLayout()  #QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)

        # LOG.info("AttrAreaWidget constructor : property {} ...".format(property))
        self.numGroups = len(property) if property is not None else 0

        self.checkboxGroupsDescDict = {}
        self.checkboxGroupWidgets = {}

        for i in xrange(self.numGroups):
            thisGroupProperty = property[i]  # thisGroupProperty is a dict
            thisGroupName = thisGroupProperty.get(
                'name')  # thisGroupName is a string
            thisGroupOption = thisGroupProperty.get(
                'option'
            )  # thisGroupOption is a tuple of dicts. each dict describes a checkbox
            thisGroupText = thisGroupProperty.get('displaytext')

            isExclusive = False if thisGroupName in config.NON_EXCLUSIVE_ATTRS_TAG_LIST else True
            if thisGroupName == config.ANNOTATION_PERSONBIKE_TYPE_GROUP_TOKEN:
                checkboxWidgetMinWidthInPixels = 52
            else:
                checkboxWidgetMinWidthInPixels = 38

            if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_UPPER_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_UPPER_COLOR_WIDGET_NUMBER
            elif thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_LOWER_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_LOWER_COLOR_WIDGET_NUMBER
            elif thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                thisGroup_maxCheckedNum = config.MAX_VEHICLE_COLOR_NUMBER
                thisGroup_maxSeperateWidgetNum = config.GUI_MAX_SEPERATE_VEHICLE_COLOR_WIDGET_NUMBER
            else:
                thisGroup_maxCheckedNum = config.MAX_CHECKED_OPTIONS_NUMBER
                thisGroup_maxSeperateWidgetNum = config.MAX_OPTIONS_NUMBER

            thisGroupWidget = CheckboxListWidget(
                thisGroupName, thisGroupText, isExclusive, thisGroupOption,
                self.checkboxStateChangedSignal, thisGroup_maxCheckedNum,
                thisGroup_maxSeperateWidgetNum)

            # add checkbox record to this checkbox group record
            thisGroupCheckboxs = {}
            for checkboxOption in thisGroupOption:
                thisCheckboxName = checkboxOption[
                    config.METADATA_ATTR_VALUE_TOKEN]
                thisCheckboxProperty = checkboxOption.copy()
                thisGroupCheckboxs[thisCheckboxName] = thisCheckboxProperty

            self.vlayout.addWidget(thisGroupWidget)

            self.checkboxGroupsDescDict[
                thisGroupName] = thisGroupCheckboxs  # add this checkbox group record to checkbox groups recrod
            self.checkboxGroupWidgets[thisGroupName] = thisGroupWidget

        # LOG.info("checkboxGroupWidgets = {} checkboxGroupsDescDict = {}".format(self.checkboxGroupWidgets, self.checkboxGroupsDescDict)

        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return

    def stateHasChanged(self, checkedWidgetsAttrDescDict):
        # LOG.info("call AttrAreaWidget.stateHasChanged({})".format(checkedWidgetsAttrDescDict))
        return

    def get_checked_widgets_name(self, group_name):
        widget = self.checkboxGroupWidgets.get(str(group_name), None)
        # LOG.info("AttrAreaWidget.get_checked_widgets_name ... widget = {}".format(widget))
        checkedWidgetsNameList = widget.get_checked_widgets_name(
        ) if widget is not None else None
        # LOG.info("AttrAreaWidget.get_checked_widgets_name ... checkedWidget = {}".format(checkedWidget))
        return checkedWidgetsNameList

    def add_hotkey(self, choice, name, hotkey):
        self.hotkeys.append((choice, name, hotkey))

    def get_checked_widgets_attrDesc(self):
        descsDict = {}
        for widgetName, widgetInfo in self.checkboxWidgetsInfo.items():
            if widgetInfo[0].isChecked():
                desc = [None, None, None, None, None]
                desc[
                    checkedWidgetsAttrDescList_checkedWidget_idx] = widgetInfo[
                        0]
                desc[
                    checkedWidgetsAttrDescList_checkedWidgetText_idx] = widgetInfo[
                        1]
                desc[
                    checkedWidgetsAttrDescList_checkedWidgetColor_idx] = widgetInfo[
                        2]
                desc[
                    checkedWidgetsAttrDescList_checkedWidgetStyleSheet_idx] = widgetInfo[
                        3]
                descsDict[widgetName] = desc

        for widgetInfo in self.popupWidgetsInfo.values():
            for actionWidget in widgetInfo[
                    popupWidgetsInfo_actionsWidgetList_idx]:
                if actionWidget.isChecked():
                    idx = widgetInfo[
                        popupWidgetsInfo_actionsWidgetList_idx].index(
                            actionWidget)
                    desc = [None, None, None, None, None]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidget_idx] = actionWidget
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetText_idx] = widgetInfo[
                            popupWidgetsInfo_actionsTextList_idx][idx]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetColor_idx] = widgetInfo[
                            popupWidgetsInfo_actionsColorList_idx][idx]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetStyleSheet_idx] = widgetInfo[
                            popupWidgetsInfo_actionsWidgetStyleSheet_idx]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetPopupParentWidget_idx] = widgetInfo[
                            popupWidgetsInfo_pushButtonWidget_idx]
                    widgetName = widgetInfo[
                        popupWidgetsInfo_actionsNameList_idx][idx]
                    descsDict[widgetName] = desc

        return descsDict

    def startEditMode(self, model_items):
        return
class PropertyEditor(QWidget):
    # Signals
    insertionModeStarted = pyqtSignal(str)
    insertionModeEnded = pyqtSignal()
    insertionPropertiesChanged = pyqtSignal(object)
    editPropertiesChanged = pyqtSignal(object)

    def __init__(self, config, parent=None):
        QWidget.__init__(self, parent)
        self._class_config = {}
        self._class_items = {}
        self._class_prototypes = {}
        self._attribute_handlers = {}
        self._handler_factory = AttributeHandlerFactory()

        self._setupGUI()
        self._parea.setGeometry(0, 0, 200, 0)
        # (快捷键,button)
        self.shortcut2button = {}
        # (button,快捷键)
        self.label2shortcut = {}
        # Add label classes from config
        for label in config:
            self.addLabelClass(label)
        self.image_path = None

    def addLabelClassByPath(self, configs_path):
        # 读配置文件
        with open(configs_path, 'r') as f:
            configs = json5.load(f)
        # 写入当前配置文件的路径
        direct = os.path.dirname(sys.argv[0])
        with open(os.path.join(direct, 'sloth.txt'), 'w') as f:
            f.write(configs_path)
        self._parea.setGeometry(0, 0, 200, 0)
        for temp_json in configs:
            self.addLabelClass(temp_json)
            # 注册
            self._register('inserter', temp_json['attributes']['class'],
                           temp_json['inserter'])
            self._register('item', temp_json['attributes']['class'],
                           temp_json['item'])
            # add_txt的下拉框里也要添加
            self.combo_box.addItem(temp_json['attributes']['class'])
            self.items.append(temp_json['attributes']['class'])
            cf.LABELS.append(temp_json)

    def onModelChanged(self, new_model):
        attrs = set([
            k for k, v in self._attribute_handlers.items()
            if v.autoAddEnabled()
        ])
        if len(attrs) > 0:
            start = time.time()
            attr2vals = {}
            for item in new_model.iterator(AnnotationModelItem):
                for attr in attrs:
                    if attr in item:
                        if attr not in attr2vals:
                            attr2vals[attr] = set((item[attr], ))
                        else:
                            attr2vals[attr] |= set((item[attr], ))
            diff = time.time() - start
            LOG.info("Extracted annotation values from model in %.2fs" % diff)
            for attr, vals in attr2vals.items():
                h = self._attribute_handlers[attr]
                for val in vals:
                    h.addValue(val, True)

    # 设置右键菜单所在位置
    def showContextMenu(self, label_class):
        self.label_menu[label_class].exec_(QCursor.pos())

    # 删除所有的item
    def remove_all_item(self):
        for v in self.shortcut2button.values():
            v.setShortcut(QKeySequence())
        self.shortcut2button.clear()
        self.label2shortcut.clear()
        self.label_menu.clear()
        self.label_action.clear()
        self._class_config.clear()
        self.combo_box.clear()
        self.items.clear()
        temp_dict = copy.copy(self._class_buttons)
        for k, v in temp_dict.items():
            self._classbox_layout.removeWidget(v)
            # 下面这句很重要,不然相当于没删
            self._class_buttons[k].deleteLater()
        self._class_buttons.clear()
        cf.LABELS.clear()

        self._parea.setGeometry(0, 0, 200, 60)

    # 删除标签
    def remove_item(self, label_class):
        """
        删除标签
        :param label_class: 删除的标签名字
        """
        try:
            # 删除快捷键
            self.endInsertionMode()
            shortcut = self.label2shortcut[label_class]
            if shortcut in self.shortcut2button and \
                    self.shortcut2button[shortcut] is not None and \
                    self.shortcut2button[shortcut] == self._class_buttons[label_class]:
                self.shortcut2button[shortcut].setShortcut(QKeySequence())
                del self.shortcut2button[shortcut]
            # 删除菜单
            del self.label_menu[label_class]
            # 删除菜单的动作
            del self.label_action[label_class]
            # 删除视图中的按钮
            self._classbox_layout.removeWidget(
                self._class_buttons[label_class])
            self._class_buttons[label_class].deleteLater()
            self._class_buttons[label_class] = None
            del self._class_config[label_class]
            # 写回json
            direct = os.path.dirname(sys.argv[0])
            with open(os.path.join(direct, 'sloth.txt'), 'r') as f:
                label_path = f.read()
            try:
                with open(label_path, 'r') as f:
                    temp = json5.load(f)
                for i, current_json in enumerate(temp):
                    if current_json['attributes']['class'] == label_class:
                        temp.remove(current_json)
                        # 遍历combo box 找到要删的
                        for i in range(len(self.combo_box)):
                            current_label = self.combo_box.itemText(i)
                            if current_label == label_class:
                                print('removed', label_class)
                                self.combo_box.removeItem(i)
                                self.items.pop(i)
                                break
                        self._class_buttons.pop(label_class)
                        break

                with open(label_path, 'w') as f:
                    json5.dump(temp,
                               f,
                               quote_keys=True,
                               trailing_commas=False,
                               indent=4,
                               separators=(',', ': '),
                               sort_keys=True,
                               ensure_ascii=False)
                self._parea.setGeometry(
                    0, 0, 200, max(self._parea.geometry().height() - 40, 60))
            except Exception as e:
                print(e)
        except Exception as e:
            print(e)

    # 添加标签
    def addLabelClass(self, label_config):
        """
        添加标签
        :param label_config: 标签的json
        """
        # Check label configuration
        if 'attributes' not in label_config:
            raise ImproperlyConfigured("Label with no 'attributes' dict found")
        attrs = label_config['attributes']
        if 'class' not in attrs:
            raise ImproperlyConfigured("Labels must have an attribute 'class'")
        label_class = attrs['class']
        if label_class in self._class_config:
            raise ImproperlyConfigured(
                "Label with class '%s' defined more than once" % label_class)

        # Store config
        self._class_config[label_class] = label_config

        # Parse configuration and create handlers and item
        self.parseConfiguration(label_class, label_config)

        # Add label class button
        button_text = label_config['text']
        button = QPushButton(button_text)
        button.setCheckable(True)
        button.setFlat(True)
        button.clicked.connect(bind(self.onClassButtonPressed, label_class))
        self._class_buttons[label_class] = button
        self._parea.setGeometry(0, 0, 200,
                                self._parea.geometry().height() + 40)
        self._classbox_layout.addWidget(button)

        # 添加右键菜单
        self.label_menu[label_class] = QtGui.QMenu(self)
        self.label_action[label_class] = self.label_menu[
            label_class].addAction('删除')
        self.label_action[label_class].triggered.connect(
            bind(self.remove_item, label_class))
        self._class_buttons[label_class].setContextMenuPolicy(
            QtCore.Qt.CustomContextMenu)
        self._class_buttons[label_class].customContextMenuRequested.connect(
            bind(self.showContextMenu, label_class))

        # Add hotkey
        if 'hotkey' in label_config:
            # 快捷键
            hotkey = label_config['hotkey']
            # 快捷键已经存在,那就去掉原来的
            if hotkey in self.shortcut2button and self.shortcut2button[
                    hotkey] is not None:
                self.shortcut2button[hotkey].setShortcut(QKeySequence())
            # 设置快捷键
            button.setShortcut(QKeySequence(hotkey))
            self.shortcut2button[hotkey] = button
            self.label2shortcut[label_class] = hotkey

    def parseConfiguration(self, label_class, label_config):
        attrs = label_config['attributes']

        # Add prototype item for insertion
        self._class_items[label_class] = AnnotationModelItem(
            {'class': label_class})

        # Create attribute handler widgets or update their values
        for attr, vals in attrs.items():
            if attr in self._attribute_handlers:
                self._attribute_handlers[attr].updateValues(vals)
            else:
                handler = self._handler_factory.create(attr, vals)
                if handler is None:
                    self._class_items[label_class][attr] = vals
                else:
                    self._attribute_handlers[attr] = handler

        for attr in attrs:
            if attr in self._attribute_handlers:
                self._class_items[label_class].update(
                    self._attribute_handlers[attr].defaults())

    def getHandler(self, attribute):
        if attribute in self._attribute_handlers:
            return self._attribute_handlers[attribute]
        else:
            return None

    def getLabelClassAttributes(self, label_class):
        return self._class_config[label_class]['attributes'].keys()

    def onClassButtonPressed(self, label_class):
        if self._class_buttons[label_class].isChecked():
            self.startInsertionMode(label_class)
        else:
            self.endInsertionMode()

    def startInsertionMode(self, label_class):
        self.endInsertionMode(False)
        for lc, button in self._class_buttons.items():
            button.setChecked(lc == label_class)
        LOG.debug("Starting insertion mode for %s" % label_class)
        self._label_editor = LabelEditor([self._class_items[label_class]],
                                         self, True)
        # self._layout.insertWidget(1, self._label_editor, 0)
        self.insertionModeStarted.emit(label_class)

    def endInsertionMode(self, uncheck_buttons=True):
        if self._label_editor is not None:
            LOG.debug("Ending insertion/edit mode")
            self._label_editor.hide()
            # self._layout.removeWidget(self._label_editor)
            self._label_editor = None
            if uncheck_buttons:
                self.uncheckAllButtons()
            self.insertionModeEnded.emit()

    def uncheckAllButtons(self):
        for lc, button in self._class_buttons.items():
            button.setChecked(False)

    def markEditButtons(self, label_classes):
        for lc, button in self._class_buttons.items():
            button.setFlat(lc not in label_classes)

    def currentEditorProperties(self):
        if self._label_editor is None:
            return None
        else:
            return self._label_editor.currentProperties()

    def startEditMode(self, model_items):
        # If we're in insertion mode, ignore empty edit requests
        if self._label_editor is not None and self._label_editor.insertionMode() \
                and len(model_items) == 0:
            return

        self.endInsertionMode()
        LOG.debug("Starting edit mode for items: %s" % model_items)
        self._label_editor = LabelEditor(model_items, self)
        self.markEditButtons(self._label_editor.labelClasses())
        self._layout.insertWidget(1, self._label_editor, 0)

    # 添加txt
    def add_txt(self):
        if not Main.isConfig(Main.get_json()):
            QMessageBox.warning(self, "Warning", '当前的配置文件错误或者为空,无法添加txt',
                                QMessageBox.Ok)
        defect = self.combo_box.currentText()
        if defect is None or defect == '':
            return
        dir_path = QFileDialog.getExistingDirectory(self)
        Main.write_txt(dir_path, {defect}, 'defect')

    # 选择图片
    def select_image(self):
        image_types = [
            '*.jpg', '*.bmp', '*.png', '*.pgm', '*.ppm', '*.tiff', '*.tif',
            '*.gif'
        ]
        format_str = ' '.join(image_types)
        fname = QFileDialog.getOpenFileName(
            self, "select training source", '.',
            "Media files (%s)" % (format_str, ))
        if fname is None or fname == '':
            return
        self.image_path = os.path.abspath(fname)
        self._image_label.setText(os.path.basename(self.image_path))

    # 获得图片路径对应的json路径
    def image2json(self, path):
        temp = path.split('.')
        return ''.join(temp[:-1]) + '.json'

    # 获得图片路径转成的训练图片路径
    def image2cpimage(self, path, id, length):
        length = max(length, 5)
        temp = path.split('.')
        return ''.join(temp[:-1]) + str(id).zfill(length) + '.' + temp[-1]

    # 判断是否包含瑕疵
    def contains_defect(self, annotations, defect_type):
        defects = set()
        for annotation in annotations:
            if 'class' in annotation:
                defects.add(annotation['class'])
        return defect_type.issubset(defects)

    # 生成训练数据
    def generate(self):
        if not Main.isConfig(Main.get_json()):
            QMessageBox.warning(self, "Warning", '配置文件错误或者为空,无法生成训练数据',
                                QMessageBox.Ok)
            return
        a = TrainDialog(self)
        a.exec_()

    # 从labeltool中设置搜索按钮
    def setFunction(self, func):
        self._search_btn.clicked.connect(func)

    # 获得关键字
    def get_key_word(self):
        key_word = self._key_word.text()
        if key_word is None or key_word == '':
            key_word = self._key_word.placeholderText()
        return key_word

    # 获得文件类型
    def get_extension(self):
        extension = self._extension.text()
        # 为空则用默认的,否则用输入的
        if extension is None or extension == '':
            extension = self._extension.placeholderText()
        return extension

    # 返回一个含有权限类型的list
    def get_attributes_type(self):
        '''
        'Rect':('sloth.items.RectItem','sloth.items.RectItemInserter'),
        'Point':('sloth.items.PointItem','sloth.items.PointItemInserter'),
        'Polygon':('sloth.items.PolygonItem','sloth.items.PolygonItemInserter')
        '''
        return ['Rect', 'Point', 'Polygon']

    # 写回json
    def rewrite_json(self, temp_json):
        # json所在的txt
        direct = os.path.dirname(sys.argv[0])
        with open(os.path.join(direct, 'sloth.txt'), 'r') as f:
            label_path = f.read()
        try:
            # 读取旧json
            with open(label_path, 'r') as f:
                temp = json5.load(f)
            # 追加我们要写入的json
            temp.append(temp_json)
            # 写入
            with open(label_path, 'w') as f:
                json5.dump(temp,
                           f,
                           quote_keys=True,
                           trailing_commas=False,
                           indent=4,
                           separators=(',', ': '),
                           sort_keys=True,
                           ensure_ascii=False)
        except Exception as e:
            print(e)

    # 添加标签
    def add_attributes(self):
        if not Main.isConfig(Main.get_json()):
            QMessageBox.warning(self, "Warning", '当前配置文件错误或者为空,不能添加标签',
                                QMessageBox.Ok)
            return
        # 转换dict
        type_dict = {
            'Rect': ('sloth.items.RectItem', 'sloth.items.RectItemInserter'),
            'Point':
            ('sloth.items.PointItem', 'sloth.items.PointItemInserter'),
            'Polygon':
            ('sloth.items.PolygonItem', 'sloth.items.PolygonItemInserter')
        }
        # 获取添加的标签信息
        attributes = {'class': self.attributes_LineEdit.text()}
        attributes_item, attributes_inserter = type_dict[
            self.attributes_type.currentText()]
        attributes_hotkey = self.hotkey.text()
        attributes_text = self.text_LineEdit.text()
        global brush2idx
        brush_idx = str(brush2idx[self.brush_combo_box.currentText()])
        temp_json = {
            'attributes': attributes,
            'inserter': attributes_inserter,
            'item': attributes_item,
            'color': ','.join(map(str, self.color_info)),
            'brush': brush_idx,
            'text': attributes_text
        }

        # 快捷键
        if attributes_hotkey is not None and attributes_hotkey != '':
            temp_json['hotkey'] = attributes_hotkey
        print(temp_json)
        try:
            # 加入标签
            self.addLabelClass(temp_json)
            print(self._class_buttons.keys())
            # 注册
            self._register('inserter', temp_json['attributes']['class'],
                           temp_json['inserter'])
            self._register('item', temp_json['attributes']['class'],
                           temp_json['item'])
            # add_txt的下拉框里也要添加
            self.combo_box.addItem(temp_json['attributes']['class'])
            self.items.append(temp_json['attributes']['class'])
            cf.LABELS.append(temp_json)
            # 写回json
            self.rewrite_json(temp_json)
        except Exception as e:
            print(e)

    # 颜色对话框
    def color_dialog(self):
        col = QtGui.QColorDialog.getColor()
        if col.isValid():
            self.color_label.setStyleSheet("QWidget { background-color: %s }" %
                                           col.name())
        self.color_info = col.getRgb()[:-1]

    # 设置控件的隐藏状态
    def component_visible(self, component_name, state):
        if component_name == '添加标签':
            self._group_box_add_label.setVisible(state)
        elif component_name == 'add_txt':
            self._group_box_add_txt.setVisible(state)
        elif component_name == 'add_files':
            self._group_box_add_files.setVisible(state)

    def _setupGUI(self):
        self._class_buttons = {}
        self.label_menu = {}
        self.label_action = {}
        self._label_editor = None

        # Label class buttons
        self._parea = QGroupBox("Labels")
        self._classbox = QScrollArea()
        self._classbox_layout = FloatingLayout()
        self._parea.setLayout(self._classbox_layout)
        self._parea.setGeometry(0, 0, 200, 200)
        self._classbox.setWidget(self._parea)
        self._classbox.setGeometry(0, 0, 100, 100)
        # 添加txt模块
        self.combo_box = QComboBox()
        self._group_box_add_txt = QGroupBox('add_txt', self)
        self._group_box_add_txt_layout = QVBoxLayout()
        self._group_box_add_txt.setLayout(self._group_box_add_txt_layout)
        temp = cf.LABELS
        self.items = []
        # 获取所有的标签
        for i in temp:
            self.items.append(i['attributes']['class'])
        # 假如下拉框
        self.combo_box.addItems(self.items)
        self.add_txt_btn = QPushButton('add txt')
        self.add_txt_btn.clicked.connect(self.add_txt)
        # 加入下拉框和按钮
        self._group_box_add_txt_layout.addWidget(self.combo_box, 0)
        self._group_box_add_txt_layout.addWidget(self.add_txt_btn, 1)

        # 根据关键字搜索图片模块
        self._group_box_add_files = QGroupBox('add files', self)
        # 文件名包含的
        self._key_word = QLineEdit('')
        self._key_word.setPlaceholderText('Second')
        # 文件类型
        self._extension = QLineEdit('')
        self._extension.setPlaceholderText('bmp')
        self._search_btn = QPushButton('search files')
        self._group_box_add_files_layout = QVBoxLayout()
        # 加入控件
        self._group_box_add_files_layout.addWidget(self._key_word, 0)
        self._group_box_add_files_layout.addWidget(self._extension, 1)
        self._group_box_add_files_layout.addWidget(self._search_btn, 2)
        self._group_box_add_files.setLayout(self._group_box_add_files_layout)

        # 添加标签模块
        self._group_box_add_label = QGroupBox("添加标签", self)
        self._add_label_group_layout = QVBoxLayout()
        self._group_box_add_label.setLayout(self._add_label_group_layout)
        # 标签的class
        self.attributes_LineEdit = QLineEdit('')
        self.attributes_LineEdit.setPlaceholderText('attributes')
        # 标签画出来的类型
        self.attributes_type = QComboBox()
        self.attributes_type.addItems(self.get_attributes_type())
        # 快捷键,目前设置了只允许一个键
        self.hotkey = QLineEdit('')
        self.hotkey.setPlaceholderText('hotkey')
        self.regx = QRegExp("[a-z0-9]$")
        self.validator = QRegExpValidator(self.regx, self.hotkey)
        self.hotkey.setValidator(self.validator)
        # 标签显示
        self.text_LineEdit = QLineEdit('')
        self.text_LineEdit.setPlaceholderText('text')
        # 颜色
        color = QtGui.QColor(0, 0, 0)
        self.color_label = QtGui.QWidget()
        self.color_label.setStyleSheet("QWidget { background-color: %s }" %
                                       color.name())
        self.color_info = [0, 0, 0]
        self.color_layout = QtGui.QHBoxLayout()
        self.color_btn = QPushButton('选择颜色')
        self.color_btn.clicked.connect(self.color_dialog)
        self.color_layout.addWidget(self.color_label)
        self.color_layout.addWidget(self.color_btn)
        # 笔刷
        global brush2idx
        self.brush_combo_box = QComboBox()
        self.brush_combo_box.addItems(list(brush2idx.keys()))
        # 按钮
        self.attributes_add_btn = QPushButton('添加标签')
        self.attributes_add_btn.clicked.connect(self.add_attributes)
        # 加入控件
        self._add_label_group_layout.addWidget(self.attributes_LineEdit, 0)
        self._add_label_group_layout.addWidget(self.attributes_type, 1)
        self._add_label_group_layout.addWidget(self.hotkey, 2)
        self._add_label_group_layout.addWidget(self.text_LineEdit, 3)
        self._label_widget = QWidget()
        self._label_widget.setLayout(self.color_layout)
        self._add_label_group_layout.addWidget(self._label_widget, 4)
        self._add_label_group_layout.addWidget(self.brush_combo_box, 5)
        self._add_label_group_layout.addWidget(self.attributes_add_btn, 6)

        # 生成训练数据按钮
        self._file_button = QPushButton('生成训练数据')
        self._file_button.clicked.connect(self.generate)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 1)
        self._layout.insertWidget(-1, self._group_box_add_label, 1)
        self._layout.insertWidget(-1, self._group_box_add_txt, 1)
        self._layout.insertWidget(-1, self._group_box_add_files, 1)
        self._layout.insertWidget(-1, self._file_button, 1)
Exemple #10
0
    def _setupGUI(self):
        self._class_buttons = {}
        self.label_menu = {}
        self.label_action = {}
        self._label_editor = None

        # Label class buttons
        self._parea = QGroupBox("Labels")
        self._classbox = QScrollArea()
        self._classbox_layout = FloatingLayout()
        self._parea.setLayout(self._classbox_layout)
        self._parea.setGeometry(0, 0, 200, 200)
        self._classbox.setWidget(self._parea)
        self._classbox.setGeometry(0, 0, 100, 100)
        # 添加txt模块
        self.combo_box = QComboBox()
        self._group_box_add_txt = QGroupBox('add_txt', self)
        self._group_box_add_txt_layout = QVBoxLayout()
        self._group_box_add_txt.setLayout(self._group_box_add_txt_layout)
        temp = cf.LABELS
        self.items = []
        # 获取所有的标签
        for i in temp:
            self.items.append(i['attributes']['class'])
        # 假如下拉框
        self.combo_box.addItems(self.items)
        self.add_txt_btn = QPushButton('add txt')
        self.add_txt_btn.clicked.connect(self.add_txt)
        # 加入下拉框和按钮
        self._group_box_add_txt_layout.addWidget(self.combo_box, 0)
        self._group_box_add_txt_layout.addWidget(self.add_txt_btn, 1)

        # 根据关键字搜索图片模块
        self._group_box_add_files = QGroupBox('add files', self)
        # 文件名包含的
        self._key_word = QLineEdit('')
        self._key_word.setPlaceholderText('Second')
        # 文件类型
        self._extension = QLineEdit('')
        self._extension.setPlaceholderText('bmp')
        self._search_btn = QPushButton('search files')
        self._group_box_add_files_layout = QVBoxLayout()
        # 加入控件
        self._group_box_add_files_layout.addWidget(self._key_word, 0)
        self._group_box_add_files_layout.addWidget(self._extension, 1)
        self._group_box_add_files_layout.addWidget(self._search_btn, 2)
        self._group_box_add_files.setLayout(self._group_box_add_files_layout)

        # 添加标签模块
        self._group_box_add_label = QGroupBox("添加标签", self)
        self._add_label_group_layout = QVBoxLayout()
        self._group_box_add_label.setLayout(self._add_label_group_layout)
        # 标签的class
        self.attributes_LineEdit = QLineEdit('')
        self.attributes_LineEdit.setPlaceholderText('attributes')
        # 标签画出来的类型
        self.attributes_type = QComboBox()
        self.attributes_type.addItems(self.get_attributes_type())
        # 快捷键,目前设置了只允许一个键
        self.hotkey = QLineEdit('')
        self.hotkey.setPlaceholderText('hotkey')
        self.regx = QRegExp("[a-z0-9]$")
        self.validator = QRegExpValidator(self.regx, self.hotkey)
        self.hotkey.setValidator(self.validator)
        # 标签显示
        self.text_LineEdit = QLineEdit('')
        self.text_LineEdit.setPlaceholderText('text')
        # 颜色
        color = QtGui.QColor(0, 0, 0)
        self.color_label = QtGui.QWidget()
        self.color_label.setStyleSheet("QWidget { background-color: %s }" %
                                       color.name())
        self.color_info = [0, 0, 0]
        self.color_layout = QtGui.QHBoxLayout()
        self.color_btn = QPushButton('选择颜色')
        self.color_btn.clicked.connect(self.color_dialog)
        self.color_layout.addWidget(self.color_label)
        self.color_layout.addWidget(self.color_btn)
        # 笔刷
        global brush2idx
        self.brush_combo_box = QComboBox()
        self.brush_combo_box.addItems(list(brush2idx.keys()))
        # 按钮
        self.attributes_add_btn = QPushButton('添加标签')
        self.attributes_add_btn.clicked.connect(self.add_attributes)
        # 加入控件
        self._add_label_group_layout.addWidget(self.attributes_LineEdit, 0)
        self._add_label_group_layout.addWidget(self.attributes_type, 1)
        self._add_label_group_layout.addWidget(self.hotkey, 2)
        self._add_label_group_layout.addWidget(self.text_LineEdit, 3)
        self._label_widget = QWidget()
        self._label_widget.setLayout(self.color_layout)
        self._add_label_group_layout.addWidget(self._label_widget, 4)
        self._add_label_group_layout.addWidget(self.brush_combo_box, 5)
        self._add_label_group_layout.addWidget(self.attributes_add_btn, 6)

        # 生成训练数据按钮
        self._file_button = QPushButton('生成训练数据')
        self._file_button.clicked.connect(self.generate)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._classbox, 1)
        self._layout.insertWidget(-1, self._group_box_add_label, 1)
        self._layout.insertWidget(-1, self._group_box_add_txt, 1)
        self._layout.insertWidget(-1, self._group_box_add_files, 1)
        self._layout.insertWidget(-1, self._file_button, 1)
class PropertyEditor(QWidget):
    # Signals
    insertionModeStarted = pyqtSignal(str, str, str)
    insertionModeEnded = pyqtSignal()
    insertionPropertiesChanged = pyqtSignal(object)
    editPropertiesChanged = pyqtSignal(object)
    checkboxStateChangedSignal = pyqtSignal(str, object)
    _optionsCheckboxStateChangedSignal = pyqtSignal(str, object)

    def setAnnotationScene(self, annotationScene):
        self._scene = annotationScene

    def setOptionStateChangedSignalSlot(self, checkboxStateChangedSignalSlot):
        if checkboxStateChangedSignalSlot is not None:
            self.checkboxStateChangedSignal.connect(
                checkboxStateChangedSignalSlot)
        self._optionsCheckboxStateChangedSignal.connect(
            self.onInserterButtonOptionChanged)

    # set enable status for the checkbox group which is attached to inserterButton
    def enableCheckboxGroup(self, inserterButtonName, enabled=True):
        widgetsInfo = self._widgets_dict.get(inserterButtonName, None)
        if not widgetsInfo: return
        if widgetsInfo[1]:
            # this button has an attachedCheckboxGroup
            attachedCheckboxGroupWidget = widgetsInfo[1]
            attachedCheckboxGroupWidget.enableAll(enabled)

    # set check status for one specific checkbox which is attached to inserterButton
    def setCheckedCheckbox(self,
                           inserterButtonName,
                           checkBoxName,
                           checked=True):
        widgetsInfo = self._widgets_dict.get(inserterButtonName, None)
        # print "inserterButtonName {} _widgets_dict {} checkBoxName {} checked {} ...".format(inserterButtonName, self._widgets_dict, checkBoxName, checked)
        if not widgetsInfo: return
        if widgetsInfo[1]:
            # this button has an attachedCheckboxGroup
            attachedCheckboxGroupWidget = widgetsInfo[1]
            attachedCheckboxGroupWidget.setCheckedCheckbox(
                checkBoxName, checked)

    # set check status for some specific checkboxs which are attached to inserterButton
    def setCheckedCheckboxs(self,
                            inserterButtonName,
                            checkBoxNamesList,
                            checked=True):
        widgetsInfo = self._widgets_dict.get(inserterButtonName, None)
        if not widgetsInfo: return
        if widgetsInfo[1]:
            # this button has an attachedCheckboxGroup
            attachedCheckboxGroupWidget = widgetsInfo[1]
            attachedCheckboxGroupWidget.setCheckedCheckboxs(
                checkBoxNamesList, checked)

    # get names list of all checkboxs in the checkbox group which is attached to inserterButton
    def getCheckboxsName(self, inserterButtonName, enabled=True):
        widgetsInfo = self._widgets_dict.get(inserterButtonName, None)
        if not widgetsInfo: return
        if widgetsInfo[1]:
            # this button has an attachedCheckboxGroup
            attachedCheckboxGroupWidget = widgetsInfo[1]
            return attachedCheckboxGroupWidget.get_checkboxs_name()
        return None

    def __init__(self, config, idName, displayName, groupBoxName, parent=None):
        QWidget.__init__(self, parent)
        self._inserters_modelitems = {
        }  # dict. { inserter_class_name : AnnotationModelItem, ...}. Note that, inserter_class_name is not always equal to inserter_button_name, for those buttons with attached options.
        self._inserters_guiitems = {
        }  # dict. { inserter_class_name : (inserter_creator_method, inserted_item_creator_method) }
        self._idName = idName
        self._groupBoxName = groupBoxName
        self._displayName = displayName
        self._scene = None
        self._current_toinsert_inserterClassName = None
        self._current_is_insert_mode = False
        self._widgets_dict = {
        }  # dict. { inserter_button_name :[buttonWidget, buttonAttachedOptionsWidget, buttonName], ....}

        self._setupGUI(self._groupBoxName)

        # Add label classes from config
        for buttonConfig in config:
            self.addButton(buttonConfig)

        # print "self._inserters_guiitems = {}".format(self._inserters_guiitems)

    def onModelChanged(self, new_model):
        pass

    def addButton(self, buttonConfig):
        # LOG.info("addLabelClass with buttonConfig {} ...".format(buttonConfig))
        # Check label configuration
        if 'attributes' not in buttonConfig:
            raise ImproperlyConfigured("Label with no 'attributes' dict found")

        inserter_creator_method = buttonConfig['inserter']
        inserted_item_creator_method = buttonConfig['item']

        attrs = buttonConfig['attributes']
        # LOG.info("buttonConfig['attributes'] {} type {} ...".format(buttonConfig['attributes'], type(buttonConfig['attributes'])))
        if config.METADATA_LABELCLASS_TOKEN not in attrs:
            raise ImproperlyConfigured(
                "Labels must have an attribute config.METADATA_LABELCLASS_TOKEN"
            )
        label_class = attrs[config.METADATA_LABELCLASS_TOKEN]
        # LOG.info("buttonConfig['attributes'][config.METADATA_LABELCLASS_TOKEN] {} type {} ...".format(attrs[config.METADATA_LABELCLASS_TOKEN], type(attrs[config.METADATA_LABELCLASS_TOKEN])))
        if label_class in self._inserters_modelitems:
            raise ImproperlyConfigured(
                "Label with class '%s' defined more than once" % label_class)

        # Add INSERTER button
        displaytext = attrs['displaytext']
        buttonName = label_class
        button = QPushButton(displaytext, self)

        optionInfo = attrs.get('optioninfo', None)
        # print "button {}: option {}".format(buttonName, optionInfo)
        buttonOptionsWidget = None
        buttonDisplayColor = None
        tmp = [
            o.get(default_config.METADATA_DISPLAYCOLOR_TOKEN, None)
            for o in optionInfo['option']
            if o.get(config.METADATA_IS_DEFAULT_TOKEN, False)
        ][0] if optionInfo else None
        buttonDisplayColor = tmp if tmp else optionInfo['option'][0].get(
            default_config.METADATA_DISPLAYCOLOR_TOKEN,
            None) if optionInfo else attrs.get(
                default_config.METADATA_DISPLAYCOLOR_TOKEN, None)

        # LOG.info(u"buttonConfig['attributes'] = {}, displaytext = {}, displayColor = {}".format(attrs, displaytext, buttonDisplayColor))

        # ==== zx add @ 20161114 to display button with color configured by user ====
        txtColor = None
        if buttonDisplayColor is not None:
            qtColor, hexColor = utils.getColorDesc(buttonDisplayColor)
            rgba = utils.hexColorStrToRGBA(hexColor)
            distance = math.sqrt((rgba[0] - 255)**2 + (rgba[1] - 255)**2 +
                                 (rgba[2] - 255)**2)
            txtColor = '#000000' if distance > config.GUI_COLOR_TAG_TEXT_BLACKWHITE_TOGGLE_THRESHOLD else '#ffffff'
            buttonDisplayColor = hexColor[0:8]
            # LOG.info(u"buttonDisplayColor = {} txtColor = {}, qtColor = {} hexColor = {}".format(buttonDisplayColor, txtColor, qtColor, hexColor ))
            # print (u"buttonDisplayColor = {} txtColor = {}, qtColor = {} hexColor = {}".format(buttonDisplayColor, txtColor, qtColor, hexColor ))

        # print "button {} buttonDisplayColor {} ...".format(buttonName, buttonDisplayColor)
        utils.set_qobj_stylesheet(
            button,
            'QPushButton',
            widgetBackgroundColor=None,
            widgetTextColor=None,
            widgetBackgroundColorWhenChecked=buttonDisplayColor,
            widgetTextColorWhenChecked=txtColor)
        # ========================== zx add end ============================

        button.clicked.connect(bind(self.onClassButtonPressed, label_class))
        # Add hotkey
        if 'hotkey' in buttonConfig:
            hotkey = QShortcut(QKeySequence(buttonConfig['hotkey']), self)
            hotkey.activated.connect(button.click)
            self._class_shortcuts[label_class] = hotkey
            # print "{} is set hotkey {} {}".format(label_class, buttonConfig['hotkey'], hotkey)

        if optionInfo:
            optionListName = optionInfo['name']
            optionListText = optionInfo['displaytext']
            option = optionInfo['option']
            buttonOptionsWidget = AttachedCheckboxGroupWidget(
                buttonName,
                optionListName,
                optionListText,
                True,
                option,
                self._optionsCheckboxStateChangedSignal,
                parent=None)

            isDefaultOption = False
            for o in option:
                new_class = o.get('tag', None)
                if new_class:
                    # Add prototype mdoelItem for insertion
                    mi = {config.METADATA_LABELCLASS_TOKEN: new_class}
                    mi['displaytext'] = o.get('displaytext', new_class)

                    self._inserters_modelitems[
                        new_class] = AnnotationModelItem(mi)
                    self._inserters_guiitems[new_class] = (
                        inserter_creator_method, inserted_item_creator_method)
                    # print "addButton.....self._inserters_guiitems[{}] = {}".format(new_class, (inserter_creator_method, inserted_item_creator_method))
                    for key, val in o.iteritems():
                        if key != 'tag':
                            self._inserters_modelitems[new_class][key] = val

        else:
            attrs = buttonConfig['attributes']

            # Add prototype mdoelItem for insertion
            mi = {config.METADATA_LABELCLASS_TOKEN: label_class}
            mi['displaytext'] = attrs.get('displaytext', label_class)

            self._inserters_modelitems[label_class] = AnnotationModelItem(mi)
            self._inserters_guiitems[label_class] = (
                inserter_creator_method, inserted_item_creator_method)

            # update their values
            for key, val in attrs.iteritems():
                self._inserters_modelitems[label_class][key] = val
                # LOG.info("self._inserters_modelitems[{}][{}] = {}".format(label_class, key, val))

        self._widgets_dict[label_class] = [button, buttonOptionsWidget]
        # print "self._widgets_dict [ {} ] = {}".format(label_class, button)

        button.setCheckable(True)

        utils.set_qobj_stylesheet(
            self._widgets_dict[label_class][0],
            'QPushButton',
            widgetBackgroundColor=None,
            widgetTextColor=None,
            widgetBackgroundColorWhenChecked=buttonDisplayColor,
            widgetTextColorWhenChecked=txtColor)

        if buttonOptionsWidget:
            # self._layout.addWidget(button)
            self._inserterButtonGroup_layout.addWidget(button)
            self._layout.addWidget(buttonOptionsWidget)
        else:
            self._inserterButtonGroup_layout.addWidget(button)

    def onClassButtonPressed(self, pressedButtonName):
        # print "onClassButtonPressed ... button {} isChecked !".format(pressedButtonName)
        inserterClassName = pressedButtonName

        if pressedButtonName not in self._widgets_dict.keys():
            # check whether passed-in pressedButtonName argument is an checkbox option name
            for kk, vv in self._widgets_dict.iteritems():
                buttonOptionsWidget = vv[1] if vv else None
                if buttonOptionsWidget:
                    optionsName = buttonOptionsWidget.get_checkboxs_name()
                    if pressedButtonName in optionsName:
                        pressedButtonName = kk
                        # print "pressedButtonName ============ {}".format(kk)

        if self._widgets_dict[pressedButtonName][0].isChecked():
            if not self._scene._image_item:
                self._widgets_dict[pressedButtonName][0].setChecked(False)
                return

            checkedOption = None
            if self._widgets_dict[pressedButtonName][1]:
                buttonOptionsWidget = self._widgets_dict[pressedButtonName][1]
                checkedOptionsNameList = buttonOptionsWidget.get_checked_widgets_name(
                )
                if not checkedOptionsNameList:
                    checkedOptionsNameList = [
                        buttonOptionsWidget._defaultCheckboxName
                    ]
                    buttonOptionsWidget.setCheckedCheckbox(
                        buttonOptionsWidget._defaultCheckboxName)

                buttonOptionsWidget.enableAll()
                checkedOptionName = checkedOptionsNameList[0]

                orgClsNames = [
                    i for i in buttonOptionsWidget.get_checkboxs_name()
                    if i in self._inserters_modelitems.keys()
                ]
                if (not orgClsNames):
                    raise RuntimeError(
                        "There are no or more than one inserters")
                inserterClassName = checkedOptionName

            if ((not self._scene._labeltool._enableAutoConnectLabelMode)
                    and (self._scene._selectedDisplayItemsList is not None)
                    and (len(self._scene._selectedDisplayItemsList) == 1)):
                parentModelItem = self._scene._selectedDisplayItemsList[0][
                    annotationscene.
                    SELECTED_DISPLAYED_ITEMS_LIST_MODELITEM_INDEX]
                clsNameOfChild = inserterClassName
                if ((clsNameOfChild == config.ANNOTATION_PERSONBIKE_TOKEN) or
                    (clsNameOfChild == config.ANNOTATION_PEDESTRAIN_TOKEN) or
                    (clsNameOfChild == config.ANNOTATION_VEHICLE_TOKEN)):
                    self._scene._selectedDisplayItemsList = []
                    parentModelItem = None

            elif (self._scene._sceneViewMode == config.OBJ_VIEW_MODE) and (
                    self._scene._objViewModeTopModelItem is not None):
                parentModelItem = self._scene._objViewModeTopModelItem
            else:
                parentModelItem = None

            LOG.info(
                "onClassButtonPressed ... self._scene._labeltool._enableAutoConnectLabelMode = {} parentModelItem = {}"
                .format(self._scene._labeltool._enableAutoConnectLabelMode,
                        parentModelItem))
            if not self._scene._labeltool._enableAutoConnectLabelMode:

                clsNameOfChild = inserterClassName
                isValid, rectBoundary = self._scene.checkModelItemValidity(
                    clsNameOfChild,
                    None,
                    parentModelItem,
                    self._scene._image_item,
                    enablePopupMsgBox=True,
                    enableEmitStatusMsg=False,
                    enableCountingThisModelItem=True)

                if not isValid:
                    LOG.info("enter hhhhhhhhhhhhhhh....")

                    # --------------------------------------
                    # added by zx @ 2017-02-08
                    # to exit all inserters among all pannels
                    # --------------------------------------
                    for i in self._scene._labeltool.propertyeditors():
                        if i:
                            i.setCheckedAll(False)
                    # self._scene._labeltool.exitInsertMode()
                    self._scene.deselectAllItems()
                    # --------------------------------------

                    return

            LOG.info("onClassButtonPressed ... call startInsertionMode...")

            self.startInsertionMode(pressedButtonName, inserterClassName)

        else:

            LOG.info("onClassButtonPressed ... call endInsertionMode...")
            self.endInsertionMode()

        return

    def startInsertionMode(self, pressedButtonName, inserterClassName):
        self.endInsertionMode(False)
        LOG.info(
            "Starting insertion mode for {} .. self._inserters_modelitems[{}]={} "
            .format(inserterClassName, inserterClassName,
                    self._inserters_modelitems[inserterClassName]))

        for lc, buttonAndOption in self._widgets_dict.items():
            buttonAndOption[0].setChecked(lc == pressedButtonName)
            if buttonAndOption[1]:
                # print ("startInsertionMode .. setchecked for {} option {} enabled = {} ".format(lc, buttonAndOption[1], (lc == pressedButtonName)))
                buttonAndOption[1].enableAll(lc == pressedButtonName)

            LOG.info(
                "startInsertionMode .. setchecked for {} button checked = {} ".
                format(lc, lc == inserterClassName))
            # print ("startInsertionMode .. setchecked for {} button {} checked = {} ".format(lc, buttonAndOption[0], lc == inserterClassName))

        self._current_toinsert_inserterClassName = inserterClassName
        # print "==== startInsertionMode set _current_is_insert_mode False ..."
        self._current_is_insert_mode = True

        LOG.info(
            u"startInsertionMode .. emit insertionModeStarted(pannel = {}, inserter = {})... "
            .format(self._idName, inserterClassName))

        self.insertionModeStarted.emit(self._idName, inserterClassName,
                                       inserterClassName)

    def endInsertionMode(self, uncheck_buttons=True):
        if uncheck_buttons:
            self.setCheckedAll(False)

        LOG.info(
            u"endInsertionMode... PropertyEditor {} endInsertionMode(uncheck_buttons = {})"
            .format(self._displayName, uncheck_buttons))
        # print (u"endInsertionMode... PropertyEditor {} endInsertionMode(uncheck_buttons = {})".format(self._displayName, uncheck_buttons))
        self._current_is_insert_mode = False
        self.insertionModeEnded.emit()

    def enableAll(self, enabled=True):
        for v, buttonAndOption in self._widgets_dict.items():
            buttonAndOption[0].setEnabled(enabled)

    def setCheckedAll(self, checked=True):
        for v, buttonAndOption in self._widgets_dict.items():
            buttonAndOption[0].setChecked(checked)

    def getChecked(self):
        buttonname = None
        for buttonName, buttonWidgets in self._widgets_dict.iteritems():
            if buttonWidgets[0].isChecked():
                return buttonName
        return buttonname

    def setChecked(self, buttonName, checked=True):
        buttonWidget = self._widgets_dict.get(buttonName, None)
        if buttonWidget is not None:
            buttonWidget[0].setChecked(checked)

    def enable(self, buttonName, enabled=True):
        buttonWidget = self._widgets_dict.get(buttonName, None)
        if buttonWidget is not None:
            buttonWidget[0].setEnabled(enabled)

    def markEditButtons(self, buttonNamesList):
        for lc, buttonAndOption in self._widgets_dict.items():
            # buttonAndOption[0].setFlat(lc not in buttonNamesList)
            buttonAndOption[0].setChecked(lc in buttonNamesList)

    def updateCurrentInserter(self, inserterClassName):
        self._current_toinsert_inserterClassName = inserterClassName
        return

    def isInsertingMode(self):
        return self._current_is_insert_mode

    def currentToInsertItemProperty(self):
        return self._inserters_modelitems.get(
            self._current_toinsert_inserterClassName,
            {}) if self._current_toinsert_inserterClassName else {}

    # return [inserter0ClassName, inserter1ClassName, ...]
    def getSupportedInserters(self):
        return self._inserters_modelitems.keys()

    def startEditMode(self, model_items):
        # If we're in insertion mode, ignore empty edit requests
        if self._current_is_insert_mode and len(model_items) == 0:
            return

        self.endInsertionMode()
        LOG.info("Starting edit mode for model_items: {} ".format(model_items))
        self._current_toinsert_inserterClassName = None
        # print "==== startEditMode set _current_is_insert_mode False ..."
        self._current_is_insert_mode = False

        labelClasses = set([
            item[config.METADATA_LABELCLASS_TOKEN] for item in model_items
            if config.METADATA_LABELCLASS_TOKEN in item
        ])
        self.markEditButtons(labelClasses)

    def _setupGUI(self, _groupBoxName):
        self._widgets_dict = {}
        self._class_shortcuts = {}

        # Label class buttons
        qss = "QGroupBox::title {subcontrol-origin: margin; subcontrol-position: top left; padding: 0 3px; color : red; font-weight:bold; }"
        self._inserterButtonGroupbox = QGroupBox(_groupBoxName, self)
        self._inserterButtonGroupbox.setStyleSheet(qss)
        self._inserterButtonGroup_layout = FloatingLayout()
        self._inserterButtonGroupbox.setLayout(
            self._inserterButtonGroup_layout)

        # Global widget
        self._layout = MyVBoxLayout()
        self.setLayout(self._layout)
        self._layout.addWidget(self._inserterButtonGroupbox, 0)
        self._layout.addStretch(1)

    def onInserterButtonOptionChanged(self, _inserterButtonName,
                                      checkboxsInfo):
        # print "onInserterButtonOptionChanged {}: inserterButton {} checkboxinfo {}".format(self._idName, _inserterButtonName, checkboxsInfo)
        if not checkboxsInfo:
            return
        if len(checkboxsInfo) != 2:
            return

        inserterButtonName = str(_inserterButtonName)

        checkedWidgetsAttrDescDict = checkboxsInfo[0]
        allCheckboxsNameList = checkboxsInfo[1]

        if len(checkedWidgetsAttrDescDict) != 1:
            return

        checkedOptionName = checkedWidgetsAttrDescDict.keys()[0]

        if (self._current_toinsert_inserterClassName == checkedOptionName):
            return

        checkedOptionDisplayText = checkedWidgetsAttrDescDict.values()[0][
            attrArea.checkedWidgetsAttrDescList_checkedWidgetText_idx]
        checkedOptionDisplayColor = checkedWidgetsAttrDescDict.values()[0][
            attrArea.checkedWidgetsAttrDescList_checkedWidgetColor_idx]

        # change inserter button displaycolor
        # print "--- self._current_is_insert_mode = {} self._widgets_dict[{}][0].is_checked() = {} ...".format(self._current_is_insert_mode, inserterButtonName, self._widgets_dict[inserterButtonName][0].isChecked())
        utils.set_qobj_stylesheet(
            self._widgets_dict[inserterButtonName][0],
            'QPushButton',
            widgetBackgroundColor=None,
            widgetTextColor=None,
            widgetBackgroundColorWhenChecked=checkedOptionDisplayColor,
            widgetTextColorWhenChecked=None)

        if not self._current_is_insert_mode or self._scene._sceneViewMode == config.OBJ_VIEW_MODE:
            parentModelItem = None

            if self._scene._sceneViewMode == config.OBJ_VIEW_MODE:
                # print "_sceneViewMode = {} _objViewModeTopModelItem = {}".format(self._scene._sceneViewMode, self._scene._objViewModeTopModelItem)
                parentModelItem = self._scene._objViewModeTopModelItem

            if not parentModelItem:
                # get current scene's parent modelitem
                if len(self._scene._selectedDisplayItemsList) > 0:
                    parentModelItem = self._scene._selectedDisplayItemsList[0][
                        annotationscene.
                        SELECTED_DISPLAYED_ITEMS_LIST_MODELITEM_INDEX]
                    # print "_selectedDisplayItemsList = {}".format(self._scene._selectedDisplayItemsList)

            if not parentModelItem:
                parentModelItem = self._scene._labeltool._current_image

            # print ("checkedOptionName = {} parentModelItem = {}".format(checkedOptionName, GET_ANNOTATION(parentModelItem)))
            # print ("allCheckboxsNameList = {}".format((allCheckboxsNameList)))

            # find all modelItems which has class which in is the option list of changed inserter option but not has not that class of option
            foundNum = 0
            foundListList = []
            for a in allCheckboxsNameList:
                foundList = model.findModelItemsWithSpecificClassNameFromRoot(
                    a, parentModelItem, 5)
                # print "find {} => {}".format(a, foundList)
                foundNum += len(foundList)  # if a != checkedOptionName else 0
                maxNum = 1
                if foundNum > maxNum:
                    print "Error: there are {} items of {} in current image! We can only modify at least {} item once!".format(
                        foundNum, allCheckboxsNameList, maxNum)
                    foundListList = []
                    break
                if a != checkedOptionName:
                    foundListList += foundList

            # print ("foundListList = {}".format((foundListList)))
            # update found modelitems' class field to current selected option
            if foundListList:
                for found in foundListList:
                    if not found: continue
                    modelItem = found[0]
                    iterativeLevelNum = found[1]
                    # print "ModelItem is changed from {} => {}!".format(modelItem.get_label_name(), checkedOptionName)
                    modelItem.update({'class': checkedOptionName},
                                     needEmitDatachanged=True)
                    sceneItem = self._scene.modelItem_to_baseItem(modelItem)

                    # print u"checkedOptionDisplayText = {} checkedOptionDisplayColor = {}".format(checkedOptionDisplayText, checkedOptionDisplayColor)
                    sceneItem.updateInfo(checkedOptionDisplayText,
                                         checkedOptionDisplayColor)

                    # newModelItem = copy.deepcopy(oldModelItem)
                    # self._scene.deleteItems(self, [oldModelItem], recursiuve=True)
        else:

            # switch to new inserter as per current selected inserter button type
            # print "---------------- _current_is_insert_mode = {} ...".format(self._current_is_insert_mode)
            # print "call onInsertionModeStarted with {} {}...".format(checkedOptionName, inserterButtonName)
            self.updateCurrentInserter(checkedOptionName)
            self._scene.onInsertionModeStarted(self._idName, checkedOptionName,
                                               inserterButtonName)

        return

    def onInsertionModeStarted(self, _classNameToInsert,
                               _buttonNameOfInserter):
        buttonNameOfInserter = str(_buttonNameOfInserter)
        self.setChecked(buttonNameOfInserter, True)
        self._current_is_insert_mode = True
        self.updateCurrentInserter(str(_classNameToInsert))
        return
    def __init__(self,
                 stateChangedSignalSlot=None,
                 property=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)

        self.hotkeys = []
        self.name = displayName

        self.vlayout = MyVBoxLayout()  #QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)

        if stateChangedSignalSlot is not None:
            self.stateChanged.connect(stateChangedSignalSlot)

        # LOG.info("AttrAreaWidget constructor : property {} ...".format(property))
        self.numGroups = len(property) if property is not None else 0

        self.buttonGroupsDescDict = {}
        self.buttonGroupWidgets = {}

        for i in xrange(self.numGroups):
            thisGroupProperty = property[i]  # thisGroupProperty is a dict
            thisGroupName = thisGroupProperty.get(
                'name')  # thisGroupName is a string
            thisGroupOption = thisGroupProperty.get(
                'option'
            )  # thisGroupOption is a tuple of dicts. each dict describes a checkbox
            thisGroupText = thisGroupProperty.get('displaytext')
            thisGroup
            isExclusive = False if thisGroupName in config.NON_EXCLUSIVE_ATTRS_TAG_LIST else True

            if thisGroupName == config.ANNOTATION_PERSONBIKE_TYPE_GROUP_TOKEN:
                checkboxWidgetMinWidthInPixels = 52
            else:
                checkboxWidgetMinWidthInPixels = 38

            thisGroupWidget = checkBoxListWidget(thisGroupName, thisGroupText,
                                                 isExclusive, thisGroupOption,
                                                 self.stateChanged)
            if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(config.MAX_UPPER_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_UPPER_COLOR_WIDGET_NUMBER)
            elif thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(config.MAX_LOWER_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_LOWER_COLOR_WIDGET_NUMBER)
            elif thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(
                    config.MAX_VEHICLE_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_VEHICLE_COLOR_WIDGET_NUMBER)
            else:
                thisGroupWidget.setMaxCheckedNum(
                    config.MAX_CHECKED_OPTIONS_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.MAX_OPTIONS_NUMBER)

            thisGroupCheckboxs = {}
            optCnt = 0
            for checkboxOption in thisGroupOption:

                thisCheckboxName = checkboxOption[
                    config.METADATA_ATTR_VALUE_TOKEN]
                thisCheckboxText = checkboxOption[
                    config.METADATA_DISPLAYTEXT_TOKEN]

                thisCheckboxProperty = checkboxOption.copy()

                # get widget display color
                if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                    thisCheckboxBkgColorChecked = thisCheckboxProperty[
                        config.COLOR_ATTR_RGB_VALUE_TOKEN]
                    thisCheckboxBkgColor = thisCheckboxBkgColorChecked
                else:
                    thisCheckboxBkgColorChecked = thisCheckboxProperty[
                        config.METADATA_DISPLAYCOLOR_TOKEN]
                    thisCheckboxBkgColor = None

                # calc widget text color
            # thisCheckboxBkgColorChecked string pattern: #123456
                txtColorChecked = None
                if thisCheckboxBkgColorChecked is not None:
                    import math
                    rgba = utils.hexColorStrToRGBA(thisCheckboxBkgColorChecked)
                    distance = math.sqrt((rgba[0] - 255)**2 +
                                         (rgba[1] - 255)**2 +
                                         (rgba[2] - 255)**2)
                    txtColorChecked = '#ffffff' if distance > config.GUI_COLOR_TAG_TEXT_BLACKWHITE_TOGGLE_THRESHOLD else '#000000'

                txtColor = txtColorChecked if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN else None

                thisGroupCheckboxs[
                    thisCheckboxName] = thisCheckboxProperty  # add checkbox record to this checkbox group record

                # add widget to this group
                if optCnt < thisGroupWidget._maxSeperateWidgetNum:
                    thisGroupWidget.add_checkbox(
                        thisCheckboxName, thisCheckboxText,
                        thisCheckboxBkgColor, txtColor,
                        thisCheckboxBkgColorChecked, txtColorChecked,
                        checkboxWidgetMinWidthInPixels)
                else:
                    thisGroupWidget.add_popup_button(
                        config.GUI_MORE_WIDGET_DISPLAYTEXT,
                        thisCheckboxName,
                        thisCheckboxText,
                        popupbutton_background_color="#808080",
                        popupbutton_text_color="#ffffff",
                        checkbox_background_color=thisCheckboxBkgColor,
                        checkbox_text_color=txtColor)

                optCnt += 1

            # thisGroupWidget.add_checkbox("Unset", u"Unset")

            thisGroupWidget.setLayout(thisGroupWidget.flayout)
            self.vlayout.addWidget(thisGroupWidget)

            self.checkboxGroupsDescDict[
                thisGroupName] = thisGroupCheckboxs  # add this checkbox group record to checkbox groups recrod
            self.checkboxGroupWidgets[thisGroupName] = thisGroupWidget

        # LOG.info("checkboxGroupWidgets = {} checkboxGroupsDescDict = {}".format(self.checkboxGroupWidgets, self.checkboxGroupsDescDict)

        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return
class buttonWithOptionsWidget(QWidget):
    def __init__(self,
                 annotation_scene,
                 stateChangedSignalSlot=None,
                 buttonWithOptionsProperty=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)
        # print "buttonWithOptionsProperty = {}".format(comboPannelProperty)
        self.buttonWithOptionsProperty = buttonWithOptionsProperty

        self.name = displayName

        self.vlayout = QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)
        self.vlayout.addWidget(self.propertyEditorWidget)
        self.vlayout.addWidget(self.attrAreaWidget)
        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return

    def enableCheckbox(self, checkboxGroupName, checkboxName, enabled=True):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.enable(checkboxName, enabled)

    def setCheckedCheckbox(self, checkboxName, checked=True):
        self.checkboxGroupWidget.setCheckedCheckbox(checkboxName, checked)

    def setCheckedCheckboxs(self, checkboxNamesList, checked=True):
        if checkboxNamesList is None:
            return
        groupWidget = self.checkboxGroupWidget.get(checkboxGroupName, None)
        if groupWidget is not None:
            # LOG.info(u"{} AttrAreaWidget.setCheckedCheckbox for group {} box {} checked {} ..".format(self.name, checkboxGroupName, checkboxNamesList, checked))
            groupWidget.setCheckedCheckboxs(checkboxNamesList, checked)

    def sendCheckedSignal(self, checkboxGroupName):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            groupWidget.sendCheckedSignal()

    def getCheckedWidgetsOptionInfo(self, checkboxGroupName):
        groupWidget = self.checkboxGroupWidgets.get(checkboxGroupName, None)
        if groupWidget is not None:
            return groupWidget.getCheckedWidgetsOptionInfo()
        return None, None

    def __init__(self,
                 stateChangedSignalSlot=None,
                 property=None,
                 displayName=None,
                 parent=None):
        QWidget.__init__(self, parent)

        self.hotkeys = []
        self.name = displayName

        self.vlayout = MyVBoxLayout()  #QVBoxLayout()
        self.vlayout.setAlignment(Qt.AlignTop)
        self.vlayout.setSpacing(4)
        self.vlayout.setMargin(4)
        #self.vlayout.setContentsMargins(0, 0, 0, 44)

        if stateChangedSignalSlot is not None:
            self.stateChanged.connect(stateChangedSignalSlot)

        # LOG.info("AttrAreaWidget constructor : property {} ...".format(property))
        self.numGroups = len(property) if property is not None else 0

        self.buttonGroupsDescDict = {}
        self.buttonGroupWidgets = {}

        for i in xrange(self.numGroups):
            thisGroupProperty = property[i]  # thisGroupProperty is a dict
            thisGroupName = thisGroupProperty.get(
                'name')  # thisGroupName is a string
            thisGroupOption = thisGroupProperty.get(
                'option'
            )  # thisGroupOption is a tuple of dicts. each dict describes a checkbox
            thisGroupText = thisGroupProperty.get('displaytext')
            thisGroup
            isExclusive = False if thisGroupName in config.NON_EXCLUSIVE_ATTRS_TAG_LIST else True

            if thisGroupName == config.ANNOTATION_PERSONBIKE_TYPE_GROUP_TOKEN:
                checkboxWidgetMinWidthInPixels = 52
            else:
                checkboxWidgetMinWidthInPixels = 38

            thisGroupWidget = checkBoxListWidget(thisGroupName, thisGroupText,
                                                 isExclusive, thisGroupOption,
                                                 self.stateChanged)
            if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(config.MAX_UPPER_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_UPPER_COLOR_WIDGET_NUMBER)
            elif thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(config.MAX_LOWER_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_LOWER_COLOR_WIDGET_NUMBER)
            elif thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                thisGroupWidget.setMaxCheckedNum(
                    config.MAX_VEHICLE_COLOR_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.GUI_MAX_SEPERATE_VEHICLE_COLOR_WIDGET_NUMBER)
            else:
                thisGroupWidget.setMaxCheckedNum(
                    config.MAX_CHECKED_OPTIONS_NUMBER)
                thisGroupWidget.setMaxSeperateWidgetNum(
                    config.MAX_OPTIONS_NUMBER)

            thisGroupCheckboxs = {}
            optCnt = 0
            for checkboxOption in thisGroupOption:

                thisCheckboxName = checkboxOption[
                    config.METADATA_ATTR_VALUE_TOKEN]
                thisCheckboxText = checkboxOption[
                    config.METADATA_DISPLAYTEXT_TOKEN]

                thisCheckboxProperty = checkboxOption.copy()

                # get widget display color
                if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN:
                    thisCheckboxBkgColorChecked = thisCheckboxProperty[
                        config.COLOR_ATTR_RGB_VALUE_TOKEN]
                    thisCheckboxBkgColor = thisCheckboxBkgColorChecked
                else:
                    thisCheckboxBkgColorChecked = thisCheckboxProperty[
                        config.METADATA_DISPLAYCOLOR_TOKEN]
                    thisCheckboxBkgColor = None

                # calc widget text color
            # thisCheckboxBkgColorChecked string pattern: #123456
                txtColorChecked = None
                if thisCheckboxBkgColorChecked is not None:
                    import math
                    rgba = utils.hexColorStrToRGBA(thisCheckboxBkgColorChecked)
                    distance = math.sqrt((rgba[0] - 255)**2 +
                                         (rgba[1] - 255)**2 +
                                         (rgba[2] - 255)**2)
                    txtColorChecked = '#ffffff' if distance > config.GUI_COLOR_TAG_TEXT_BLACKWHITE_TOGGLE_THRESHOLD else '#000000'

                txtColor = txtColorChecked if thisGroupName == config.ANNOTATION_UPPER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_LOWER_COLOR_TOKEN or thisGroupName == config.ANNOTATION_VEHICLE_COLOR_TOKEN else None

                thisGroupCheckboxs[
                    thisCheckboxName] = thisCheckboxProperty  # add checkbox record to this checkbox group record

                # add widget to this group
                if optCnt < thisGroupWidget._maxSeperateWidgetNum:
                    thisGroupWidget.add_checkbox(
                        thisCheckboxName, thisCheckboxText,
                        thisCheckboxBkgColor, txtColor,
                        thisCheckboxBkgColorChecked, txtColorChecked,
                        checkboxWidgetMinWidthInPixels)
                else:
                    thisGroupWidget.add_popup_button(
                        config.GUI_MORE_WIDGET_DISPLAYTEXT,
                        thisCheckboxName,
                        thisCheckboxText,
                        popupbutton_background_color="#808080",
                        popupbutton_text_color="#ffffff",
                        checkbox_background_color=thisCheckboxBkgColor,
                        checkbox_text_color=txtColor)

                optCnt += 1

            # thisGroupWidget.add_checkbox("Unset", u"Unset")

            thisGroupWidget.setLayout(thisGroupWidget.flayout)
            self.vlayout.addWidget(thisGroupWidget)

            self.checkboxGroupsDescDict[
                thisGroupName] = thisGroupCheckboxs  # add this checkbox group record to checkbox groups recrod
            self.checkboxGroupWidgets[thisGroupName] = thisGroupWidget

        # LOG.info("checkboxGroupWidgets = {} checkboxGroupsDescDict = {}".format(self.checkboxGroupWidgets, self.checkboxGroupsDescDict)

        self.vlayout.addStretch(1)
        self.setLayout(self.vlayout)
        return

    def stateHasChanged(self, checkedWidgetsAttrDescDict):
        # LOG.info("AttrAreaWidget.stateChanged with {}".format(checkedWidgetsAttrDescDict))
        return

    def get_checked_widgets_name(self, group_name):
        widget = self.checkboxGroupWidgets.get(str(group_name), None)
        # LOG.info("AttrAreaWidget.get_checked_widgets_name ... widget = {}".format(widget))
        checkedWidgetsNameList = widget.get_checked_widgets_name(
        ) if widget is not None else None
        # LOG.info("AttrAreaWidget.get_checked_widgets_name ... checkedWidget = {}".format(checkedWidget))
        return checkedWidgetsNameList

    def add_hotkey(self, choice, name, hotkey):
        self.hotkeys.append((choice, name, hotkey))

    def get_checked_widgets_attrDesc(self):
        descsDict = {}
        for widgetName, widgetInfo in self.checkboxWidgetsInfo.items():
            if widgetInfo[0].isChecked():
                desc = [None, None, None, None]
                desc[
                    checkedWidgetsAttrDescList_checkedWidget_idx] = widgetInfo[
                        0]
                desc[
                    checkedWidgetsAttrDescList_checkedWidgetText_idx] = widgetInfo[
                        1]
                desc[
                    checkedWidgetsAttrDescList_checkedWidgetStyleSheet_idx] = widgetInfo[
                        2]
                descsDict[widgetName] = desc

        for widgetInfo in self.popupWidgetsInfo.values():
            for actionWidget in widgetInfo[
                    popupWidgetsInfo_actionsWidgetList_idx]:
                if actionWidget.isChecked():
                    idx = widgetInfo[
                        popupWidgetsInfo_actionsWidgetList_idx].index(
                            actionWidget)
                    desc = [None, None, None, None]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidget_idx] = actionWidget
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetText_idx] = widgetInfo[
                            popupWidgetsInfo_actionsTextList_idx][idx]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetStyleSheet_idx] = widgetInfo[
                            popupWidgetsInfo_actionsWidgetStyleSheet_idx]
                    desc[
                        checkedWidgetsAttrDescList_checkedWidgetPopupParentWidget_idx] = widgetInfo[
                            popupWidgetsInfo_pushButtonWidget_idx]
                    widgetName = widgetInfo[
                        popupWidgetsInfo_actionsNameList_idx][idx]
                    descsDict[widgetName] = desc

        return descsDict

    def startEditMode(self, model_items):
        return