def _add_cookie_callback(self):
        if self.stage.live:
            cookie_idx = self.cookie_select.currentIndex()
            pos_idx = self.pos_select.currentIndex()

            self.logger.info(
                'Adding cookie {0} to the recipe at {1}'.format(self.icings[cookie_idx], self.positions[pos_idx]))
            self.recipe.add_cookie(
                {'icing': self.icings[cookie_idx]}, self.positions[pos_idx])

            image = self.q_image_displays[pos_idx]

            scene = QGraphicsScene()
            scene.addPixmap(
                QPixmap(os.path.join(DATA_DIR, self.pattern_images[cookie_idx].value)))
            image.setScene(scene)
            image.fitInView(scene.itemsBoundingRect())

            image.show()

        else:
            self.logger.info(
                'Stage is dead, cannot do anything.  Please exit.')
Beispiel #2
0
class SelectByColorDlg(QDialog):
    def __init__(self, color, pieceItems):
        super().__init__()
        self._color = color
        self.resize(800, 800)

        self.scene = QGraphicsScene()
        layout = QVBoxLayout(self)
        self.view = _MyView(self)
        layout.addWidget(self.view)
        self.view.setScene(self.scene)
        self._view_widget = QGLWidget()
        self.view.setViewport(self._view_widget)
        self.view.setMouseTracking(True)

        gry = sum(color) / 3.0
        if (gry < 168):
            bg = [224,]*3
        else:
            bg = [128,]*3
        self.scene.setBackgroundBrush(QBrush(QColor(*bg)))

        self.setup_scene(self.scene, pieceItems)
        d = self.piece_diag
        self.view.scale(60./d, 60./d)

        self._hover_radius = 1.0
        self.selected_item_ids = []

    def setup_scene(self, scene, pieceItems):
        root = self._rootitem = _RootItem(self._color)
        scene.addItem(root)
        # order piece items by color distance of the most similar color
        pairs = [(self._distance(item), item) for item in pieceItems]
        pairs.sort(key = lambda pair: pair[0])

        phi_increment = 0.61803398874 * 2 * pi
        phi = 0.0

        gamma = 1.0
        # scale distance relative to average piece size
        if pairs:
            rect = pairs[0][1].boundingRect()
            diag = (rect.width()**2 + rect.height()**2) ** 0.5
            # take the median piece
            idx = len(pairs) // 2
            meddist = pairs[idx][0]
            meddist = max(meddist, .1)
            scale = 6*diag/(meddist) ** gamma
            self.piece_diag = diag
        else:
            scale = 1.0
            self.piece_diag = 1.0

        self.dist_and_id = []
        for distance, orig_item in pairs:
            item = orig_item.copy_to(root, rotate=False)
            rr = item.boundingRect()
            item.setTransformOriginPoint(rr.center())
            item.setRotation(item.angle_deg)


            item.setPos(
                scale * distance**gamma * cos(phi)-rr.width()*0.5,
                scale * distance**gamma * sin(phi)-rr.height()*0.5
            )
            self.dist_and_id.append((scale*distance**gamma, item.id))
            phi += phi_increment
        self.updateSceneRect()

    def set_hover_radius(self, r):
        self._hover_radius = r
        self.selected_item_ids = [pair[1] for pair in self.dist_and_id if pair[0] < r]
        self._rootitem.sel_size = r
        self._rootitem.update()

    def confirm_result(self):
        self.accept()

    def _distance(self, pieceItem):
        color = self._color
        def dst(c1, c2):
            c1 = rgb2Lab(c1)
            c2 = rgb2Lab(c2)
            return sum((c1i-c2i)**2 for c1i, c2i in zip(c1, c2)) ** 0.5
        if not pieceItem.dominant_colors:
            return 255.0
        return min(dst(color, piececolor) for piececolor in pieceItem.dominant_colors[:4])

    def updateSceneRect(self):
        r = self.scene.itemsBoundingRect()
        w, h = r.width(), r.height()
        a = .05
        r = r.adjusted(-w*a, -h*a, w*a, h*a)
        self.scene.setSceneRect(r)
Beispiel #3
0
class OWQualityControl(widget.OWWidget):
    name = "Quality Control"
    description = "Experiment quality control"
    icon = "../widgets/icons/QualityControl.svg"
    priority = 5000

    inputs = [("Experiment Data", Orange.data.Table, "set_data")]
    outputs = []

    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation",
                           dist_pcorr),
                          ("Euclidean distance",
                           dist_eucl),
                          ("Distance from Spearman correlation",
                           dist_spearman)]

    settingsHandler = SetContextHandler()

    split_by_labels = settings.ContextSetting({})
    sort_by_labels = settings.ContextSetting({})

    selected_distance_index = settings.Setting(0)

    def __init__(self, parent=None):
        super().__init__(parent)

        ## Attributes
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None
        self.base_group_index = 0

        ## GUI
        box = gui.widgetBox(self.controlArea, "Info")
        self.info_box = gui.widgetLabel(box, "\n")

        ## Separate By box
        box = gui.widgetBox(self.controlArea, "Separate By")
        self.split_by_model = itemmodels.PyListModel(parent=self)
        self.split_by_view = QListView()
        self.split_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.split_by_view.setModel(self.split_by_model)
        box.layout().addWidget(self.split_by_view)

        self.split_by_view.selectionModel().selectionChanged.connect(
            self.on_split_key_changed)

        ## Sort By box
        box = gui.widgetBox(self.controlArea, "Sort By")
        self.sort_by_model = itemmodels.PyListModel(parent=self)
        self.sort_by_view = QListView()
        self.sort_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.sort_by_view.setModel(self.sort_by_model)
        box.layout().addWidget(self.sort_by_view)

        self.sort_by_view.selectionModel().selectionChanged.connect(
            self.on_sort_key_changed)

        ## Distance box
        box = gui.widgetBox(self.controlArea, "Distance Measure")
        gui.comboBox(box, self, "selected_distance_index",
                     items=[name for name, _ in self.DISTANCE_FUNCTIONS],
                     callback=self.on_distance_measure_changed)

        self.scene = QGraphicsScene()
        self.scene_view = QGraphicsView(self.scene)
        self.scene_view.setRenderHints(QPainter.Antialiasing)
        self.scene_view.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.mainArea.layout().addWidget(self.scene_view)

        self.scene_view.installEventFilter(self)

        self._disable_updates = False
        self._cached_distances = {}
        self._base_index_hints = {}
        self.main_widget = None

        self.resize(800, 600)

    def clear(self):
        """Clear the widget state."""
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None

        with disable_updates(self):
            self.split_by_model[:] = []
            self.sort_by_model[:] = []

        self.main_widget = None
        self.scene.clear()
        self.info_box.setText("\n")
        self._cached_distances = {}

    def set_data(self, data=None):
        """Set input experiment data."""
        self.closeContext()
        self.clear()

        self.error(0)
        self.warning(0)

        if data is not None:
            keys = self.get_suitable_keys(data)
            if not keys:
                self.error(0, "Data has no suitable feature labels.")
                data = None

        self.data = data
        if data is not None:
            self.on_new_data()

    def update_label_candidates(self):
        """Update the label candidates selection GUI 
        (Group/Sort By views).

        """
        keys = self.get_suitable_keys(self.data)
        with disable_updates(self):
            self.split_by_model[:] = keys
            self.sort_by_model[:] = keys

    def get_suitable_keys(self, data):
        """ Return suitable attr label keys from the data where
        the key has at least two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def selected_split_by_labels(self):
        """Return the current selected split labels.
        """
        sel_m = self.split_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_sort_by_labels(self):
        """Return the current selected sort labels
        """
        sel_m = self.sort_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_distance(self):
        """Return the selected distance function.
        """
        return self.DISTANCE_FUNCTIONS[self.selected_distance_index][1]

    def selected_base_group_index(self):
        """Return the selected base group index
        """
        return self.base_group_index

    def selected_base_indices(self, base_group_index=None):
        indices = []
        for g, ind in self.groups:
            if base_group_index is None:
                label = group_label(self.selected_split_by_labels(), g)
                ind = [i for i in ind if i is not None]
                i = self._base_index_hints.get(label, ind[0] if ind else None)
            else:
                i = ind[base_group_index]
            indices.append(i)
        return indices

    def on_new_data(self):
        """We have new data and need to recompute all.
        """
        self.closeContext()

        self.update_label_candidates()
        self.info_box.setText(
            "%s genes \n%s experiments" %
            (len(self.data),  len(self.data.domain.attributes))
        )

        self.base_group_index = 0

        keys = self.get_suitable_keys(self.data)
        self.openContext(keys)

        ## Restore saved context settings (split/sort selection)
        split_by_labels = self.split_by_labels
        sort_by_labels = self.sort_by_labels

        def select(model, selection_model, selected_items):
            """Select items in a Qt item model view
            """
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            for ind in indices:
                selection_model.select(model.index(ind),
                                       QItemSelectionModel.Select)

        with disable_updates(self):
            select(self.split_by_view.model(),
                   self.split_by_view.selectionModel(),
                   split_by_labels)

            select(self.sort_by_view.model(),
                   self.sort_by_view.selectionModel(),
                   sort_by_labels)

        with widget_disable(self):
            self.split_and_update()

    def on_split_key_changed(self, *args):
        """Split key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.split_by_labels = self.selected_split_by_labels()
                self.split_and_update()

    def on_sort_key_changed(self, *args):
        """Sort key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.sort_by_labels = self.selected_sort_by_labels()
                self.split_and_update()

    def on_distance_measure_changed(self):
        """Distance measure has changed
        """
        if self.data is not None:
            with widget_disable(self):
                self.update_distances()
                self.replot_experiments()

    def on_view_resize(self, size):
        """The view with the quality plot has changed
        """
        if self.main_widget:
            current = self.main_widget.size()
            self.main_widget.resize(size.width() - 6,
                                    current.height())

            self.scene.setSceneRect(self.scene.itemsBoundingRect())

    def on_rug_item_clicked(self, item):
        """An ``item`` in the quality plot has been clicked.
        """
        update = False
        sort_by_labels = self.selected_sort_by_labels()
        if sort_by_labels and item.in_group:
            ## The item is part of the group
            if item.group_index != self.base_group_index:
                self.base_group_index = item.group_index
                update = True

        else:
            if sort_by_labels:
                # If the user clicked on an background item it
                # invalidates the sorted labels selection
                with disable_updates(self):
                    self.sort_by_view.selectionModel().clear()
                    update = True

            index = item.index
            group = item.group
            label = group_label(self.selected_split_by_labels(), group)

            if self._base_index_hints.get(label, 0) != index:
                self._base_index_hints[label] = index
                update = True

        if update:
            with widget_disable(self):
                self.split_and_update()

    def eventFilter(self, obj, event):
        if obj is self.scene_view and event.type() == QEvent.Resize:
            self.on_view_resize(event.size())
        return super().eventFilter(obj, event)

    def split_and_update(self):
        """
        Split the data based on the selected sort/split labels
        and update the quality plot.

        """
        split_labels = self.selected_split_by_labels()
        sort_labels = self.selected_sort_by_labels()

        self.warning(0)
        if not split_labels:
            self.warning(0, "No separate by label selected.")

        self.groups, self.unique_pos = \
                exp.separate_by(self.data, split_labels,
                                consider=sort_labels,
                                add_empty=True)

        self.groups = sorted(self.groups.items(),
                             key=lambda t: list(map(float_if_posible, t[0])))
        self.unique_pos = sorted(self.unique_pos.items(),
                                 key=lambda t: list(map(float_if_posible, t[0])))

        if self.groups:
            if sort_labels:
                group_base = self.selected_base_group_index()
                base_indices = self.selected_base_indices(group_base)
            else:
                base_indices = self.selected_base_indices()
            self.update_distances(base_indices)
            self.replot_experiments()

    def get_cached_distances(self, measure):
        if measure not in self._cached_distances:
            attrs = self.data.domain.attributes
            mat = numpy.zeros((len(attrs), len(attrs)))

            self._cached_distances[measure] = \
                (mat, set(zip(range(len(attrs)), range(len(attrs)))))

        return self._cached_distances[measure]

    def get_cached_distance(self, measure, i, j):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        if key in computed:
            return matrix[i, j]
        else:
            return None

    def get_distance(self, measure, i, j):
        d = self.get_cached_distance(measure, i, j)
        if d is None:
            vec_i = take_columns(self.data, [i])
            vec_j = take_columns(self.data, [j])
            d = measure(vec_i, vec_j)

            mat, computed = self.get_cached_distances(measure)
            mat[i, j] = d
            key = key = (i, j) if i < j else (j, i)
            computed.add(key)
        return d

    def store_distance(self, measure, i, j, dist):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        matrix[j, i] = matrix[i, j] = dist
        computed.add(key)

    def update_distances(self, base_indices=()):
        """Recompute the experiment distances.
        """
        distance = self.selected_distance()
        if base_indices == ():
            base_group_index = self.selected_base_group_index()
            base_indices = [ind[base_group_index] \
                            for _, ind in self.groups]

        assert(len(base_indices) == len(self.groups))

        base_distances = []
        attributes = self.data.domain.attributes
        pb = gui.ProgressBar(self, len(self.groups) * len(attributes))

        for (group, indices), base_index in zip(self.groups, base_indices):
            # Base column of the group
            if base_index is not None:
                base_vec = take_columns(self.data, [base_index])
                distances = []
                # Compute the distances between base column
                # and all the rest data columns.
                for i in range(len(attributes)):
                    if i == base_index:
                        distances.append(0.0)
                    elif self.get_cached_distance(distance, i, base_index) is not None:
                        distances.append(self.get_cached_distance(distance, i, base_index))
                    else:
                        vec_i = take_columns(self.data, [i])
                        dist = distance(base_vec, vec_i)
                        self.store_distance(distance, i, base_index, dist)
                        distances.append(dist)
                    pb.advance()

                base_distances.append(distances)
            else:
                base_distances.append(None)

        pb.finish()
        self.distances = base_distances

    def replot_experiments(self):
        """Replot the whole quality plot.
        """
        self.scene.clear()
        labels = []

        max_dist = numpy.nanmax(list(filter(None, self.distances)))
        rug_widgets = []

        group_pen = QPen(Qt.black)
        group_pen.setWidth(2)
        group_pen.setCapStyle(Qt.RoundCap)
        background_pen = QPen(QColor(0, 0, 250, 150))
        background_pen.setWidth(1)
        background_pen.setCapStyle(Qt.RoundCap)

        main_widget = QGraphicsWidget()
        layout = QGraphicsGridLayout()
        attributes = self.data.domain.attributes
        if self.data is not None:
            for (group, indices), dist_vec in zip(self.groups, self.distances):
                indices_set = set(indices)
                rug_items = []
                if dist_vec is not None:
                    for i, attr in enumerate(attributes):
                        # Is this a within group distance or background
                        in_group = i in indices_set
                        if in_group:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           1.0, self.on_rug_item_clicked)
                            rug_item.setPen(group_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)
                            rug_item.group_index = indices.index(i)
                            rug_item.setZValue(rug_item.zValue() + 1)
                        else:
                            rug_item = ClickableRugItem(dist_vec[i] / max_dist,
                                           0.85, self.on_rug_item_clicked)
                            rug_item.setPen(background_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)

                        rug_item.group = group
                        rug_item.index = i
                        rug_item.in_group = in_group

                        rug_items.append(rug_item)

                rug_widget = RugGraphicsWidget(parent=main_widget)
                rug_widget.set_rug(rug_items)

                rug_widgets.append(rug_widget)

                label = group_label(self.selected_split_by_labels(), group)
                label_item = QGraphicsSimpleTextItem(label, main_widget)
                label_item = GraphicsSimpleTextLayoutItem(label_item, parent=layout)
                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                labels.append(label_item)

        for i, (label, rug_w) in enumerate(zip(labels, rug_widgets)):
            layout.addItem(label, i, 0, Qt.AlignVCenter)
            layout.addItem(rug_w, i, 1)
            layout.setRowMaximumHeight(i, 30)

        main_widget.setLayout(layout)
        self.scene.addItem(main_widget)
        self.main_widget = main_widget
        self.rug_widgets = rug_widgets
        self.labels = labels
        self.on_view_resize(self.scene_view.size())
Beispiel #4
0
class ConductorGraph(Plugin):

    _deferred_fit_in_view=Signal()
    _client_list_update_signal=Signal()
    
    def __init__(self, context):
        self._context=context
        super(ConductorGraph, self).__init__(context)
        self.initialised=False
        self.setObjectName('Conductor Graph')
        self._current_dotcode=None
        self._node_items=None
        self._edge_items=None
        self._node_item_events={}
        self._edge_item_events={}
        self._client_info_list={}
        self._widget=QWidget()
        self.cur_selected_client_name = ""
        self.pre_selected_client_name = ""
            
        # factory builds generic dotcode items
        self.dotcode_factory=PydotFactory()
        # self.dotcode_factory=PygraphvizFactory()
        self.dotcode_generator=RosGraphDotcodeGenerator()
        self.dot_to_qt=DotToQtGenerator()
        
        self._graph=ConductorGraphInfo()
        self._graph._reg_event_callback(self._update_client_list)
        self._graph._reg_period_callback(self._set_network_statisics)
        
        rospack=rospkg.RosPack()
        ui_file=os.path.join(rospack.get_path('concert_conductor_graph'), 'ui', 'conductor_graph.ui')
        loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('ConductorGraphUi')

        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))

        self._scene=QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        #self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('window-new'))
        self._widget.refresh_graph_push_button.pressed.connect(self._update_conductor_graph)

        self._widget.highlight_connections_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.auto_fit_graph_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()
        
        self._widget.tabWidget.currentChanged.connect(self._change_client_tab)
        self._client_list_update_signal.connect(self._update_conductor_graph)
        
        #rospy.Subscriber(concert_msgs.Strings.CONCERT_CLIENT_CHANGES, ConcertClients, self._update_client_list)
        
        context.add_widget(self._widget)
    
    def restore_settings(self, plugin_settings, instance_settings):
        self.initialised=True
        self._refresh_rosgraph()
    def shutdown_plugin(self):
        pass
    
    def _update_conductor_graph(self):
        # re-enable controls customizing fetched ROS graph

        self._refresh_rosgraph()
        self._update_client_tab()

    def _refresh_rosgraph(self):
        if not self.initialised:
            return
        self._update_graph_view(self._generate_dotcode())
        
    def _generate_dotcode(self):
        return self.dotcode_generator.generate_dotcode(rosgraphinst=self._graph,
                                                       dotcode_factory=self.dotcode_factory,
                                                       orientation='LR'
                                                       )
    def _update_graph_view(self, dotcode): 
        #if dotcode==self._current_dotcode:
        #    return
        self._current_dotcode=dotcode
        self._redraw_graph_view()
   
    def _update_client_list(self):
        print "[conductor graph]: _update_client_list"       
        self._client_list_update_signal.emit()
        pass
    
    def _start_service(self,node_name,service_name):
        
        service=self._graph._client_info_list[node_name]['gateway_name']+"/"+service_name  
        info_text='' 
        
        if service_name=='status':
            service_handle=rospy.ServiceProxy(service, Status)
            call_result=service_handle()
            
            info_text="<html>"
            info_text +="<p>-------------------------------------------</p>"
            info_text +="<p><b>application_namespace: </b>" +call_result.application_namespace+"</p>"
            info_text +="<p><b>remote_controller: </b>" +call_result.remote_controller+"</p>"
            info_text +="<p><b>application_status: </b>" +call_result.application_status+"</p>"
            info_text +="</html>"
            self._client_list_update_signal.emit()

        elif service_name=='platform_info':
            service_handle=rospy.ServiceProxy(service, GetPlatformInfo)
            call_result=service_handle()

            info_text = "<html>"
            info_text += "<p>-------------------------------------------</p>"
            info_text += "<p><b>rocon_uri: </b>" + call_result.platform_info.uri + "</p>"
            info_text += "<p><b>concert_version: </b>" + call_result.platform_info.version + "</p>"
            info_text += "</html>"
            self._client_list_update_signal.emit()
            
        elif service_name=='invite':
            #sesrvice
            service_handle=rospy.ServiceProxy(service, Invite) 
            #dialog
            dlg=QDialog(self._widget) 
            dlg.setMinimumSize(400,0)
            dlg.setMaximumSize(400,0)
            dlg.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding)
            #dialog layout
            ver_layout=QVBoxLayout(dlg)           
            ver_layout.setContentsMargins (0,0,0,0)

            dynamic_arg=[]
            dynamic_arg.append(DynamicArgumentLayer(ver_layout,'Remote Target Name',False,[('remote_target_name','string')]))
            dynamic_arg.append(DynamicArgumentLayer(ver_layout,'Application Namespace',False,[('application_namespace','string')]))
            dynamic_arg.append(DynamicArgumentLayer(ver_layout,'Cancel',False,[('cancel','bool')]))
            #button
            button_hor_sub_widget=QWidget()
            button_hor_layout=QHBoxLayout(button_hor_sub_widget)   
                   
            btn_call=QPushButton("Call")
            btn_cancel=QPushButton("cancel")
            
            btn_call.clicked.connect(lambda: dlg.done(0))
            btn_call.clicked.connect(lambda : self._call_invite_service(service,service_handle,dynamic_arg))

            btn_cancel.clicked.connect(lambda: dlg.done(0))
            #add button
            button_hor_layout.addWidget(btn_call)            
            button_hor_layout.addWidget(btn_cancel)
            #add button layout            
            ver_layout.addWidget(button_hor_sub_widget)

            dlg.setVisible(True)        

        elif service_name=='start_app':
            #sesrvice
            service_handle=rospy.ServiceProxy(service, StartApp) 
            #dialog
            dlg=QDialog(self._widget) 
            dlg.setMinimumSize(400,0)
            dlg.setMaximumSize(400,0)
            dlg.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Expanding)
            #dialog layout
            ver_layout=QVBoxLayout(dlg)           
            ver_layout.setContentsMargins (0,0,0,0)

            dynamic_arg=[]
            dynamic_arg.append(DynamicArgumentLayer(ver_layout,'Name',False,[('name','string')]))
            dynamic_arg.append(DynamicArgumentLayer(ver_layout,'Remappings',True,[('remap to','string'),('remap from','string')]))
            #button
            button_hor_sub_widget=QWidget()
            button_hor_layout=QHBoxLayout(button_hor_sub_widget)   
                   
            btn_call=QPushButton("Call")
            btn_cancel=QPushButton("cancel")
            
            btn_call.clicked.connect(lambda: dlg.done(0))
            btn_call.clicked.connect(lambda : self._call_start_app_service(service,service_handle,dynamic_arg))
            
            btn_cancel.clicked.connect(lambda: dlg.done(0))
            #add button
            button_hor_layout.addWidget(btn_call)            
            button_hor_layout.addWidget(btn_cancel)
            #add button layout            
            ver_layout.addWidget(button_hor_sub_widget)

            dlg.setVisible(True)        

        elif service_name=='stop_app':
            service_handle=rospy.ServiceProxy(service, StopApp)
            call_result=service_handle()

            info_text="<html>"
            info_text +="<p>-------------------------------------------</p>"
            info_text +="<p><b>stopped: </b>" +str(call_result.stopped)+"</p>"
            info_text +="<p><b>error_code: </b>" +str(call_result.error_code)+"</p>"
            info_text +="<p><b>message: </b>" +call_result.message+"</p>"
            info_text +="</html>"

            self._update_client_tab()
        else:
            print 'has no service'
            return
        
        # display the result of calling service  
        # get tab widget handle
        service_text_widget=None
        cur_tab_widget=self._widget.tabWidget.currentWidget()        
        
        if cur_tab_widget==None:
            return
            
        object_name='services_text_widget'
        for k in cur_tab_widget.children():
            if k.objectName().count(object_name) >=1 :
                service_text_widget=k
                break
        if service_text_widget==None:
            return
            
        service_text_widget.clear()
        service_text_widget.appendHtml(info_text)

    def _call_invite_service(self,service,service_handle,dynamic_arg):        
        remote_target_name=""
        application_namespace=""
        cancel=False

        for k in dynamic_arg:
            if k.name=='Remote Target Name':
                item_widget=k._get_param_list()[0][0][1]
                remote_target_name=item_widget.toPlainText()
            
            elif k.name=='Application Namespace':
                item_widgetwidget=k._get_param_list()[0][0][1]
                application_namespace=item_widget.toPlainText()
            
            elif k.name=='Cancel':
                item_widget=k._get_param_list()[0][0][1]    
                cancel=item_widget.itemData(item_widget.currentIndex())
        #calling service
        call_result=service_handle(remote_target_name,application_namespace,cancel)
        #status update
        self._client_list_update_signal.emit()
        # display the result of calling service  

        info_text="<html>"
        info_text +="<p>-------------------------------------------</p>"
        info_text +="<p><b>result: </b>" +str(call_result.result)+"</p>"
        info_text +="<p><b>error_code: </b>" +str(call_result.error_code)+"</p>"
        info_text +="<p><b>message: </b>" +call_result.message+"</p>"
        info_text +="</html>"        
        # get tab widget handle
        service_text_widget=None
        cur_tab_widget=self._widget.tabWidget.currentWidget()        
        if cur_tab_widget==None:
            return

        object_name='services_text_widget'
        for k in cur_tab_widget.children():
            if k.objectName().count(object_name) >=1 :
                service_text_widget=k
                break
        if service_text_widget==None:
            return
            
        service_text_widget.clear()
        service_text_widget.appendHtml(info_text)

        pass
    
    def _call_start_app_service(self,service,service_handle,dynamic_arg):
        name=""
        remappings=[]
        for k in dynamic_arg:
            if k.name=='Name':
                name=k._get_param_list()[0][0][1].toPlainText()
            elif k.name=='Remappings':    
                for l in k._get_param_list():
                    remap_to=l[0][1].toPlainText()
                    remap_from=l[1][1].toPlainText()
                    remappings.append(Remapping(remap_to,remap_from))
        #calling service
        call_result=service_handle(name,remappings)
        #status update
        self._client_list_update_signal.emit()

        # display the result of calling service          
        info_text = ''
        info_text="<html>"
        info_text +="<p>-------------------------------------------</p>"
        info_text +="<p><b>started: </b>" +str(call_result.started)+"</p>"
        info_text +="<p><b>error_code: </b>" +str(call_result.error_code)+"</p>"
        info_text +="<p><b>message: </b>" +call_result.message+"</p>"
        info_text +="<p><b>app_namespace: </b>" +call_result.app_namespace+"</p>"
        info_text +="</html>"
        # get tab widget handle
        service_text_widget=None
        cur_tab_widget=self._widget.tabWidget.currentWidget()        
        if cur_tab_widget==None:
            return
        object_name='services_text_widget'
        for k in cur_tab_widget.children():
            if k.objectName().count(object_name) >=1 :
                service_text_widget=k
                break
        if service_text_widget==None:
            return
            
        service_text_widget.clear()
        service_text_widget.appendHtml(info_text)

        pass

    def _update_client_tab(self):
        print '[_update_client_tab]'
        self.pre_selected_client_name = self.cur_selected_client_name
        self._widget.tabWidget.clear()   
        
        for k in self._graph._client_info_list.values(): 
            main_widget=QWidget()
           
            ver_layout=QVBoxLayout(main_widget)
           
            ver_layout.setContentsMargins (9,9,9,9)
            ver_layout.setSizeConstraint (ver_layout.SetDefaultConstraint)
            
            #button layout
            sub_widget=QWidget()
            sub_widget.setAccessibleName('sub_widget')
            btn_grid_layout=QGridLayout(sub_widget)

            btn_grid_layout.setContentsMargins (9,9,9,9)

            btn_grid_layout.setColumnStretch (1, 0)
            btn_grid_layout.setRowStretch (2, 0)

            invite_btn=QPushButton("Invite")
            platform_info_btn=QPushButton("Get Platform Info")
            status_btn=QPushButton("Get Status")
            start_app_btn=QPushButton("Start App")
            stop_app_btn=QPushButton("Stop App")              

            invite_btn.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"invite"))
            platform_info_btn.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"platform_info"))  
            status_btn.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"status"))  
            start_app_btn.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"start_app"))  
            stop_app_btn.clicked.connect(lambda: self._start_service(self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex()),"stop_app"))
                    
            btn_grid_layout.addWidget(invite_btn)
            btn_grid_layout.addWidget(platform_info_btn)
            btn_grid_layout.addWidget(status_btn)
            btn_grid_layout.addWidget(start_app_btn)
            btn_grid_layout.addWidget(stop_app_btn)             
            ver_layout.addWidget(sub_widget)            

            #client information layout
            context_label = QLabel()
            context_label.setText("Client information")
            ver_layout.addWidget(context_label)
            
            app_context_widget=QPlainTextEdit()
            app_context_widget.setObjectName(k["name"]+'_'+'app_context_widget')
            app_context_widget.setAccessibleName('app_context_widget')
            app_context_widget.appendHtml(k["app_context"])
            app_context_widget.setReadOnly(True) 
            
            cursor = app_context_widget.textCursor()
            cursor.movePosition(QTextCursor.Start,QTextCursor.MoveAnchor,0)
            app_context_widget.setTextCursor(cursor)
            ver_layout.addWidget(app_context_widget)
            
            #service layout
            context_label = QLabel()
            context_label.setText("Service result")
            ver_layout.addWidget(context_label)
            
            services_text_widget=QPlainTextEdit()
            services_text_widget.setObjectName(k["name"]+'_'+'services_text_widget')
            services_text_widget.setReadOnly(True) 
            cursor = services_text_widget.textCursor()
            cursor.movePosition(QTextCursor.Start,QTextCursor.MoveAnchor,0)
            services_text_widget.setTextCursor(cursor)            
            ver_layout.addWidget(services_text_widget)
            
            # new icon
            path=""
            if k["is_new"]==True:
                path=os.path.join(os.path.dirname(os.path.abspath(__file__)),"../../resources/images/new.gif")            

            #add tab
            self._widget.tabWidget.addTab(main_widget,QIcon(path), k["name"]);

        #set previous selected tab
        for k in range(self._widget.tabWidget.count()):
            tab_text=self._widget.tabWidget.tabText(k)
            if tab_text == self.pre_selected_client_name:
                self._widget.tabWidget.setCurrentIndex(k)

    def _change_client_tab(self,index):
        self.cur_selected_client_name = self._widget.tabWidget.tabText(self._widget.tabWidget.currentIndex())
        if self._widget.tabWidget.widget(index) !=None:
            for k in  self._widget.tabWidget.widget(index).children():
                if k.objectName().count("services_text_widget"):
                    k.clear()
        pass    
        
    def _set_network_statisics(self):
        if self._edge_items == None:
            return
        else:
            for edge_items in self._edge_items.itervalues():
                for edge_item in edge_items:
                     edge_dst_name=edge_item.to_node._label.text()
                     edge_item.setToolTip(str(self._graph._client_info_list[edge_dst_name]['conn_stats']))
                     
    def _redraw_graph_view(self):
        self._scene.clear()
        self._node_item_events={}
        self._edge_item_events={}
        self._node_items=None
        self._edge_items=None

        if self._widget.highlight_connections_check_box.isChecked():
            highlight_level=3
        else:
            highlight_level=1
            
        highlight_level=3 if self._widget.highlight_connections_check_box.isChecked() else 1

        # layout graph and create qt items
        (nodes, edges)=self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
                                                            highlight_level=highlight_level,
                                                            same_label_siblings=True)
        self._node_items=nodes
        self._edge_items=edges

        # if we wish to make special nodes, do that here (maybe subclass GraphItem, just like NodeItem does)
        #node
        for node_item in nodes.itervalues():
            # set the color of conductor to orange           
            if node_item._label.text()==self._graph._concert_conductor_name:
                royal_blue=QColor(65, 105, 255)
                node_item._default_color=royal_blue
                node_item.set_color(royal_blue)

            # redefine mouse event
            self._node_item_events[node_item._label.text()]=GraphEventHandler(self._widget.tabWidget,node_item,node_item.mouseDoubleClickEvent);
            node_item.mouseDoubleClickEvent=self._node_item_events[node_item._label.text()].NodeEvent;
            
            self._scene.addItem(node_item)
            
        #edge
        for edge_items in edges.itervalues():
            for edge_item in edge_items:
                #redefine the edge hover event
                
                self._edge_item_events[edge_item._label.text()]=GraphEventHandler(self._widget.tabWidget,edge_item,edge_item._label.hoverEnterEvent);
                edge_item._label.hoverEnterEvent =self._edge_item_events[edge_item._label.text()].EdgeEvent;
                
                #self._edge_item_events[edge_item._label.text()]=GraphEventHandler(self._widget.tabWidget,edge_item,edge_item.mouseDoubleClickEvent);
                #edge_item.mouseDoubleClickEvent=self._edge_item_events[edge_item._label.text()].EdgeEvent;

                edge_item.add_to_scene(self._scene)

                #set the color of node as connection strength one of red, yellow, green
                edge_dst_name=edge_item.to_node._label.text()
                if edge_dst_name in self._graph._client_info_list.keys():
                  connection_strength=self._graph._client_info_list[edge_dst_name]['connection_strength']
                  if connection_strength=='very_strong':
                      green=QColor(0, 255, 0)
                      edge_item._default_color=green
                      edge_item.set_color(green)

                  elif connection_strength=='strong':
                      green_yellow=QColor(125, 255,0)
                      edge_item._default_color=green_yellow
                      edge_item.set_color(green_yellow)
                        
                  elif connection_strength=='normal':
                      yellow=QColor(238, 238,0)
                      edge_item._default_color=yellow
                      edge_item.set_color(yellow)

                  elif connection_strength=='weak':
                      yellow_red=QColor(255, 125,0)
                      edge_item._default_color=yellow_red
                      edge_item.set_color(yellow_red)
                      
                  elif connection_strength=='very_weak':
                      red=QColor(255, 0,0)
                      edge_item._default_color=red
                      edge_item.set_color(red)
                #set the tooltip about network information
                edge_item.setToolTip(str(self._graph._client_info_list[edge_dst_name]['conn_stats']))    

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
  
        if self._widget.auto_fit_graph_check_box.isChecked():
            self._fit_in_view()

    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)
class Canvas(QGraphicsView):
    def __init__(self, window, resultDict, imagePath):
        QGraphicsView.__init__(self)
        self.window = window
        self.pen = QPen(QColor("red"))
        self.pen.setWidth(0.5)
        self.canvasScene = QGraphicsScene()
        self.setScene(self.canvasScene)
        self.resultDict = resultDict
        self.imagePath = imagePath
        self.setBackgroundBrush(QBrush(Qt.black, Qt.SolidPattern))

    def drawImage(self, imageFile):
        """Draw an image on the canvas"""
        image = QPixmap(imageFile)
        self.canvasScene.addPixmap(image)
        return image

    def drawFeaturePoint(self, pointList):
        """Draw a feature point on the canvas"""
        radius = 0.5
        width, height = 2, 2

        x1, y1, x2, y2 = pointList

        #Draw ellipse and bounding rect. Is a hacked version!
        self.canvasScene.addEllipse(x1 - radius + 5, y1 - radius + 3, 2 * radius, 2 * radius, self.pen)
        self.canvasScene.addEllipse(x2 - radius + self.imageWidth + 10, y2 - radius + 3, 2 * radius, 2 * radius, self.pen)
        self.canvasScene.addRect(x1 - width / 2. + 5, y1 - height / 2. + 3, width, height, self.pen)
        self.canvasScene.addRect(x2 - width / 2. + self.imageWidth + 10, y2 - height / 2. + 3, width, height, self.pen)

    def drawFeatureImages(self, imageFile):
        """Draw two consecutive images on the screen"""
        #Load image files
        path, file_ = os.path.split(imageFile)
        image1 = QPixmap(os.path.join(path, 'first_' + file_))
        image2 = QPixmap(os.path.join(path, 'second_' + file_))
        self.imageWidth = image1.width()

        #Add pixmaps
        image1Map = self.canvasScene.addPixmap(image1)
        image2Map = self.canvasScene.addPixmap(image2)

        #Shift pixmaps to the right position
        image1Map.setOffset(QPointF(5, 3))
        image2Map.setOffset(QPointF(10 + image1.width(), 3))

    def drawPolygon(self, Polygon):
        """Draw a polygon on the canvas"""
        polygon = QPolygonF()
        for point in Polygon:
            polygon.append(QPointF(point[0], point[1]))
        self.canvasScene.addPolygon(polygon, self.pen)

    def getWorkerId(self):
        return self.resultDict.values()[self.index][0][0]

    def getAssignmentId(self):
        return self.resultDict.keys()[self.index]

    def nextImage(self):
        """Load next image"""
        self.index += 1
        self.canvasScene.clear()
        if self.index > len(self.resultDict) - 1 or len(self.resultDict) <= 0:
            self.canvasScene.addText("No annotations to review")
            self.window.reviewFlag = False
            self.window.updateTable()

        else:
            #Draw Image and Polygon
            assignmentId = self.resultDict.keys()[self.index]
            result = self.resultDict[assignmentId]
            image = result[0][1]
            pointList = result[0][2]
            if self.window.segmentation_mode:
                pointList = [round(float(point), 3) for point in pointList]
                pointList = zip(*[iter(pointList)] * 2)
                self.drawImage(os.path.join(self.imagePath, image))
                self.drawPolygon(pointList)
            else:
                pointList = [round(float(point), 3) for point in pointList]
                pointList = zip(*[iter(pointList)] * 4)
                self.drawFeatureImages(os.path.join(self.imagePath, image))
                for point in pointList:
                    self.drawFeaturePoint(point)

        #update scene
        self.window.setWindowTitle("MTurk Review Tool ({0}/{1})   Rejected: {2}   Approved: {3}".format(self.index + 1,
                                        len(self.resultDict), len(self.window.rejected), len(self.window.approved)))
        self.canvasScene.setSceneRect(self.canvasScene.itemsBoundingRect())
        self.fitInView(0, 0, self.canvasScene.width(), self.canvasScene.height(), 1)    
        self.canvasScene.update(0, 0, self.canvasScene.width(), self.canvasScene.height())
Beispiel #6
0
class TreeDialog(QDialog):
    def __init__(self):
        import treedialog
        QDialog.__init__(self)
        self.ui = treedialog.Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.draw_buton.clicked.connect(self.new_anim)
        self.scene = QGraphicsScene()
        self.ui.image.setScene(self.scene)
        self.ui.image.setRenderHints(QPainter.HighQualityAntialiasing)

    def get_params(self):
        params = dict()
        params["branch_split"] = self.ui.branch_split.value()
        params["branch_after_range"] = (self.ui.branch_after_min.value(), self.ui.branch_after_max.value())
        params["branch_split_var"] = self.ui.branch_split_var.value()
        params["gravity"] = self.ui.gravity.value()
        params["down_die_probability"] = self.ui.down_die_probability.value()
        params["down_damping_x"] = self.ui.down_damping_x.value()
        params["down_damping_y"] = self.ui.down_damping_x.value()
        params["start_branches"] = self.ui.start_branches.value()
        params["keep_central"] = self.ui.keep_central.value()
        params["color"] = QColor(self.ui.r.value() * 255, self.ui.g.value() * 255, self.ui.b.value() * 255)
        params["color_speed"] = self.ui.color_speed.value()
        params["painter_thickness"] = self.ui.painter_thickness.value()
        params["painter_generations"] = self.ui.painter_generations.value()
        start_rand = [random.uniform(-1, 1) * self.ui.v_start_var.value() for i in range(2)]
        params["v_start"] = complex(self.ui.v_start_x.value()+start_rand[0], self.ui.v_start_y.value()+start_rand[1])
        return params

    def new_anim(self):
        image = None
        painter_id = random.randint(0, 2**32)
        self.active_painer = painter_id
        index = 0
        every = self.ui.repaint.value()

        self.scene.clear()

        tree_count = self.ui.tree_count.value()
        need_spacing = 65
        used_locations = []
        for tree_index in range(tree_count):
            z_range = 80
            base_z = random.uniform(-z_range, z_range)

            def make_base():
                return complex(10 + random.uniform(-350 + 150*tree_count, 350 + 150*tree_count), base_z)
            def closest_distance_to_used(base):
                return min([np.abs(np.real(base - item)) for item in used_locations])
            base_location = make_base()
            while not len(used_locations) == 0 and closest_distance_to_used(base_location) < need_spacing:
                base_location = make_base()

            used_locations.append(base_location)
            scale = 1.0 + 0.75 * ((z_range - base_z) / (2*z_range)) ** 2
            self.tree = Tree(self.get_params(), base_location=base_location, scale=scale)
            for iteration in self.tree.grow_iterations(self.ui.generations.value(), yield_every=every):
                self.tree.draw(self.scene, incremental=True)
                self.ui.progress.setText("Working ... displayed frame: {0}".format(index))

                gradient = QLinearGradient(self.scene.itemsBoundingRect().topLeft(),
                                           self.scene.itemsBoundingRect().bottomLeft())
                gradient.setColorAt(0.4, QColor(0, 0, 0))
                gradient.setColorAt(0, QColor(25, 25, 25))
                self.scene.setBackgroundBrush(QBrush(gradient))

                self.ui.image.repaint()
                QApplication.processEvents()
                index += every
                if self.active_painer != painter_id:
                    return

        self.ui.image.fitInView(self.scene.itemsBoundingRect(), 1)
        d = GrassDrawer(self.ui.image)
        d.draw_some_grass(150 + 75*tree_count)
        #self.ui.image.fitInView(self.scene.itemsBoundingRect(), 1)
        self.ui.progress.setText("Done. Displayed frame: {0}".format(index))
class Nexus(QMainWindow):
	"""
	Die Hauptklasse des Programms.

	In dieser Klasse wird die GUI gesteuert und die Würfelwürfe aufgerufen.
	"""
	
	dicePoolChanged = pyqtSignal(int)

	xAgainChanged = pyqtSignal(int)
	cursed = pyqtSignal(bool)


	def __init__(self,  parent=None):
		"""
		Konstruktor 
		"""

		self.translator_app = QTranslator()
		self.translator_qt = QTranslator()

		QApplication.installTranslator( self.translator_app )
		QApplication.installTranslator( self.translator_qt )

		QWidget.__init__(self,  parent)

		QCoreApplication.setOrganizationName("Caern")
		QCoreApplication.setOrganizationDomain("www.caern.de")
		QCoreApplication.setApplicationName("DiceRoller WoD")
		QCoreApplication.setApplicationVersion(QString.number(PROGRAM_VERSION_MAJOR) +
			"." +
			QString.number(PROGRAM_VERSION_MINOR) +
			"." +
			QString.number(PROGRAM_VERSION_CHANGE)
		)
		QApplication.setWindowIcon(QIcon(":/icons/logo/WoD.png"))

		self.ui = Ui_MainWindow()
		self.ui.setupUi(self)
		
		self.createInfo()

		#self.createLanguageMenu()
		self.instantRoll = InstantRoll()
		self.extendedRoll = ExtendedRoll()

		# Dieser Zähler bestimmt, wie der rollende Würfel angezeigt wird.
		self.timerDice = QTimer()
		# Verzögert die tatsächliche Ausführung des Würfelwurfs.
		self.timerRoll = QTimer()
		
		self.populateUi()
		self.createConnections()
		self.initializing()

		self.setWindowTitle(QCoreApplication.applicationName())

		#self.retranslateUi()
		
		## Die von der letzten Benutzung gespeicherte Größe und Position auf dem Bildschirm laden.
		#self.readSettings()


	#def closeEvent(self, event):
		#"""
		#Diese Funktion wird aufgerufen, wann immer das Programm geschlossen wird.
		#Die Idee dahinter ist, vor dem Beenden, Größe und Position des Fensters zu speichern.
		#"""
		#self.writeSettings()
		#event.accept()


	def createInfo(self):
		"""
		Erzeugt Tooltips und Hilfe für die einzelnen Teile des Programms.
		"""

		self.ui.action_houserules.setStatusTip(self.ui.action_houserules.toolTip())


	#def createLanguageMenu(self):
		#"""
		#Erzeugt das Menü zum Umschalten zwischen den möglichen Sprachen.
		#"""

		#self.menu_language = QMenu( self.tr("&Language") )
		#self.actionGroup_language = QActionGroup(self)

		#self.langPath = getPath() + "/" + PROGRAM_LANGUAGE_PATH
		#self.dir_qm = QDir( self.langPath );
		#self.fileNames = self.dir_qm.entryList( QStringList( "DiceRoller-WoD_*.qm" ));

		## Englisch hat keine qm-Datei,  also muß es von Hand hinzugefügt werden.
		#self.action = QAction( "&1 English",  self.actionGroup_language )
		#self.action.setCheckable( True )
		#self.action.setData( "en" )
		#self.action.setChecked( True )

		#self.menu_language.addAction( self.action )
		#self.actionGroup_language.addAction( self.action )

		#iter = 0
		#for i in self.fileNames:
			#self.trFilename = unicode(i)
			#self.locale = unicode(i)
			#self.locale = self.locale[(self.locale.find( "_" )+1):(self.locale.find( "." ))]

			#self.translator = QTranslator()
			#self.translator.load( self.trFilename,  self.dir_qm.absolutePath() )
			#self.language = self.translator.translate( "MainWindow",  "English" )

			#self.action = QAction( "&" + QString.number(iter + 2) + " " + self.language,  self.actionGroup_language )
			#self.action.setCheckable( True )
			#self.action.setData( self.locale )

			#self.menu_language.addAction ( self.action )
			#self.actionGroup_language.addAction ( self.action )

			#iter += 1

		#self.actionGroup_language.triggered.connect(self.switchLanguage)

		#self.ui.menuBar.insertMenu(self.ui.menuHelp.menuAction(),  self.menu_language)


	#def switchLanguage( self,  action ):
		#"""
		#Schaltet zwischen den einzelnen Sprachen um.
		#"""

		#self.locale = action.data().toString();
		#self.qmPath = getPath() + "/" + PROGRAM_LANGUAGE_PATH

		##if self.translator_app.load( "DiceRoller-WoD_" + self.locale,  self.qmPath ):
			##qDebug("Hat DiceRoller-WoD_" + self.locale + " geladen.")

		##if self.translator_qt.load( "qt_" + self.locale,  QLibraryInfo.location ( QLibraryInfo.TranslationsPath ) ):
			##qDebug("Hat qt_" + self.locale + " geladen.")

		## Alle Texte neu setzen
		#self.retranslateUi()
		## Seltsamerweise ist retranslate in Ui_MainWindow leer. Ich weiß nicht,  wieso das der Fall ist.
		#self.ui.retranslateUi(self.ui)


	#def retranslateUi(self):
		#"""
		#Diese Funktion übersetzt alle Texte, welche nicht in der .ui-Datei festgelegt sind, sondern im Quellcode (hier) geschrieben wurden.
		#"""

		#self.menu_language.setTitle( self.tr( "&Language" ) )
		#self.reset()


	def populateUi(self):
		self.svgRenderer = QSvgRenderer(":/icons/W10.svg")
		self.scene = QGraphicsScene()
		self.view = QGraphicsView()
		self.view.setFrameShape(QFrame.NoFrame)
		self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
		self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
		self.view.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
		self.view.setStyleSheet("background-color: transparent;");
		
		self.view.setScene(self.scene)
		self.ui.horizontalLayout_dice.insertWidget(1, self.view)


	def createConnections(self):
		"""
		Erstelle die Verbindungen zwischen den verschiedenen Klassen und Elementen des Programms.
		"""

		self.ui.action_about.triggered.connect(self.aboutApp)
		self.ui.action_aboutQt.triggered.connect(QApplication.aboutQt)
		self.ui.action_houserules.toggled.connect(self.setHouserules)
		self.ui.pushButton_roll.clicked.connect(self.roll)
		self.ui.spinBox_pool.valueChanged.connect(self.calcDicePool)
		self.ui.spinBox_pool.valueChanged.connect(self.reset)
		self.ui.spinBox_modifier.valueChanged.connect(self.calcDicePoolMod)
		self.ui.spinBox_modifier.valueChanged.connect(self.reset)
		self.ui.checkBox_rote.toggled.connect(self.instantRoll.setRote)
		self.ui.checkBox_rote.toggled.connect(self.extendedRoll.setRote)
		self.ui.checkBox_rote.toggled.connect(self.reset)
		self.ui.comboBox_xAgain.activated.connect(self.setXAgainThreshold)
		self.ui.comboBox_xAgain.activated.connect(self.reset)
		self.ui.groupBox_extended.toggled.connect(self.changeText)
		self.ui.radioButton_target.toggled.connect(self.changeText)
		self.ui.radioButton_maxRolls.toggled.connect(self.changeText)
		self.ui.groupBox_extended.toggled.connect(self.reset)
		self.ui.radioButton_target.toggled.connect(self.setExtendedMode)
		self.ui.spinBox_target.valueChanged.connect(self.extendedRoll.setTarget)
		self.ui.spinBox_target.valueChanged.connect(self.reset)
		self.ui.spinBox_maxRolls.valueChanged.connect(self.extendedRoll.setMaxRolls)
		self.ui.spinBox_maxRolls.valueChanged.connect(self.reset)
		self.ui.checkBox_rollsLimited.toggled.connect(self.extendedRoll.setLimited)
		self.ui.checkBox_rollsLimited.toggled.connect(self.reset)
		self.xAgainChanged.connect(self.instantRoll.setThreshold)
		self.xAgainChanged.connect(self.extendedRoll.setThreshold)
		self.cursed.connect(self.instantRoll.setCurse)
		self.cursed.connect(self.extendedRoll.setCurse)
		self.instantRoll.rolled.connect(self.setResultSuccesses)
		self.extendedRoll.rolled.connect(self.setResultSuccesses)
		self.extendedRoll.rollsNeeded.connect(self.setResultRolls)
		self.instantRoll.rollFinished.connect(self.setResult)
		self.extendedRoll.rollFinished.connect(self.setResult)
		
		self.dicePoolChanged.connect(self.changeDiceDisplay)

		self.timerDice.timeout.connect(self.displayDice)
		self.timerRoll.timeout.connect(self._executeRoll)


	def initializing(self):
		"""
		Initialisiert das Programm mit den Startwerten.
		"""

		self.ui.action_quit.setIcon(QIcon(":/icons/actions/exit.png"))
		self.ui.action_about.setIcon(QIcon(":/icons/logo/WoD.png"))
		self.ui.pushButton_quit.setIcon(self.ui.action_quit.icon())
		self.ui.pushButton_roll.setIcon(QIcon(":icons/W10_0.svg"))
		
		self.ui.action_quit.setMenuRole(QAction.QuitRole)
		self.ui.action_about.setText(self.tr("About %1...").arg(QApplication.applicationName()))
		self.ui.action_about.setMenuRole(QAction.AboutRole)

		self.ui.spinBox_pool.setValue(2)
		self.ui.checkBox_rote.setChecked(False)
		self.ui.comboBox_xAgain.setCurrentIndex(0)
		self.ui.spinBox_target.setValue(1)
		self.changeText()
		self.ui.radioButton_target.setChecked(True)
		self.ui.groupBox_extended.setChecked(False)
		self.ui.checkBox_rollsLimited.setChecked(True)

		self.dice = []
		for i in xrange(10):
			self.W10_x = QGraphicsSvgItem()
			self.W10_x.setSharedRenderer(self.svgRenderer)
			self.W10_x.setElementId("layer" + str(i))
			#self.W10_x.setVisible(False)
			# Ich lege diese Liste an, da ich auf die Liste in self.scene irgendwie nicht zugreifen kann.
			self.dice.append(self.W10_x)
			#self.scene.addItem(self.W10_x)


	def displayDice(self, value=None):
		"""
		@todo Der Würfel kann mehrmals in Folge das selbe Ergebnis anzeigen, was dazu führt, daß der Bildablauf zu stocken scheint.
		"""

		if (value == None):
			dieValue = Random.random(10)-1
		else:
			dieValue = value

		for item in self.scene.items():
			self.scene.removeItem(item)

		self.scene.addItem(self.dice[dieValue])
		self.view.setSceneRect(self.scene.itemsBoundingRect())
		self.view.fitInView(self.dice[dieValue])


	def changeDiceDisplay(self, number):
		"""
		Diese Funktion bestimmt, wieviele Würfel angezeigt werden.
		"""
		pass
		
		#if (self.ui.horizontalLayout_dice.count > 2):
			#pass
		
		#randomValue = Random.random(10)-1

		#for die in xrange(number):
			#self.__W10_scene = QGraphicsScene()
			#self.__W10_scene.addItem(self.dice[randomValue])
			
			#self.__W10_view = QGraphicsView()
			#self.__W10_view.setScene(self.__W10_scene)
			#self.__W10_view.setSceneRect(self.scene.itemsBoundingRect())
			#self.__W10_view.fitInView(self.dice[randomValue])
			#self.ui.horizontalLayout_dice.insertWidget(1, self.__W10_view)


	def aboutApp(self):
		"""
		Zeigt die Info-Nachricht an.
		"""

		self.appText = self.tr("""
			<h1>%1</h1>
			<h2>Version: %2</h2>
			<p>Copyright (C) 2011 by Victor von Rhein<br>
			EMail: [email protected]</p>
		""").arg(QCoreApplication.applicationName()).arg(QCoreApplication.applicationVersion())
		self.gnuText = self.tr("""
			<h2>GNU General Public License</h2>
			<p>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation,  either version 3 of the License,  or (at your option) any later version.</p>
			<p>This program is distributed in the hope that it will be useful,  but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.</p>
			<p>You should have received a copy of the GNU General Public License along with this program.  If not,  see <a>http://www.gnu.org/licenses/</a>.</p>
		""")
		self.wodText = self.tr("""
			<h2>%1</h2>
			<p>%1,  %2,  the %3 and all referring terms and symbols are copyrighted by %4</p>
		""").arg("World of Darkness").arg("White Wolf").arg("White Wolf-Logo").arg("White Wolf Inc.")
		self.aboutText = self.appText + self.gnuText + self.wodText
		QMessageBox.about(self,  "About " + QCoreApplication.applicationName(),  self.aboutText )


	def roll(self):
		"""
		Der Wurf wird durchgeführt. Der tatsächliche Wurf wird aber von den Timern angestoßen.
		"""

		# Es wird ein rollender Würfel angezeigt.
		self.timerDice.start(DICEROLL_TIMER_INTERVAL)
		self.timerRoll.start(DICEROLL_TIMER_DELAY)


	def _executeRoll(self):
		"""
		Entscheidet vor dem eigentlichen Würfelwurf, ob ein normaler oder ein erweiterter Wurf notwendig ist und führt diesen aus.
		"""

		if self.ui.groupBox_extended.isChecked():
			#qDebug("Checked")
			self.extendedRoll.roll()
		else:
			#qDebug("Not Checked")
			self.instantRoll.roll()

		# Die Anzeige des rollenden Würfels wird angehalten
		self.timerDice.stop()
		self.timerRoll.stop()


	def calcDicePool(self, value):
		"""
		Berechnet die Größe des zur Verfügung stehenden Würfelpools, welcher von den Würfeln und den Modifikatoren abhängt.
		"""

		self.instantRoll.poolSize = value + self.ui.spinBox_modifier.value()
		self.extendedRoll.poolSize = self.instantRoll.poolSize
		self.extendedRoll.limit = value
		
		self.dicePoolChanged.emit(self.instantRoll.poolSize)


	def calcDicePoolMod(self, value):
		"""
		Berechnet wie schon calcDicePool() die Größe des Würfelvorrats, allerdings werden dieser Funktion andere Argumente übergeben.
		"""

		self.instantRoll.poolSize = value + self.ui.spinBox_pool.value()
		self.extendedRoll.poolSize = value + self.ui.spinBox_pool.value()


	def setHouserules(self, value):
		#qDebug("Test" + str(value))
		self.extendedRoll.isHouserules = value


	def setXAgainThreshold(self, value):
		"""
		Legt fest, bei welchem Ergebnis weitergewürfelt werden kann und wann dies überhaupt nicht der Fall sein sollte oder gar Erfolge abgezogen werden können.
		"""

		self.__threshold = 0
		if (value < 3):
			self.__threshold = 10 - value	# Index 0 entspricht 10 again, 1 entspricht 9 again etc.
		else:
			self.__threshold = 11	# Index 3 entspricht "no reroll"

		self.xAgainChanged.emit(self.__threshold)

		if (value > 3):
			self.cursed.emit(True)	# Kein reroll und 1er werden Abgezogen.
		else:
			self.cursed.emit(False)	# 1er werden nicht abgezogen.


	def setExtendedMode(self, sw):
		"""
		Legt den Modus fest, mit welchem der erweiterte Wurf durchgeführt wird. Entweder wird auf ein Ergebnishingewürfelt, oder nach einer bestimmten Anzahl würde die Anzahl der Erfolge gezählt.
		"""

		if (sw):
			self.extendedRoll.isResultInRolls = False
		else:
			self.extendedRoll.isResultInRolls = True


	def setResult(self, value):
		"""
		Schreibt das Ergebnis des Wurfs in die GUI. Dabei wird auch je nach Erfolgsqualität bei dem dargestellten Würfel eine andere Augenzahl gezeigt.
		"""

		self.ui.statusBar.showMessage(self.tr("Result of diceroll is displayed."))

		if (value == DieResult.dramaticFailure):
			self.ui.label_resultText.setText(self.tr("Dramatic Failure"));
			self.ui.label_result.setPixmap(QPixmap(":/icons/actions/cnrdelete-all1.png"));
			self.displayDice(1)
		elif (value == DieResult.failure):
			self.ui.label_resultText.setText(self.tr("Failure"));
			self.ui.label_result.setPixmap(QPixmap(":/icons/actions/fileclose.png"));
			self.displayDice(Random.random(2, 7))
		elif (value == DieResult.success):
			self.ui.label_resultText.setText(self.tr("Success"));
			self.ui.label_result.setPixmap(QPixmap(":/icons/actions/ok.png"));
			self.displayDice(Random.random(8, 9))
		else:
			self.ui.label_resultText.setText(self.tr("Exceptional Success"));
			self.ui.label_result.setPixmap(QPixmap(":/icons/actions/bookmark.png"));
			self.displayDice(0)


	def setResultRolls(self, value):
		"""
		Zeigt in der GUI an, wieviele Würfe nötig waren.
		"""

		if (self.ui.groupBox_extended.isChecked() and self.ui.radioButton_target.isChecked()):
			self.ui.lcdNumber_successes.display(value)


	def setResultSuccesses(self, value):
		"""
		Zeigt in der GUI an, wieviele Erfolge erzielt wurden.
		"""

		if (not self.ui.groupBox_extended.isChecked() or not self.ui.radioButton_target.isChecked()):
			self.ui.lcdNumber_successes.display(value)


	def changeText(self):
		"""
		Verändert den Text in der Statuszeile.
		"""

		if (self.ui.groupBox_extended.isChecked() and self.ui.radioButton_target.isChecked()):
			self.ui.label_successes.setText(self.tr("Number of rolls needed:"))
		else:
			self.ui.label_successes.setText(self.tr("Number of successes:"))


	def reset(self):
		"""
		Setzt das Programm auf einen definierten Startwert zurück.
		"""

		self.ui.label_result.setPixmap(QPixmap(":/icons/actions/fileclose.png"))
		self.ui.label_resultText.setText(self.tr("No result yet!"))
		self.ui.lcdNumber_successes.display(0)
		self.ui.statusBar.showMessage(self.tr("Press the Button to roll the dice!"))
Beispiel #8
0
class Window(QWidget):
    ''' The MainWindow widget '''
    def __init__(self, game):
        super().__init__()

        self.game = game
        self.game.ui = self

        self.setWindowTitle('WordJuggler')
        self.resize(800, 600)
        self.setStyleSheet('QGroupBox { border:0; font:bold;' +
                           'padding:20px 10px; min-width:220px; }')

        self.board = BoardItem(game.width, game.height)
        self.rack = RackItem(game.rack_size, game.width, game.height)
        self.scene = QGraphicsScene()
        self.scene.setBackgroundBrush(QBrush(QColor('#f9ece0')))
        self.scene.addItem(self.board)
        self.scene.addItem(self.rack)
        self.scene.setSceneRect(self.scene.itemsBoundingRect())
        self.view = BoardView(self.scene, self)
        self.view.letterChanged.connect(self.letterChanged)

        self.ranking = QGroupBox('Rankings')
        self.rankings = QLabel()
        rankings = QVBoxLayout()
        rankings.addWidget(self.rankings)
        self.ranking.setLayout(rankings)

        self.statistic = QGroupBox('Statistics')
        self.statistics = QLabel()
        statistics = QVBoxLayout()
        statistics.addWidget(self.statistics)
        self.statistic.setLayout(statistics)

        self.move = QGroupBox('Last 10 Moves')
        self.moves = QLabel()
        moves = QVBoxLayout()
        moves.addWidget(self.moves)
        self.move.setLayout(moves)

        self.buttons = QVBoxLayout()
        self.buttons.setSpacing(3)
        self.continue_button = QPushButton('Place &Word')
        self.continue_button.setEnabled(False)
        self.continue_button.setFixedSize(130, 25)
        self.continue_button.clicked.connect(self.continueClicked)
        self.pass_button = QPushButton('&Pass')
        self.pass_button.setEnabled(False)
        self.pass_button.setFixedSize(130, 25)
        self.pass_button.clicked.connect(self.passClicked)
        self.exchange_button = QPushButton('&Exchange')
        self.exchange_button.setEnabled(False)
        self.exchange_button.setFixedSize(130, 25)
        self.exchange_button.clicked.connect(self.exchangeClicked)
        self.buttons.addWidget(self.exchange_button, alignment=Qt.AlignCenter)
        self.buttons.addWidget(self.pass_button, alignment=Qt.AlignCenter)
        self.buttons.addWidget(self.continue_button, alignment=Qt.AlignCenter)

        information = QVBoxLayout()
        information.setMargin(20)
        information.setSpacing(20)
        information.addWidget(self.ranking)
        information.addWidget(self.statistic)
        information.addWidget(self.move)
        information.addStretch()
        information.addLayout(self.buttons)

        layout = QHBoxLayout()
        layout.setSpacing(0)
        layout.setMargin(0)
        layout.addWidget(self.view)
        layout.addLayout(information)

        self.setLayout(layout)
        self.show()

        for player in self.game.players:
            player.played_cb = self.playerDone
        self.playerNext()

    def update(self, *args, **kwargs):
        self.rankings.setText(
            '<br>'.join('%i. <font color=%s>%s</font> (%i points)' %
                        (i + 1, player.color, player.name, player.score)
                        for i,player in
                        enumerate(sorted(self.game.players, reverse=True,
                                         key=attrgetter('score')))))
        self.statistics.setText(('Total Players: %i\n' +
                                 'Placed Words: %i\n' +
                                 'Remaining Letters: %i') %
                                (len(self.game.players),
                                 len(list(self.game.board.get_words())),
                                 self.game.letters.remaining_letters))
        moves = []
        for i,(player,move) in list(enumerate(self.game.moves))[-10:]:
            if move[0] == Player.PASS:
                desc = 'Pass'
            elif move[0] == Player.EXCHANGE_LETTERS:
                desc = 'Exchange (%s,%s)' % move[1:]
            else:
                desc = 'Word (%i,%i,%s,%s,%i)' % move[1:]
            moves.append('%i. <font color=%s>%s</font>' % (i + 1, player.color,
                                                           desc))
        self.moves.setText('<br>'.join(moves))

        super().update(*args, **kwargs)

    def letterChanged(self):
        ''' As soon as a letter changes we need to en/disable all controls '''
        self.exchange_button.setEnabled(False)
        self.exchange_button.setText('Exchange')
        if self.game.letters.remaining_letters >= self.game.rack_size:
            selected = ''.join(l.char for l in self.scene.items()
                               if type(l) is LetterItem and l.selected)
            if selected:
                self.exchange_button.setText('Exchange: %s' % selected)
                self.exchange_button.setEnabled(True)
        self.pass_button.setEnabled(True)
        self.continue_button.setEnabled(True if self.board.validNewWord() else
                                        False)

    def playerNext(self):
        player = self.game.next_player()

        self.letterChanged()
        self.update()
        player.update_letters()

        self.rack.name = self.game.current_player.name
        self.rack.color = self.game.current_player.color

        for i,letter in enumerate(player.letters):
            item = LetterItem(letter, self.game.letters.get_score(letter),
                              player.color)
            item.own(self.rack, i, move=False)
            self.scene.addItem(item)

        self.update()
        player.played_cb = self.playerDone
        player.play()

    def continueClicked(self):
        if type(self.game.current_player) is Human:
            self.game.current_player.continue_cb()

    def passClicked(self):
        if type(self.game.current_player) is Human:
            self.game.current_player.pass_cb()

    def exchangeClicked(self):
        if type(self.game.current_player) is Human:
            self.game.current_player.exchange_cb()

    def playerDone(self, player, move, *args):
        self.exchange_button.setEnabled(False)
        self.exchange_button.setText('Exchange')
        self.pass_button.setEnabled(False)
        self.continue_button.setEnabled(False)

        for item in self.scene.items():
            if type(item) is LetterItem and not item.is_safe and \
               not item.deleted:
                item.own(None)
                item.fade()

        for x,y,letter in self.game.board:
            if not self.board.getLetter(x, y):
                item = LetterItem(letter.char,
                                  self.game.letters.get_score(letter),
                                  letter.player.color, safe=True)
                item.own(self.board, x, y, move=False)
                self.scene.addItem(item)

        self.update()

        if self.game.state() == self.game.RUNNING:
            self.game.current_player.update_letters()
            self.playerNext()
        else:
            self.game.finish_score()
            self.update()
            self.gameOver()
        
    def getLetters(self, count, msg=''):
        print('random letters: %s' % self.game.get_letters_old(count))
        while True:
            text,ok = QInputDialog.getText(self, 'New Letters',
                'Player: <font color=%s>%s</font><br>' % (
                self.game.current_player.color, self.game.current_player.name)
                + msg + 'Tell me %i new letters in order to continue..' % count)

            text = ''.join(filter(lambda x: x in self.game.letters.letters,
                                  text.lower()))

            if len(text) == count and all(self.game.letters.available(c) for c
                                          in text):
                return text

    def gameOver(self):
        winner = sorted(self.game.players, reverse=True,
                        key=attrgetter('score'))[0]
        self.dialog = QMessageBox(QMessageBox.Information, 'Game Over',
            ('<b>Game Over!</b><br><br>The player ' +
             '<b><font color=%s>%s</font></b> has won!') %
            (winner.color, winner.name),
            QMessageBox.Ok, self)
        self.dialog.show()
class OWQualityControl(widget.OWWidget):
    name = "Quality Control"
    description = "Experiment quality control"
    icon = "../widgets/icons/QualityControl.svg"
    priority = 5000

    inputs = [("Experiment Data", Orange.data.Table, "set_data")]
    outputs = []

    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation", dist_pcorr),
                          ("Euclidean distance", dist_eucl),
                          ("Distance from Spearman correlation", dist_spearman)
                          ]

    settingsHandler = SetContextHandler()

    split_by_labels = settings.ContextSetting({})
    sort_by_labels = settings.ContextSetting({})

    selected_distance_index = settings.Setting(0)

    def __init__(self, parent=None):
        super().__init__(parent)

        ## Attributes
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None
        self.base_group_index = 0

        ## GUI
        box = gui.widgetBox(self.controlArea, "Info")
        self.info_box = gui.widgetLabel(box, "\n")

        ## Separate By box
        box = gui.widgetBox(self.controlArea, "Separate By")
        self.split_by_model = itemmodels.PyListModel(parent=self)
        self.split_by_view = QListView()
        self.split_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.split_by_view.setModel(self.split_by_model)
        box.layout().addWidget(self.split_by_view)

        self.split_by_view.selectionModel().selectionChanged.connect(
            self.on_split_key_changed)

        ## Sort By box
        box = gui.widgetBox(self.controlArea, "Sort By")
        self.sort_by_model = itemmodels.PyListModel(parent=self)
        self.sort_by_view = QListView()
        self.sort_by_view.setSelectionMode(QListView.ExtendedSelection)
        self.sort_by_view.setModel(self.sort_by_model)
        box.layout().addWidget(self.sort_by_view)

        self.sort_by_view.selectionModel().selectionChanged.connect(
            self.on_sort_key_changed)

        ## Distance box
        box = gui.widgetBox(self.controlArea, "Distance Measure")
        gui.comboBox(box,
                     self,
                     "selected_distance_index",
                     items=[name for name, _ in self.DISTANCE_FUNCTIONS],
                     callback=self.on_distance_measure_changed)

        self.scene = QGraphicsScene()
        self.scene_view = QGraphicsView(self.scene)
        self.scene_view.setRenderHints(QPainter.Antialiasing)
        self.scene_view.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.mainArea.layout().addWidget(self.scene_view)

        self.scene_view.installEventFilter(self)

        self._disable_updates = False
        self._cached_distances = {}
        self._base_index_hints = {}
        self.main_widget = None

        self.resize(800, 600)

    def clear(self):
        """Clear the widget state."""
        self.data = None
        self.distances = None
        self.groups = None
        self.unique_pos = None

        with disable_updates(self):
            self.split_by_model[:] = []
            self.sort_by_model[:] = []

        self.main_widget = None
        self.scene.clear()
        self.info_box.setText("\n")
        self._cached_distances = {}

    def set_data(self, data=None):
        """Set input experiment data."""
        self.closeContext()
        self.clear()

        self.error(0)
        self.warning(0)

        if data is not None:
            keys = self.get_suitable_keys(data)
            if not keys:
                self.error(0, "Data has no suitable feature labels.")
                data = None

        self.data = data
        if data is not None:
            self.on_new_data()

    def update_label_candidates(self):
        """Update the label candidates selection GUI 
        (Group/Sort By views).

        """
        keys = self.get_suitable_keys(self.data)
        with disable_updates(self):
            self.split_by_model[:] = keys
            self.sort_by_model[:] = keys

    def get_suitable_keys(self, data):
        """ Return suitable attr label keys from the data where
        the key has at least two unique values in the data.

        """
        attrs = [attr.attributes.items() for attr in data.domain.attributes]
        attrs = reduce(operator.iadd, attrs, [])
        # in case someone put non string values in attributes dict
        attrs = [(str(key), str(value)) for key, value in attrs]
        attrs = set(attrs)
        values = defaultdict(set)
        for key, value in attrs:
            values[key].add(value)
        keys = [key for key in values if len(values[key]) > 1]
        return keys

    def selected_split_by_labels(self):
        """Return the current selected split labels.
        """
        sel_m = self.split_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_sort_by_labels(self):
        """Return the current selected sort labels
        """
        sel_m = self.sort_by_view.selectionModel()
        indices = [r.row() for r in sel_m.selectedRows()]
        return [self.sort_by_model[i] for i in indices]

    def selected_distance(self):
        """Return the selected distance function.
        """
        return self.DISTANCE_FUNCTIONS[self.selected_distance_index][1]

    def selected_base_group_index(self):
        """Return the selected base group index
        """
        return self.base_group_index

    def selected_base_indices(self, base_group_index=None):
        indices = []
        for g, ind in self.groups:
            if base_group_index is None:
                label = group_label(self.selected_split_by_labels(), g)
                ind = [i for i in ind if i is not None]
                i = self._base_index_hints.get(label, ind[0] if ind else None)
            else:
                i = ind[base_group_index]
            indices.append(i)
        return indices

    def on_new_data(self):
        """We have new data and need to recompute all.
        """
        self.closeContext()

        self.update_label_candidates()
        self.info_box.setText(
            "%s genes \n%s experiments" %
            (len(self.data), len(self.data.domain.attributes)))

        self.base_group_index = 0

        keys = self.get_suitable_keys(self.data)
        self.openContext(keys)

        ## Restore saved context settings (split/sort selection)
        split_by_labels = self.split_by_labels
        sort_by_labels = self.sort_by_labels

        def select(model, selection_model, selected_items):
            """Select items in a Qt item model view
            """
            all_items = list(model)
            try:
                indices = [all_items.index(item) for item in selected_items]
            except:
                indices = []
            for ind in indices:
                selection_model.select(model.index(ind),
                                       QItemSelectionModel.Select)

        with disable_updates(self):
            select(self.split_by_view.model(),
                   self.split_by_view.selectionModel(), split_by_labels)

            select(self.sort_by_view.model(),
                   self.sort_by_view.selectionModel(), sort_by_labels)

        with widget_disable(self):
            self.split_and_update()

    def on_split_key_changed(self, *args):
        """Split key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.split_by_labels = self.selected_split_by_labels()
                self.split_and_update()

    def on_sort_key_changed(self, *args):
        """Sort key has changed
        """
        with widget_disable(self):
            if not self._disable_updates:
                self.base_group_index = 0
                self.sort_by_labels = self.selected_sort_by_labels()
                self.split_and_update()

    def on_distance_measure_changed(self):
        """Distance measure has changed
        """
        if self.data is not None:
            with widget_disable(self):
                self.update_distances()
                self.replot_experiments()

    def on_view_resize(self, size):
        """The view with the quality plot has changed
        """
        if self.main_widget:
            current = self.main_widget.size()
            self.main_widget.resize(size.width() - 6, current.height())

            self.scene.setSceneRect(self.scene.itemsBoundingRect())

    def on_rug_item_clicked(self, item):
        """An ``item`` in the quality plot has been clicked.
        """
        update = False
        sort_by_labels = self.selected_sort_by_labels()
        if sort_by_labels and item.in_group:
            ## The item is part of the group
            if item.group_index != self.base_group_index:
                self.base_group_index = item.group_index
                update = True

        else:
            if sort_by_labels:
                # If the user clicked on an background item it
                # invalidates the sorted labels selection
                with disable_updates(self):
                    self.sort_by_view.selectionModel().clear()
                    update = True

            index = item.index
            group = item.group
            label = group_label(self.selected_split_by_labels(), group)

            if self._base_index_hints.get(label, 0) != index:
                self._base_index_hints[label] = index
                update = True

        if update:
            with widget_disable(self):
                self.split_and_update()

    def eventFilter(self, obj, event):
        if obj is self.scene_view and event.type() == QEvent.Resize:
            self.on_view_resize(event.size())
        return super().eventFilter(obj, event)

    def split_and_update(self):
        """
        Split the data based on the selected sort/split labels
        and update the quality plot.

        """
        split_labels = self.selected_split_by_labels()
        sort_labels = self.selected_sort_by_labels()

        self.warning(0)
        if not split_labels:
            self.warning(0, "No separate by label selected.")

        self.groups, self.unique_pos = \
                exp.separate_by(self.data, split_labels,
                                consider=sort_labels,
                                add_empty=True)

        self.groups = sorted(self.groups.items(),
                             key=lambda t: list(map(float_if_posible, t[0])))
        self.unique_pos = sorted(
            self.unique_pos.items(),
            key=lambda t: list(map(float_if_posible, t[0])))

        if self.groups:
            if sort_labels:
                group_base = self.selected_base_group_index()
                base_indices = self.selected_base_indices(group_base)
            else:
                base_indices = self.selected_base_indices()
            self.update_distances(base_indices)
            self.replot_experiments()

    def get_cached_distances(self, measure):
        if measure not in self._cached_distances:
            attrs = self.data.domain.attributes
            mat = numpy.zeros((len(attrs), len(attrs)))

            self._cached_distances[measure] = \
                (mat, set(zip(range(len(attrs)), range(len(attrs)))))

        return self._cached_distances[measure]

    def get_cached_distance(self, measure, i, j):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        if key in computed:
            return matrix[i, j]
        else:
            return None

    def get_distance(self, measure, i, j):
        d = self.get_cached_distance(measure, i, j)
        if d is None:
            vec_i = take_columns(self.data, [i])
            vec_j = take_columns(self.data, [j])
            d = measure(vec_i, vec_j)

            mat, computed = self.get_cached_distances(measure)
            mat[i, j] = d
            key = key = (i, j) if i < j else (j, i)
            computed.add(key)
        return d

    def store_distance(self, measure, i, j, dist):
        matrix, computed = self.get_cached_distances(measure)
        key = (i, j) if i < j else (j, i)
        matrix[j, i] = matrix[i, j] = dist
        computed.add(key)

    def update_distances(self, base_indices=()):
        """Recompute the experiment distances.
        """
        distance = self.selected_distance()
        if base_indices == ():
            base_group_index = self.selected_base_group_index()
            base_indices = [ind[base_group_index] \
                            for _, ind in self.groups]

        assert (len(base_indices) == len(self.groups))

        base_distances = []
        attributes = self.data.domain.attributes
        pb = gui.ProgressBar(self, len(self.groups) * len(attributes))

        for (group, indices), base_index in zip(self.groups, base_indices):
            # Base column of the group
            if base_index is not None:
                base_vec = take_columns(self.data, [base_index])
                distances = []
                # Compute the distances between base column
                # and all the rest data columns.
                for i in range(len(attributes)):
                    if i == base_index:
                        distances.append(0.0)
                    elif self.get_cached_distance(distance, i,
                                                  base_index) is not None:
                        distances.append(
                            self.get_cached_distance(distance, i, base_index))
                    else:
                        vec_i = take_columns(self.data, [i])
                        dist = distance(base_vec, vec_i)
                        self.store_distance(distance, i, base_index, dist)
                        distances.append(dist)
                    pb.advance()

                base_distances.append(distances)
            else:
                base_distances.append(None)

        pb.finish()
        self.distances = base_distances

    def replot_experiments(self):
        """Replot the whole quality plot.
        """
        self.scene.clear()
        labels = []

        max_dist = numpy.nanmax(list(filter(None, self.distances)))
        rug_widgets = []

        group_pen = QPen(Qt.black)
        group_pen.setWidth(2)
        group_pen.setCapStyle(Qt.RoundCap)
        background_pen = QPen(QColor(0, 0, 250, 150))
        background_pen.setWidth(1)
        background_pen.setCapStyle(Qt.RoundCap)

        main_widget = QGraphicsWidget()
        layout = QGraphicsGridLayout()
        attributes = self.data.domain.attributes
        if self.data is not None:
            for (group, indices), dist_vec in zip(self.groups, self.distances):
                indices_set = set(indices)
                rug_items = []
                if dist_vec is not None:
                    for i, attr in enumerate(attributes):
                        # Is this a within group distance or background
                        in_group = i in indices_set
                        if in_group:
                            rug_item = ClickableRugItem(
                                dist_vec[i] / max_dist, 1.0,
                                self.on_rug_item_clicked)
                            rug_item.setPen(group_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)
                            rug_item.group_index = indices.index(i)
                            rug_item.setZValue(rug_item.zValue() + 1)
                        else:
                            rug_item = ClickableRugItem(
                                dist_vec[i] / max_dist, 0.85,
                                self.on_rug_item_clicked)
                            rug_item.setPen(background_pen)
                            tooltip = experiment_description(attr)
                            rug_item.setToolTip(tooltip)

                        rug_item.group = group
                        rug_item.index = i
                        rug_item.in_group = in_group

                        rug_items.append(rug_item)

                rug_widget = RugGraphicsWidget(parent=main_widget)
                rug_widget.set_rug(rug_items)

                rug_widgets.append(rug_widget)

                label = group_label(self.selected_split_by_labels(), group)
                label_item = QGraphicsSimpleTextItem(label, main_widget)
                label_item = GraphicsSimpleTextLayoutItem(label_item,
                                                          parent=layout)
                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                labels.append(label_item)

        for i, (label, rug_w) in enumerate(zip(labels, rug_widgets)):
            layout.addItem(label, i, 0, Qt.AlignVCenter)
            layout.addItem(rug_w, i, 1)
            layout.setRowMaximumHeight(i, 30)

        main_widget.setLayout(layout)
        self.scene.addItem(main_widget)
        self.main_widget = main_widget
        self.rug_widgets = rug_widgets
        self.labels = labels
        self.on_view_resize(self.scene_view.size())