Beispiel #1
0
    def __updateState(self):
        """
        Update the widget with the new source/sink node signal descriptions.
        """
        widget = QGraphicsWidget()
        widget.setLayout(QGraphicsGridLayout())

        # Space between left and right anchors
        widget.layout().setHorizontalSpacing(50)

        left_node = EditLinksNode(self, direction=Qt.LeftToRight, node=self.source)

        left_node.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)

        right_node = EditLinksNode(self, direction=Qt.RightToLeft, node=self.sink)

        right_node.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)

        left_node.setMinimumWidth(150)
        right_node.setMinimumWidth(150)

        widget.layout().addItem(left_node, 0, 0)
        widget.layout().addItem(right_node, 0, 1)

        title_template = "<center><b>{0}<b></center>"

        left_title = GraphicsTextWidget(self)
        left_title.setHtml(title_template.format(escape(self.source.title)))
        left_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        right_title = GraphicsTextWidget(self)
        right_title.setHtml(title_template.format(escape(self.sink.title)))
        right_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        widget.layout().addItem(left_title, 1, 0, alignment=Qt.AlignHCenter | Qt.AlignTop)
        widget.layout().addItem(right_title, 1, 1, alignment=Qt.AlignHCenter | Qt.AlignTop)

        widget.setParentItem(self)

        max_w = max(left_node.sizeHint(Qt.PreferredSize).width(), right_node.sizeHint(Qt.PreferredSize).width())

        # fix same size
        left_node.setMinimumWidth(max_w)
        right_node.setMinimumWidth(max_w)
        left_title.setMinimumWidth(max_w)
        right_title.setMinimumWidth(max_w)

        self.layout().addItem(widget)
        self.layout().activate()

        self.sourceNodeWidget = left_node
        self.sinkNodeWidget = right_node
        self.sourceNodeTitle = left_title
        self.sinkNodeTitle = right_title
    def switchView(self, startup=False):
        if self.showsTabs:
            self.scrolled_panel_layout = QGraphicsLinearLayout(Qt.Vertical)
            scrolled_panel = QGraphicsWidget()
            scrolled_panel.setLayout(self.scrolled_panel_layout)
            self.scrolled_panel_layout.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
            self.scrolled_panel_layout.addItem(self.source_panel)
            self.scrolled_panel_layout.addItem(self.sink_panel)
        else:
            scrolled_panel = Plasma.TabBar()
            scrolled_panel.addTab(i18n("Playback"), self.sink_panel)
            scrolled_panel.addTab(i18n("Record"), self.source_panel)
            self.source_panel.show()

        self.scrolled_panel = scrolled_panel
        self.showsTabs = not self.showsTabs
        self.scroller.setWidget(self.scrolled_panel)
        if not startup:
            self.check_geometries()
Beispiel #3
0
class OWHierarchicalClustering(widget.OWWidget):
    name = "Hierarchical Clustering"
    description = ("Hierarchical clustering based on distance matrix, and "
                   "a dendrogram viewer.")
    icon = "icons/HierarchicalClustering.svg"
    priority = 2100

    inputs = [("Distances", Orange.misc.DistMatrix, "set_distances")]

    outputs = [("Selected Data", Orange.data.Table),
               ("Other Data", Orange.data.Table)]

    #: Selected linkage
    linkage = settings.Setting(1)
    #: Index of the selected annotation item (variable, ...)
    annotation_idx = settings.Setting(0)
    #: Selected tree pruning (none/max depth)
    pruning = settings.Setting(0)
    #: Maximum depth when max depth pruning is selected
    max_depth = settings.Setting(10)

    #: Selected cluster selection method (none, cut distance, top n)
    selection_method = settings.Setting(0)
    #: Cut height ratio wrt root height
    cut_ratio = settings.Setting(75.0)
    #: Number of top clusters to select
    top_n = settings.Setting(3)

    append_clusters = settings.Setting(True)
    cluster_role = settings.Setting(2)
    cluster_name = settings.Setting("Cluster")
    autocommit = settings.Setting(False)

    #: Cluster variable domain role
    AttributeRole, ClassRole, MetaRole = 0, 1, 2

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

        self.matrix = None
        self.items = None
        self.linkmatrix = None
        self.root = None
        self._displayed_root = None
        self.cutoff_height = 0.0
        self._invalidated = False

        gui.comboBox(gui.widgetBox(self.controlArea, "Linkage"),
                     self,
                     "linkage",
                     items=LINKAGE,
                     callback=self._invalidate_clustering)

        box = gui.widgetBox(self.controlArea, "Annotation")
        self.label_cb = gui.comboBox(box,
                                     self,
                                     "annotation_idx",
                                     callback=self._update_labels)

        self.label_cb.setModel(itemmodels.VariableListModel())
        self.label_cb.model()[:] = ["None", "Enumeration"]

        box = gui.radioButtons(self.controlArea,
                               self,
                               "pruning",
                               box="Pruning",
                               callback=self._invalidate_pruning)
        grid = QGridLayout()
        box.layout().addLayout(grid)
        grid.addWidget(gui.appendRadioButton(box, "None", addToLayout=False),
                       0, 0)
        self.max_depth_spin = gui.spin(box,
                                       self,
                                       "max_depth",
                                       minv=1,
                                       maxv=100,
                                       callback=self._invalidate_pruning,
                                       keyboardTracking=False)

        grid.addWidget(
            gui.appendRadioButton(box, "Max depth", addToLayout=False), 1, 0)
        grid.addWidget(self.max_depth_spin, 1, 1)

        box = gui.radioButtons(self.controlArea,
                               self,
                               "selection_method",
                               box="Selection",
                               callback=self._selection_method_changed)

        grid = QGridLayout()
        box.layout().addLayout(grid)
        grid.addWidget(gui.appendRadioButton(box, "Manual", addToLayout=False),
                       0, 0)
        grid.addWidget(
            gui.appendRadioButton(box, "Height ratio", addToLayout=False), 1,
            0)
        self.cut_ratio_spin = gui.spin(box,
                                       self,
                                       "cut_ratio",
                                       0,
                                       100,
                                       step=1e-1,
                                       spinType=float,
                                       callback=self._selection_method_changed)
        self.cut_ratio_spin.setSuffix("%")

        grid.addWidget(self.cut_ratio_spin, 1, 1)

        grid.addWidget(gui.appendRadioButton(box, "Top N", addToLayout=False),
                       2, 0)
        self.top_n_spin = gui.spin(box,
                                   self,
                                   "top_n",
                                   1,
                                   20,
                                   callback=self._selection_method_changed)
        grid.addWidget(self.top_n_spin, 2, 1)
        box.layout().addLayout(grid)

        self.controlArea.layout().addStretch()

        box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_clusters",
                     "Append cluster IDs",
                     callback=self._invalidate_output)

        ibox = gui.indentedBox(box)
        name_edit = gui.lineEdit(ibox, self, "cluster_name")
        name_edit.editingFinished.connect(self._invalidate_output)

        cb = gui.comboBox(
            ibox,
            self,
            "cluster_role",
            callback=self._invalidate_output,
            items=["Attribute", "Class variable", "Meta variable"])
        form = QFormLayout(fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow,
                           labelAlignment=Qt.AlignLeft,
                           spacing=8)
        form.addRow("Name", name_edit)
        form.addRow("Place", cb)

        ibox.layout().addSpacing(5)
        ibox.layout().addLayout(form)
        ibox.layout().addSpacing(5)

        cb = gui.checkBox(box, self, "autocommit", "Commit automatically")
        b = gui.button(box, self, "Commit", callback=self.commit, default=True)
        gui.setStopper(self, b, cb, "_invalidated", callback=self.commit)

        self.scene = QGraphicsScene()
        self.view = QGraphicsView(
            self.scene,
            horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
            alignment=Qt.AlignLeft | Qt.AlignVCenter)

        def axis_view(orientation):
            ax = pg.AxisItem(orientation=orientation, maxTickLength=7)
            scene = QGraphicsScene()
            scene.addItem(ax)
            view = QGraphicsView(
                scene,
                horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
                verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
                alignment=Qt.AlignLeft | Qt.AlignVCenter)
            view.setFixedHeight(ax.size().height())
            ax.line = SliderLine(orientation=Qt.Horizontal,
                                 length=ax.size().height())
            scene.addItem(ax.line)
            return view, ax

        self.top_axis_view, self.top_axis = axis_view("top")
        self.mainArea.layout().setSpacing(1)
        self.mainArea.layout().addWidget(self.top_axis_view)
        self.mainArea.layout().addWidget(self.view)
        self.bottom_axis_view, self.bottom_axis = axis_view("bottom")
        self.mainArea.layout().addWidget(self.bottom_axis_view)

        self._main_graphics = QGraphicsWidget()
        self._main_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self._main_layout.setSpacing(1)

        self._main_graphics.setLayout(self._main_layout)
        self.scene.addItem(self._main_graphics)

        self.dendrogram = DendrogramWidget()
        self.dendrogram.setSizePolicy(QSizePolicy.MinimumExpanding,
                                      QSizePolicy.MinimumExpanding)
        self.dendrogram.selectionChanged.connect(self._invalidate_output)
        self.dendrogram.selectionEdited.connect(self._selection_edited)

        fm = self.fontMetrics()
        self.dendrogram.setContentsMargins(5,
                                           fm.lineSpacing() / 2, 5,
                                           fm.lineSpacing() / 2)
        self.labels = GraphicsSimpleTextList()
        self.labels.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.labels.setAlignment(Qt.AlignLeft)
        self.labels.setMaximumWidth(200)
        self.labels.layout().setSpacing(0)

        self._main_layout.addItem(self.dendrogram)
        self._main_layout.addItem(self.labels)

        self._main_layout.setAlignment(self.dendrogram,
                                       Qt.AlignLeft | Qt.AlignVCenter)
        self._main_layout.setAlignment(self.labels,
                                       Qt.AlignLeft | Qt.AlignVCenter)

        self.view.viewport().installEventFilter(self)
        self.top_axis_view.viewport().installEventFilter(self)
        self.bottom_axis_view.viewport().installEventFilter(self)
        self._main_graphics.installEventFilter(self)

        self.cut_line = SliderLine(self.dendrogram, orientation=Qt.Horizontal)
        self.cut_line.valueChanged.connect(self._dendrogram_slider_changed)
        self.cut_line.hide()

        self.bottom_axis.line.valueChanged.connect(self._axis_slider_changed)
        self.top_axis.line.valueChanged.connect(self._axis_slider_changed)
        self.dendrogram.geometryChanged.connect(self._dendrogram_geom_changed)
        self._set_cut_line_visible(self.selection_method == 1)

    def set_distances(self, matrix):
        self.matrix = matrix
        self._invalidate_clustering()
        self._set_items(matrix.row_items if matrix is not None else None)

    def _set_items(self, items):
        self.items = items
        if items is None:
            self.label_cb.model()[:] = ["None", "Enumeration"]
        elif isinstance(items, Orange.data.Table):
            vars = list(items.domain)
            self.label_cb.model()[:] = ["None", "Enumeration"] + vars
        elif isinstance(items, list) and \
                all(isinstance(var, Orange.data.Variable) for var in items):
            self.label_cb.model()[:] = ["None", "Enumeration", "Name"]
        else:
            self.label_cb.model()[:] = ["None", "Enumeration"]
        self.annotation_idx = min(self.annotation_idx,
                                  len(self.label_cb.model()) - 1)

    def handleNewSignals(self):
        self._update_labels()

    def _clear_plot(self):
        self.labels.set_labels([])
        self.dendrogram.set_root(None)

    def _set_displayed_root(self, root):
        self._clear_plot()
        self._displayed_root = root
        self.dendrogram.set_root(root)

        self._update_labels()

        self._main_graphics.resize(
            self._main_graphics.size().width(),
            self._main_graphics.sizeHint(Qt.PreferredSize).height())
        self._main_graphics.layout().activate()

    def _update(self):
        self._clear_plot()

        distances = self.matrix

        if distances is not None:
            # Convert to flat upper triangular distances
            i, j = numpy.triu_indices(distances.X.shape[0], k=1)
            distances = distances.X[i, j]

            method = LINKAGE[self.linkage].lower()
            Z = scipy.cluster.hierarchy.linkage(distances, method=method)
            tree = tree_from_linkage(Z)
            self.linkmatrix = Z
            self.root = tree

            self.top_axis.setRange(tree.value.height, 0.0)
            self.bottom_axis.setRange(tree.value.height, 0.0)

            if self.pruning:
                self._set_displayed_root(prune(tree, level=self.max_depth))
            else:
                self._set_displayed_root(tree)
        else:
            self.linkmatrix = None
            self.root = None
            self._set_displayed_root(None)

        self._apply_selection()

    def _update_labels(self):
        labels = []
        if self.root and self._displayed_root:
            indices = [leaf.value.index for leaf in leaves(self.root)]

            if self.annotation_idx == 0:
                labels = []
            elif self.annotation_idx == 1:
                labels = [str(i) for i in indices]
            elif isinstance(self.items, Orange.data.Table):
                var = self.label_cb.model()[self.annotation_idx]
                col = self.items[:, var]
                labels = [var.repr_val(next(iter(row))) for row in col]
                labels = [labels[idx] for idx in indices]
            else:
                labels = []

            if labels and self._displayed_root is not self.root:
                joined = leaves(self._displayed_root)
                labels = [
                    ", ".join(labels[leaf.value.first:leaf.value.last])
                    for leaf in joined
                ]

        self.labels.set_labels(labels)
        self.labels.setMinimumWidth(1 if labels else -1)

    def _invalidate_clustering(self):
        self._update()
        self._update_labels()

    def _invalidate_output(self):
        self._invalidated = True
        if self.autocommit:
            self.commit()

    def _invalidate_pruning(self):
        if self.root:
            selection = self.dendrogram.selected_nodes()
            ranges = [node.value.range for node in selection]
            if self.pruning:
                self._set_displayed_root(prune(self.root,
                                               level=self.max_depth))
            else:
                self._set_displayed_root(self.root)
            selected = [
                node for node in preorder(self._displayed_root)
                if node.value.range in ranges
            ]

            self.dendrogram.set_selected_clusters(selected)

        self._apply_selection()

    def commit(self):
        self._invalidated = False

        items = getattr(self.matrix, "items", self.items)
        if not items:
            # nothing to commit
            return

        selection = self.dendrogram.selected_nodes()
        selection = sorted(selection, key=lambda c: c.value.first)

        indices = [leaf.value.index for leaf in leaves(self.root)]

        maps = [
            indices[node.value.first:node.value.last] for node in selection
        ]

        selected_indices = list(chain(*maps))
        unselected_indices = sorted(
            set(range(self.root.value.last)) - set(selected_indices))

        selected = [items[k] for k in selected_indices]
        unselected = [items[k] for k in unselected_indices]

        if not selected:
            self.send("Selected Data", None)
            self.send("Other Data", None)
            return
        selected_data = unselected_data = None

        if isinstance(items, Orange.data.Table):
            c = numpy.zeros(len(items))

            for i, indices in enumerate(maps):
                c[indices] = i
            c[unselected_indices] = len(maps)

            mask = c != len(maps)

            if self.append_clusters:
                clust_var = Orange.data.DiscreteVariable(
                    str(self.cluster_name),
                    values=[
                        "Cluster {}".format(i + 1) for i in range(len(maps))
                    ] + ["Other"])
                data, domain = items, items.domain

                attrs = domain.attributes
                class_ = domain.class_vars
                metas = domain.metas

                X, Y, M = data.X, data.Y, data.metas
                if self.cluster_role == self.AttributeRole:
                    attrs = attrs + (clust_var, )
                    X = numpy.c_[X, c]
                elif self.cluster_role == self.ClassRole:
                    class_ = class_ + (clust_var, )
                    Y = numpy.c_[Y, c]
                elif self.cluster_role == self.MetaRole:
                    metas = metas + (clust_var, )
                    M = numpy.c_[M, c]

                domain = Orange.data.Domain(attrs, class_, metas)
                data = Orange.data.Table(domain, X, Y, M)
            else:
                data = items

            if selected:
                selected_data = data[mask]
            if unselected:
                unselected_data = data[~mask]

        self.send("Selected Data", selected_data)
        self.send("Other Data", unselected_data)

    def sizeHint(self):
        return QSize(800, 500)

    def eventFilter(self, obj, event):
        if obj is self.view.viewport() and event.type() == QEvent.Resize:
            width = self.view.viewport().width() - 2
            self._main_graphics.setMaximumWidth(width)
            self._main_graphics.setMinimumWidth(width)
            self._main_graphics.layout().activate()
        elif event.type() == QEvent.MouseButtonPress and \
                (obj is self.top_axis_view.viewport() or
                 obj is self.bottom_axis_view.viewport()):
            self.selection_method = 1
            # Map click point to cut line local coordinates
            pos = self.top_axis_view.mapToScene(event.pos())
            cut = self.top_axis.line.mapFromScene(pos)
            self.top_axis.line.setValue(cut.x())
            # update the line visibility, output, ...
            self._selection_method_changed()

        return super().eventFilter(obj, event)

    def _dendrogram_geom_changed(self):
        pos = self.dendrogram.pos_at_height(self.cutoff_height)
        geom = self.dendrogram.geometry()
        crect = self.dendrogram.contentsRect()

        self._set_slider_value(pos.x(), geom.width())
        self.cut_line.setLength(geom.height())

        self.top_axis.resize(crect.width(), self.top_axis.height())
        self.top_axis.setPos(geom.left() + crect.left(), 0)
        self.top_axis.line.setPos(self.cut_line.scenePos().x(), 0)

        self.bottom_axis.resize(crect.width(), self.bottom_axis.height())
        self.bottom_axis.setPos(geom.left() + crect.left(), 0)
        self.bottom_axis.line.setPos(self.cut_line.scenePos().x(), 0)

        geom = self._main_graphics.geometry()
        assert geom.topLeft() == QPointF(0, 0)
        self.scene.setSceneRect(geom)

        geom.setHeight(self.top_axis.size().height())

        self.top_axis.scene().setSceneRect(geom)
        self.bottom_axis.scene().setSceneRect(geom)

    def _axis_slider_changed(self, value):
        self.cut_line.setValue(value)

    def _dendrogram_slider_changed(self, value):
        p = QPointF(value, 0)
        cl_height = self.dendrogram.height_at(p)

        self.set_cutoff_height(cl_height)

        # Sync the cut positions between the dendrogram and the axis.
        self._set_slider_value(value, self.dendrogram.size().width())

    def _set_slider_value(self, value, span):
        with blocked(self.cut_line):
            self.cut_line.setValue(value)
            self.cut_line.setRange(0, span)

        with blocked(self.top_axis.line):
            self.top_axis.line.setValue(value)
            self.top_axis.line.setRange(0, span)

        with blocked(self.bottom_axis.line):
            self.bottom_axis.line.setValue(value)
            self.bottom_axis.line.setRange(0, span)

    def set_cutoff_height(self, height):
        self.cutoff_height = height
        if self.root:
            self.cut_ratio = 100 * height / self.root.value.height
        self.select_max_height(height)

    def _set_cut_line_visible(self, visible):
        self.cut_line.setVisible(visible)
        self.top_axis.line.setVisible(visible)
        self.bottom_axis.line.setVisible(visible)

    def select_top_n(self, n):
        root = self._displayed_root
        if root:
            clusters = top_clusters(root, n)
            self.dendrogram.set_selected_clusters(clusters)

    def select_max_height(self, height):
        root = self._displayed_root
        if root:
            clusters = clusters_at_height(root, height)
            self.dendrogram.set_selected_clusters(clusters)

    def _selection_method_changed(self):
        self._set_cut_line_visible(self.selection_method == 1)
        if self.root:
            self._apply_selection()

    def _apply_selection(self):
        if not self.root:
            return

        if self.selection_method == 0:
            pass
        elif self.selection_method == 1:
            height = self.cut_ratio * self.root.value.height / 100
            self.set_cutoff_height(height)
            pos = self.dendrogram.pos_at_height(height)
            self._set_slider_value(pos.x(), self.dendrogram.size().width())
        elif self.selection_method == 2:
            self.select_top_n(self.top_n)

    def _selection_edited(self):
        # Selection was edited by clicking on a cluster in the
        # dendrogram view.
        self.selection_method = 0
        self._selection_method_changed()
class OWHierarchicalClustering(widget.OWWidget):
    name = "Hierarchical Clustering"
    description = ("Hierarchical clustering based on distance matrix, and "
                   "a dendrogram viewer.")
    icon = "icons/HierarchicalClustering.svg"
    priority = 2100

    inputs = [("Distances", Orange.misc.DistMatrix, "set_distances")]

    outputs = [("Selected Data", Orange.data.Table),
               ("Other Data", Orange.data.Table)]

    #: Selected linkage
    linkage = settings.Setting(1)
    #: Index of the selected annotation item (variable, ...)
    annotation_idx = settings.Setting(0)
    #: Selected tree pruning (none/max depth)
    pruning = settings.Setting(0)
    #: Maximum depth when max depth pruning is selected
    max_depth = settings.Setting(10)

    #: Selected cluster selection method (none, cut distance, top n)
    selection_method = settings.Setting(0)
    #: Cut height ratio wrt root height
    cut_ratio = settings.Setting(75.0)
    #: Number of top clusters to select
    top_n = settings.Setting(3)

    append_clusters = settings.Setting(True)
    cluster_role = settings.Setting(2)
    cluster_name = settings.Setting("Cluster")
    autocommit = settings.Setting(False)

    #: Cluster variable domain role
    AttributeRole, ClassRole, MetaRole = 0, 1, 2

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

        self.matrix = None
        self.items = None
        self.linkmatrix = None
        self.root = None
        self._displayed_root = None
        self.cutoff_height = 0.0

        gui.comboBox(gui.widgetBox(self.controlArea, "Linkage"),
                     self, "linkage", items=LINKAGE,
                     callback=self._invalidate_clustering)

        box = gui.widgetBox(self.controlArea, "Annotation")
        self.label_cb = gui.comboBox(
            box, self, "annotation_idx", callback=self._update_labels)

        self.label_cb.setModel(itemmodels.VariableListModel())
        self.label_cb.model()[:] = ["None", "Enumeration"]

        box = gui.radioButtons(
            self.controlArea, self, "pruning", box="Pruning",
            callback=self._invalidate_pruning
        )
        grid = QGridLayout()
        box.layout().addLayout(grid)
        grid.addWidget(
            gui.appendRadioButton(box, "None", addToLayout=False),
            0, 0
        )
        self.max_depth_spin = gui.spin(
            box, self, "max_depth", minv=1, maxv=100,
            callback=self._invalidate_pruning,
            keyboardTracking=False
        )

        grid.addWidget(
            gui.appendRadioButton(box, "Max depth", addToLayout=False),
            1, 0)
        grid.addWidget(self.max_depth_spin, 1, 1)

        box = gui.radioButtons(
            self.controlArea, self, "selection_method",
            box="Selection",
            callback=self._selection_method_changed)

        grid = QGridLayout()
        box.layout().addLayout(grid)
        grid.addWidget(
            gui.appendRadioButton(box, "Manual", addToLayout=False),
            0, 0
        )
        grid.addWidget(
            gui.appendRadioButton(box, "Height ratio", addToLayout=False),
            1, 0
        )
        self.cut_ratio_spin = gui.spin(
            box, self, "cut_ratio", 0, 100, step=1e-1, spinType=float,
            callback=self._selection_method_changed
        )
        self.cut_ratio_spin.setSuffix("%")

        grid.addWidget(self.cut_ratio_spin, 1, 1)

        grid.addWidget(
            gui.appendRadioButton(box, "Top N", addToLayout=False),
            2, 0
        )
        self.top_n_spin = gui.spin(box, self, "top_n", 1, 20,
                                   callback=self._selection_method_changed)
        grid.addWidget(self.top_n_spin, 2, 1)
        box.layout().addLayout(grid)

        self.controlArea.layout().addStretch()

        box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_clusters", "Append cluster IDs",
                     callback=self._invalidate_output)

        ibox = gui.indentedBox(box)
        name_edit = gui.lineEdit(ibox, self, "cluster_name")
        name_edit.editingFinished.connect(self._invalidate_output)

        cb = gui.comboBox(
            ibox, self, "cluster_role", callback=self._invalidate_output,
            items=["Attribute",
                   "Class variable",
                   "Meta variable"]
        )
        form = QFormLayout(
            fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow,
            labelAlignment=Qt.AlignLeft,
            spacing=8
        )
        form.addRow("Name", name_edit)
        form.addRow("Place", cb)

        ibox.layout().addSpacing(5)
        ibox.layout().addLayout(form)
        ibox.layout().addSpacing(5)

        gui.auto_commit(box, self, "autocommit", "Send data", "Auto send is on",
                        box=False)

        self.scene = QGraphicsScene()
        self.view = QGraphicsView(
            self.scene,
            horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
            verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
            alignment=Qt.AlignLeft | Qt.AlignVCenter
        )

        def axis_view(orientation):
            ax = pg.AxisItem(orientation=orientation, maxTickLength=7)
            scene = QGraphicsScene()
            scene.addItem(ax)
            view = QGraphicsView(
                scene,
                horizontalScrollBarPolicy=Qt.ScrollBarAlwaysOff,
                verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn,
                alignment=Qt.AlignLeft | Qt.AlignVCenter
            )
            view.setFixedHeight(ax.size().height())
            ax.line = SliderLine(orientation=Qt.Horizontal,
                                 length=ax.size().height())
            scene.addItem(ax.line)
            return view, ax

        self.top_axis_view, self.top_axis = axis_view("top")
        self.mainArea.layout().setSpacing(1)
        self.mainArea.layout().addWidget(self.top_axis_view)
        self.mainArea.layout().addWidget(self.view)
        self.bottom_axis_view, self.bottom_axis = axis_view("bottom")
        self.mainArea.layout().addWidget(self.bottom_axis_view)

        self._main_graphics = QGraphicsWidget()
        self._main_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self._main_layout.setSpacing(1)

        self._main_graphics.setLayout(self._main_layout)
        self.scene.addItem(self._main_graphics)

        self.dendrogram = DendrogramWidget()
        self.dendrogram.setSizePolicy(QSizePolicy.MinimumExpanding,
                                      QSizePolicy.MinimumExpanding)
        self.dendrogram.selectionChanged.connect(self._invalidate_output)
        self.dendrogram.selectionEdited.connect(self._selection_edited)

        fm = self.fontMetrics()
        self.dendrogram.setContentsMargins(
            5, fm.lineSpacing() / 2,
            5, fm.lineSpacing() / 2
        )
        self.labels = GraphicsSimpleTextList()
        self.labels.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.labels.setAlignment(Qt.AlignLeft)
        self.labels.setMaximumWidth(200)
        self.labels.layout().setSpacing(0)

        self._main_layout.addItem(self.dendrogram)
        self._main_layout.addItem(self.labels)

        self._main_layout.setAlignment(
            self.dendrogram, Qt.AlignLeft | Qt.AlignVCenter)
        self._main_layout.setAlignment(
            self.labels, Qt.AlignLeft | Qt.AlignVCenter)

        self.view.viewport().installEventFilter(self)
        self.top_axis_view.viewport().installEventFilter(self)
        self.bottom_axis_view.viewport().installEventFilter(self)
        self._main_graphics.installEventFilter(self)

        self.cut_line = SliderLine(self.dendrogram,
                                   orientation=Qt.Horizontal)
        self.cut_line.valueChanged.connect(self._dendrogram_slider_changed)
        self.cut_line.hide()

        self.bottom_axis.line.valueChanged.connect(self._axis_slider_changed)
        self.top_axis.line.valueChanged.connect(self._axis_slider_changed)
        self.dendrogram.geometryChanged.connect(self._dendrogram_geom_changed)
        self._set_cut_line_visible(self.selection_method == 1)

    def set_distances(self, matrix):
        self.matrix = matrix
        self._invalidate_clustering()
        self._set_items(matrix.row_items if matrix is not None else None)

    def _set_items(self, items):
        self.items = items
        if items is None:
            self.label_cb.model()[:] = ["None", "Enumeration"]
        elif isinstance(items, Orange.data.Table):
            vars = list(items.domain)
            self.label_cb.model()[:] = ["None", "Enumeration"] + vars
        elif isinstance(items, list) and \
                all(isinstance(var, Orange.data.Variable) for var in items):
            self.label_cb.model()[:] = ["None", "Enumeration", "Name"]
        else:
            self.label_cb.model()[:] = ["None", "Enumeration"]
        self.annotation_idx = min(self.annotation_idx,
                                  len(self.label_cb.model()) - 1)

    def handleNewSignals(self):
        self._update_labels()

    def _clear_plot(self):
        self.labels.set_labels([])
        self.dendrogram.set_root(None)

    def _set_displayed_root(self, root):
        self._clear_plot()
        self._displayed_root = root
        self.dendrogram.set_root(root)

        self._update_labels()

        self._main_graphics.resize(
            self._main_graphics.size().width(),
            self._main_graphics.sizeHint(Qt.PreferredSize).height()
        )
        self._main_graphics.layout().activate()

    def _update(self):
        self._clear_plot()

        distances = self.matrix

        if distances is not None:
            # Convert to flat upper triangular distances
            i, j = numpy.triu_indices(distances.X.shape[0], k=1)
            distances = distances.X[i, j]

            method = LINKAGE[self.linkage].lower()
            Z = scipy.cluster.hierarchy.linkage(
                distances, method=method
            )
            tree = tree_from_linkage(Z)
            self.linkmatrix = Z
            self.root = tree

            self.top_axis.setRange(tree.value.height, 0.0)
            self.bottom_axis.setRange(tree.value.height, 0.0)

            if self.pruning:
                self._set_displayed_root(prune(tree, level=self.max_depth))
            else:
                self._set_displayed_root(tree)
        else:
            self.linkmatrix = None
            self.root = None
            self._set_displayed_root(None)

        self._apply_selection()

    def _update_labels(self):
        labels = []
        if self.root and self._displayed_root:
            indices = [leaf.value.index for leaf in leaves(self.root)]

            if self.annotation_idx == 0:
                labels = []
            elif self.annotation_idx == 1:
                labels = [str(i) for i in indices]
            elif isinstance(self.items, Orange.data.Table):
                var = self.label_cb.model()[self.annotation_idx]
                col = self.items[:, var]
                labels = [var.repr_val(next(iter(row))) for row in col]
                labels = [labels[idx] for idx in indices]
            else:
                labels = []

            if labels and self._displayed_root is not self.root:
                joined = leaves(self._displayed_root)
                labels = [", ".join(labels[leaf.value.first: leaf.value.last])
                          for leaf in joined]

        self.labels.set_labels(labels)
        self.labels.setMinimumWidth(1 if labels else -1)

    def _invalidate_clustering(self):
        self._update()
        self._update_labels()

    def _invalidate_output(self):
        self.commit()

    def _invalidate_pruning(self):
        if self.root:
            selection = self.dendrogram.selected_nodes()
            ranges = [node.value.range for node in selection]
            if self.pruning:
                self._set_displayed_root(
                    prune(self.root, level=self.max_depth))
            else:
                self._set_displayed_root(self.root)
            selected = [node for node in preorder(self._displayed_root)
                        if node.value.range in ranges]

            self.dendrogram.set_selected_clusters(selected)

        self._apply_selection()

    def commit(self):
        items = getattr(self.matrix, "items", self.items)
        if not items:
            # nothing to commit
            return

        selection = self.dendrogram.selected_nodes()
        selection = sorted(selection, key=lambda c: c.value.first)

        indices = [leaf.value.index for leaf in leaves(self.root)]

        maps = [indices[node.value.first:node.value.last]
                for node in selection]

        selected_indices = list(chain(*maps))
        unselected_indices = sorted(set(range(self.root.value.last)) -
                                    set(selected_indices))

        selected = [items[k] for k in selected_indices]
        unselected = [items[k] for k in unselected_indices]

        if not selected:
            self.send("Selected Data", None)
            self.send("Other Data", None)
            return
        selected_data = unselected_data = None

        if isinstance(items, Orange.data.Table):
            c = numpy.zeros(len(items))

            for i, indices in enumerate(maps):
                c[indices] = i
            c[unselected_indices] = len(maps)

            mask = c != len(maps)

            if self.append_clusters:
                clust_var = Orange.data.DiscreteVariable(
                    str(self.cluster_name),
                    values=["Cluster {}".format(i + 1)
                            for i in range(len(maps))] +
                           ["Other"], ordered=True
                )
                data, domain = items, items.domain

                attrs = domain.attributes
                class_ = domain.class_vars
                metas = domain.metas

                if self.cluster_role == self.AttributeRole:
                    attrs = attrs + (clust_var,)
                elif self.cluster_role == self.ClassRole:
                    class_ = class_ + (clust_var,)
                elif self.cluster_role == self.MetaRole:
                    metas = metas + (clust_var,)

                domain = Orange.data.Domain(attrs, class_, metas)
                data = Orange.data.Table(domain, data)
                data.get_column_view(clust_var)[0][:] = c
            else:
                data = items

            if selected:
                selected_data = data[mask]
            if unselected:
                unselected_data = data[~mask]

        self.send("Selected Data", selected_data)
        self.send("Other Data", unselected_data)

    def sizeHint(self):
        return QSize(800, 500)

    def eventFilter(self, obj, event):
        if obj is self.view.viewport() and event.type() == QEvent.Resize:
            width = self.view.viewport().width() - 2
            self._main_graphics.setMaximumWidth(width)
            self._main_graphics.setMinimumWidth(width)
            self._main_graphics.layout().activate()
        elif event.type() == QEvent.MouseButtonPress and \
                (obj is self.top_axis_view.viewport() or
                 obj is self.bottom_axis_view.viewport()):
            self.selection_method = 1
            # Map click point to cut line local coordinates
            pos = self.top_axis_view.mapToScene(event.pos())
            cut = self.top_axis.line.mapFromScene(pos)
            self.top_axis.line.setValue(cut.x())
            # update the line visibility, output, ...
            self._selection_method_changed()

        return super().eventFilter(obj, event)

    def _dendrogram_geom_changed(self):
        pos = self.dendrogram.pos_at_height(self.cutoff_height)
        geom = self.dendrogram.geometry()
        crect = self.dendrogram.contentsRect()

        self._set_slider_value(pos.x(), geom.width())
        self.cut_line.setLength(geom.height())

        self.top_axis.resize(crect.width(), self.top_axis.height())
        self.top_axis.setPos(geom.left() + crect.left(), 0)
        self.top_axis.line.setPos(self.cut_line.scenePos().x(), 0)

        self.bottom_axis.resize(crect.width(), self.bottom_axis.height())
        self.bottom_axis.setPos(geom.left() + crect.left(), 0)
        self.bottom_axis.line.setPos(self.cut_line.scenePos().x(), 0)

        geom = self._main_graphics.geometry()
        assert geom.topLeft() == QPointF(0, 0)
        self.scene.setSceneRect(geom)

        geom.setHeight(self.top_axis.size().height())

        self.top_axis.scene().setSceneRect(geom)
        self.bottom_axis.scene().setSceneRect(geom)

    def _axis_slider_changed(self, value):
        self.cut_line.setValue(value)

    def _dendrogram_slider_changed(self, value):
        p = QPointF(value, 0)
        cl_height = self.dendrogram.height_at(p)

        self.set_cutoff_height(cl_height)

        # Sync the cut positions between the dendrogram and the axis.
        self._set_slider_value(value, self.dendrogram.size().width())

    def _set_slider_value(self, value, span):
        with blocked(self.cut_line):
            self.cut_line.setValue(value)
            self.cut_line.setRange(0, span)

        with blocked(self.top_axis.line):
            self.top_axis.line.setValue(value)
            self.top_axis.line.setRange(0, span)

        with blocked(self.bottom_axis.line):
            self.bottom_axis.line.setValue(value)
            self.bottom_axis.line.setRange(0, span)

    def set_cutoff_height(self, height):
        self.cutoff_height = height
        if self.root:
            self.cut_ratio = 100 * height / self.root.value.height
        self.select_max_height(height)

    def _set_cut_line_visible(self, visible):
        self.cut_line.setVisible(visible)
        self.top_axis.line.setVisible(visible)
        self.bottom_axis.line.setVisible(visible)

    def select_top_n(self, n):
        root = self._displayed_root
        if root:
            clusters = top_clusters(root, n)
            self.dendrogram.set_selected_clusters(clusters)

    def select_max_height(self, height):
        root = self._displayed_root
        if root:
            clusters = clusters_at_height(root, height)
            self.dendrogram.set_selected_clusters(clusters)

    def _selection_method_changed(self):
        self._set_cut_line_visible(self.selection_method == 1)
        if self.root:
            self._apply_selection()

    def _apply_selection(self):
        if not self.root:
            return

        if self.selection_method == 0:
            pass
        elif self.selection_method == 1:
            height = self.cut_ratio * self.root.value.height / 100
            self.set_cutoff_height(height)
            pos = self.dendrogram.pos_at_height(height)
            self._set_slider_value(pos.x(), self.dendrogram.size().width())
        elif self.selection_method == 2:
            self.select_top_n(self.top_n)

    def _selection_edited(self):
        # Selection was edited by clicking on a cluster in the
        # dendrogram view.
        self.selection_method = 0
        self._selection_method_changed()
Beispiel #5
0
    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())
class Channel(QGraphicsWidget):

    def __init__(self , parent):
        QGraphicsWidget.__init__(self)
        self.veromix = parent
        self.index = -1
        self.pa = parent.getPulseAudio()
        self.set_name("")
        self.deleted = True
        self.pa_sink = None
        self.extended_panel_shown = False
        self.extended_panel= None
        self.show_meter = True
        self.expander = None
        self.popup_menu = None
        self.card_settings = None
        self.menus = None
        self.port_actions = None

        self.double_click_filter = ChannelEventFilter(self)
        self.installEventFilter(self.double_click_filter)

        self.init()
        self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed,True))

    def init(self):
        self.layout = QGraphicsLinearLayout(Qt.Vertical)
        self.layout.setContentsMargins(2,2,2,0)
        self.setLayout(self.layout)
        self.initArrangement()
        self.composeArrangement()
        self.setAcceptDrops(True)
        self._on_upate_expander_enabled()

    def initArrangement(self):
        self.create_frame()
        self.create_panel()
        self.createMute()
        self.createMiddle()
        self.create_expander()

    def composeArrangement(self):
        self.layout.addItem(self.frame)
        self.frame_layout.addItem(self.panel)
        self.panel_layout.addItem(self.mute)
        self.panel_layout.addItem(self.middle)

    def create_frame(self):
        self.frame = Plasma.Frame()
        self.frame_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.frame.setEnabledBorders (Plasma.FrameSvg.NoBorder)
        self.frame.setFrameShadow(Plasma.Frame.Plain)
        self.frame_layout.setContentsMargins(0,0,0,0)
        self.frame.setLayout(self.frame_layout)

    def create_panel(self):
        self.panel = QGraphicsWidget()
        self.panel_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self.panel_layout.setContentsMargins(6,8,10,6)
        self.panel.setLayout(self.panel_layout)

    def createMute(self):
        self.mute = MuteButton(self)
        self.connect(self.mute, SIGNAL("clicked()"), self.on_mute_cb)

    def createMiddle(self):
        self.middle = QGraphicsWidget()
        self.middle_layout = QGraphicsLinearLayout(Qt.Vertical)
        #self.middle_layout.setContentsMargins(6,8,6,0)
        self.middle_layout.setContentsMargins(0,0,0,0)
        self.middle.setLayout(self.middle_layout)
        self.middle.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.createSlider()
        self.middle_layout.addItem(self.slider)

    def createSlider(self):
        self.slider = MeterSlider(None, self.veromix.is_slider_unit_value_visible())
        self.slider.installEventFilter(self.double_click_filter)
        self.slider.set_meter_visible(self.veromix.is_meter_visible())
        self.slider.setOrientation(Qt.Horizontal)
        self.slider.setMaximum(self.veromix.get_max_volume_value())
        self.slider.setMinimum(0)
        self.slider.volumeChanged.connect( self.on_slider_cb)

    def create_expander(self):
        self.expander = Plasma.IconWidget(self.panel)
        self.expander.setZValue(10)
        self.connect(self, SIGNAL("geometryChanged()"), self._resize_widgets)
        self.expander.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
        self.expander.clicked.connect(self.on_expander_clicked)
        self.expander.setSvg("widgets/arrows", "left-arrow")

    def create_context_menu(self, event):
        self.popup_menu = QMenu()
        self.popup_menu.triggered.connect(self.on_contextmenu_clicked)
        self.context_menu_create_custom()
        self.context_menu_create_mute()
        self.context_menu_create_meter()
        self.context_menu_create_unlock_channels()
        self.context_menu_create_effects()
        self.create_menu_kill_sink()
        self.context_menu_create_settings()
        if event:
            self.popup_menu.exec_(event.screenPos())
        else:
            self.popup_menu.exec_(QCursor.pos())

    def context_menu_create_mute(self):
        action_mute = QAction(i18n("Muted"), self.popup_menu)
        self.popup_menu.addAction(action_mute)
        action_mute.setCheckable(True)
        action_mute.setChecked(self.isMuted())
        action_mute.triggered.connect(self.on_mute_cb)

    def context_menu_create_meter(self):
        action_meter = QAction(i18n("Volume meter"), self.popup_menu)
        self.popup_menu.addAction(action_meter)
        action_meter.setCheckable(True)
        action_meter.setChecked(self.pa_sink.has_monitor())
        action_meter.triggered.connect(self.on_meter_cb)

    def context_menu_create_unlock_channels(self):
        action_unlock = QAction(i18n("Unlock channels"), self.popup_menu)
        self.popup_menu.addAction(action_unlock)
        action_unlock.setCheckable(True)
        action_unlock.setChecked(self.extended_panel_shown)
        action_unlock.triggered.connect(self.toggle_channel_lock)

    def context_menu_create_ports(self):
        self.port_actions = {}
        if len(self.pa_sink.ports.keys()) > 1:
            ports_menu = QMenu(i18n("Ports"), self.popup_menu)
            ports = self.pa_sink.ports

            for port in ports.keys():
                action = QAction(in_unicode(ports[port]), self.popup_menu)
                self.port_actions[action]=port
                if port == self.pa_sink.active_port:
                    action.setCheckable(True)
                    action.setChecked(True)
                else:
                    action.setChecked(False)
                    action.setCheckable(False)
                ports_menu.addAction(action)
            self.popup_menu.addMenu(ports_menu)

    def create_menu_kill_sink(self):
        pass

    def context_menu_create_sounddevices(self):
        self.card_settings = {}
        self.menus = []
        for card in self.veromix.card_infos.values():
            current = self.veromix.get_card_info_for(self)
            if current != None and  current.get_description() == card.get_description():
                card_menu = QMenu(i18n("Profile"), self.popup_menu)
                self.popup_menu.addMenu(card_menu)
            else:
                card_menu = QMenu(card.get_description(), self.popup_menu)
                self.menus.append(card_menu)
            active_profile_name = card.get_active_profile_name()
            self.profiles = card.get_profiles()
            for profile in self.profiles:
                action = QAction(in_unicode(profile.description), card_menu)
                self.card_settings[action] = card
                if profile.name == active_profile_name:
                    action.setCheckable(True)
                    action.setChecked(True)
                card_menu.addAction(action)

    def context_menu_create_sounddevices_other(self):
        if len(self.menus) > 0:
            self.popup_menu.addSeparator()
            for each in self.menus:
                self.popup_menu.addMenu(each)

    def context_menu_create_custom(self):
        pass

    def context_menu_create_effects(self):
        pass

    def context_menu_create_settings(self):
        self.popup_menu.addSeparator()
        action_settings = QAction(i18n("Veromix Settings"), self.popup_menu)
        self.popup_menu.addAction(action_settings)
        action_settings.triggered.connect(self.veromix.applet.showConfigurationInterface)

    def _resize_widgets(self):
        self.expander.setPos(int(self.panel.size().width() - self.expander.size().width()) ,0)

    def on_double_clicked(self):
        self.slider.toggle_meter()
        self.pa_sink.toggle_monitor()
        self.slider.set_meter_value(0)

    def on_step_volume(self, up):
        vol = self.pa_sink.get_volume()
        STEP = 5
        if up:
            vol = vol + STEP
        else:
            vol = vol - STEP
        if vol < 0:
            vol = 0
        if vol > self.veromix.get_max_volume_value():
            vol = self.veromix.get_max_volume_value()
        self.setVolume(vol)

    def setVolume(self, value):
        vol = self.pa_sink.volumeDiffFor(value)
        if self.veromix.get_auto_mute():
            for c in vol:
                if c <= 0:
                    ## FIXME HACK for MurzNN this should be conditional
                    self.pa.set_sink_mute(self.index, True)
                    self.automatically_muted = True
                    return
            if self.automatically_muted :
                self.automatically_muted = False
                self.pa.set_sink_mute(self.index, False)
        self.set_channel_volumes(vol)

    def get_volume(self):
        if self.pa_sink:
            return self.pa_sink.get_volume()
        return [0]

    def on_expander_clicked(self):
        self.contextMenuEvent(None)

    def toggle_channel_lock(self):
        self.middle_layout.removeItem(self.slider)
        self.slider = None
        if (self.extended_panel_shown):
            self.extended_panel_shown = False
            self.expander.setSvg("widgets/arrows", "left-arrow")
            self.createSlider()
            self.middle_layout.addItem(self.slider)
        else:
            self.extended_panel_shown = True
            self.expander.setSvg("widgets/arrows", "down-arrow")
            self.slider = SinkChannelWidget(self.veromix, self)
            self.slider.installEventFilter(self.double_click_filter)
            self.middle_layout.addItem(self.slider)
        self.middle_layout.setContentsMargins(0,0,0,0)
        self.middle.setContentsMargins(0,0,0,0)
        self.update_with_info(self.pa_sink)
        self.veromix.check_geometries()

    def on_update_configuration(self):
        self.slider.set_meter_visible(self.veromix.is_meter_visible())
        self.slider.setMaximum(self.veromix.get_max_volume_value())
        self.slider.set_unit_value_visible(self.veromix.is_slider_unit_value_visible())
        self._on_upate_expander_enabled()

    def _on_upate_expander_enabled(self):
        if self.veromix.is_expander_enabled():
            self.expander.show()
        else:
            self.expander.hide()

    def on_contextmenu_clicked(self, action):
        if action in self.card_settings.keys():
            card = self.card_settings[action]
            for profile in card.get_profiles():
                if action.text() == profile.description:
                    self.veromix.pa.set_card_profile(card.index, profile.name)
        if action in self.port_actions.keys():
            self.pa_sink.set_port(self.port_actions[action])

    def contextMenuEvent(self,event):
        self.create_context_menu(event)

    def on_mute_cb(self):
        self.pa_sink.toggle_mute()

    def on_meter_cb(self):
        self.on_double_clicked()

    def sink_input_kill(self):
        self.pa_sink.kill()

    def set_channel_volumes(self, values):
        self.pa_sink.set_volume(values)

    def on_update_meter(self, index, value, number_of_sinks):
        if self.index == index:
            self.slider.set_meter_value(int(value))

    def update_with_info(self,info):
        self.pa_sink = info
        self.index = info.index
        self.update_label()
        self.updateIcon()
        if self.slider:
            self.slider.update_with_info(info)
        if self.extended_panel:
            self.extended_panel.update_with_info(info)
        self.update()

    def update_label(self):
        if self.pa_sink:
            self.set_name(self.pa_sink.name)

    def getOutputIndex(self):
        return self.index

    def sinkIndexFor( self, index):
        return (index * 100000) + 100000

    def updateIcon(self):
        pass

    def on_slider_cb(self, value):
        self.setVolume(value)

    def isDefaultSink(self):
        if self.pa_sink and "isdefault" in self.pa_sink.props:
            return self.pa_sink.props["isdefault"] == "True"
        return False

    def startDrag(self,event):
        pass

    def removeSlider(self):
        # if a slider is not visible, plasmoidviewer crashes if the slider is not removed.. (dont ask me)
        if self.slider:
            self.middle_layout.removeItem(self.slider)
        self.slider = None

    def isMuted(self):
        if self.pa_sink:
            return self.pa_sink.isMuted()
        return False

    def isSinkOutput(self):
        if self.pa_sink:
            return self.pa_sink.is_sinkoutput()
        return False

    def isSinkInput(self):
        if self.pa_sink:
            return self.pa_sink.is_sinkinput()
        return False

    def isSink(self):
        if self.pa_sink:
            return self.pa_sink.is_sink()
        return False

    def isNowplaying(self):
        return False

    def isSourceOutput(self):
        if self.pa_sink:
            return self.pa_sink.is_sourceoutput()
        return False

    def wheelEvent(self, event):
        if self.slider:
            self.slider.wheelEvent(event)

    def set_name(self, string):
        self._name = in_unicode(string)

    def name(self):
        return self._name

    def update_module_info(self, index, name, argument, n_used, auto_unload):
        pass

    def get_ladspa_type(self):
        return str(type(self))

    def get_pasink_name(self):
        return self.pa_sink.name

## LADSPA helpers

    def populate_presets_menu(self, target_menu, checked_item, add_actions):
        effect_menu = QMenu(i18n("Presets"), target_menu)
        if add_actions:
            self.action_save_preset = QAction(i18n("Save"),effect_menu)
            effect_menu.addAction(self.action_save_preset)
            if not self.is_preset():
                self.action_save_preset.setEnabled(False)

            self.action_save_as_preset = QAction(i18n("Save As..."),effect_menu)
            effect_menu.addAction(self.action_save_as_preset)
            effect_menu.addSeparator()

        for preset in LADSPAPresetLoader().presets():
            action = QAction(preset["preset_name"],effect_menu)
            effect_menu.addAction(action)
            if checked_item == preset["preset_name"]:
                action.setCheckable(True)
                action.setChecked(True)
                action.setEnabled(False)
        target_menu.addMenu(effect_menu)

    def populate_switch_effect_menu(self, target_menu, checked_item):
        effect_menu = QMenu(i18n("Effect"), target_menu)
        for preset in LADSPAEffects().effects():
            action = QAction(preset["preset_name"],effect_menu)
            effect_menu.addAction(action)
            if checked_item == preset["label"]:
                action.setCheckable(True)
                action.setChecked(True)
                action.setEnabled(False)
        target_menu.addMenu(effect_menu)

    def on_set_ladspa_effect(self, value, master):
        parameters = ""
        preset = None
        for p in LADSPAEffects().effects():
            if p["preset_name"] == value:
                parameters = "sink_name=" + urllib.quote(p["name"])
                preset = p

        for p in LADSPAPresetLoader().presets():
            if p["preset_name"] == value:
                parameters = "sink_name=" + urllib.quote(p["preset_name"])
                preset = p

        parameters =  parameters + " master=" + master + " "
        parameters =  parameters + " plugin=" + preset["plugin"]
        parameters =  parameters + " label=" + preset["label"]
        parameters =  parameters + " control=" + preset["control"]
        self.pa_sink.set_ladspa_sink(parameters)

    def next_focus(self, forward=True):
        channels = self.veromix.get_visible_channels()
        if len(channels) > 0:
            index = 0
            if self in channels:
                index = channels.index(self)
                if forward:
                    index = index + 1
                    if index >= len(channels):
                        index = 0
                else:
                    index = index - 1
                    if index < 0:
                        index = len(channels) - 1
            channels[index].set_focus()

    def set_focus(self):
        self.slider.set_focus()

    def pa_sink_proxy(self):
        return self.pa_sink
Beispiel #7
0
    def __updateState(self):
        """
        Update the widget with the new source/sink node signal descriptions.
        """
        widget = QGraphicsWidget()
        widget.setLayout(QGraphicsGridLayout())

        # Space between left and right anchors
        widget.layout().setHorizontalSpacing(50)

        left_node = EditLinksNode(self,
                                  direction=Qt.LeftToRight,
                                  node=self.source)

        left_node.setSizePolicy(QSizePolicy.MinimumExpanding,
                                QSizePolicy.MinimumExpanding)

        right_node = EditLinksNode(self,
                                   direction=Qt.RightToLeft,
                                   node=self.sink)

        right_node.setSizePolicy(QSizePolicy.MinimumExpanding,
                                 QSizePolicy.MinimumExpanding)

        left_node.setMinimumWidth(150)
        right_node.setMinimumWidth(150)

        widget.layout().addItem(
            left_node,
            0,
            0,
        )
        widget.layout().addItem(
            right_node,
            0,
            1,
        )

        title_template = "<center><b>{0}<b></center>"

        left_title = GraphicsTextWidget(self)
        left_title.setHtml(title_template.format(escape(self.source.title)))
        left_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        right_title = GraphicsTextWidget(self)
        right_title.setHtml(title_template.format(escape(self.sink.title)))
        right_title.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        widget.layout().addItem(left_title,
                                1,
                                0,
                                alignment=Qt.AlignHCenter | Qt.AlignTop)
        widget.layout().addItem(right_title,
                                1,
                                1,
                                alignment=Qt.AlignHCenter | Qt.AlignTop)

        widget.setParentItem(self)

        max_w = max(
            left_node.sizeHint(Qt.PreferredSize).width(),
            right_node.sizeHint(Qt.PreferredSize).width())

        # fix same size
        left_node.setMinimumWidth(max_w)
        right_node.setMinimumWidth(max_w)
        left_title.setMinimumWidth(max_w)
        right_title.setMinimumWidth(max_w)

        self.layout().addItem(widget)
        self.layout().activate()

        self.sourceNodeWidget = left_node
        self.sinkNodeWidget = right_node
        self.sourceNodeTitle = left_title
        self.sinkNodeTitle = right_title
class VeroMix(QGraphicsWidget):
    sinkOutputChanged = pyqtSignal()

    def __init__(self,parent):
        QGraphicsWidget.__init__(self)
        self.applet = parent
        self.pa = None
        self.last_resize_running = datetime.datetime.now()
        self.last_resize_running_timer_running = False
        self.card_infos = {}
        self.ladspa_index = 1

        self.setFocusPolicy(Qt.TabFocus)

    def init(self):
        self.setAcceptsHoverEvents (True)
        self.layout = QGraphicsLinearLayout(Qt.Vertical, self)
        self.layout.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))

        self.scroller = LockableScrollWidget(self)
        self.scroller.setMinimumSize(120,90)
        self.layout.addItem(self.scroller)
        if self.applet.formFactor() != Plasma.Planar  :
            self.scroller.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.source_panel = QGraphicsWidget()
        self.sink_panel = QGraphicsWidget()

        self.scrolled_panel_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.scrolled_panel_widget = QGraphicsWidget()
        self.scrolled_panel_widget.setLayout(self.scrolled_panel_layout)
        self.scrolled_panel_layout.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.scrolled_panel_layout.setContentsMargins(0,0,0,6)

        self.showsTabs =  not self.applet.useTabs()
        self.switchView(True)

        self.source_panel_layout = SortedLayout(Qt.Vertical, False)
        self.source_panel_layout.setSpacing(0)
        self.source_panel.setLayout(self.source_panel_layout)
        self.source_panel_layout.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        if self.showsTabs:
            self.source_panel_layout.addStretch()

        self.sink_panel_layout = SortedLayout(Qt.Vertical, False)
        self.sink_panel_layout.setSpacing(0)

        self.sink_panel.setLayout(self.sink_panel_layout)
        self.sink_panel_layout.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self.layout.setContentsMargins(0,0,0,0)
        self.source_panel_layout.setContentsMargins(0,0,0,0)
        self.sink_panel_layout.setContentsMargins(0,0,0,0)

        #QTimer.singleShot(4000, self.start_pa)
        self.start_pa()
        self.connect_mediaplayers()

    def switchView(self, startup=False):
        if self.showsTabs:
            self.scrolled_panel_layout = QGraphicsLinearLayout(Qt.Vertical)
            scrolled_panel = QGraphicsWidget()
            scrolled_panel.setLayout(self.scrolled_panel_layout)
            self.scrolled_panel_layout.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
            self.scrolled_panel_layout.addItem(self.source_panel)
            self.scrolled_panel_layout.addItem(self.sink_panel)
        else:
            scrolled_panel = Plasma.TabBar()
            scrolled_panel.addTab(i18n("Playback"), self.sink_panel)
            scrolled_panel.addTab(i18n("Record"), self.source_panel)
            self.source_panel.show()

        self.scrolled_panel = scrolled_panel
        self.showsTabs = not self.showsTabs
        self.scroller.setWidget(self.scrolled_panel)
        if not startup:
            self.check_geometries()

    def on_update_configuration(self):
        for source in self.source_panel_layout.getChannels().values():
            source.on_update_configuration()
        for sink in self.sink_panel_layout.getChannels().values():
            sink.on_update_configuration()

        self.pa.set_autostart_meters(bool(self.applet.is_meter_visible()))

    # connect to pulseaudio(dbus) callbacks
    def start_pa(self):
        try:
            self.pa = PulseAudio(self)
            self.pa.connect_veromix_service()
        except Exception, e:
            self.showMessage(KIcon("script-error"), i18n("There is a problem with the backgroud-service. \
                                                        <ul> \
                                                        <li>If you just upgraded try killing the process named: VeromixServiceMain.py and relaunch this plasmoid</li> \
                                                        <li>If you don't know how to do that consider rebooting</li></ul><br/>\
                                                        <a href=\"http://code.google.com/p/veromix-plasmoid/wiki/Debugging\">See wiki for more details</a> <span style=\"font-size: small;\">(right click and copy url)</span>."))
            print "\nError connecting to veromix-service:\n" , e, "\n"
            return
        self.connect(self.pa, SIGNAL("on_sink_input_info(PyQt_PyObject)"), self.on_sink_input_info)
        self.connect(self.pa, SIGNAL("on_sink_info(PyQt_PyObject)"), self.on_sink_info)
        self.connect(self.pa, SIGNAL("on_source_output_info(PyQt_PyObject)"), self.on_source_output_info)
        self.connect(self.pa, SIGNAL("on_source_info(PyQt_PyObject)"), self.on_source_info)

        self.connect(self.pa, SIGNAL("on_sink_remove(int)"), self.on_remove_sink)
        self.connect(self.pa, SIGNAL("on_sink_input_remove(int)"), self.on_remove_sink_input)
        self.connect(self.pa, SIGNAL("on_source_remove(int)"), self.on_remove_source)
        self.connect(self.pa, SIGNAL("on_source_output_remove(int)"), self.on_remove_source_output)

        self.connect(self.pa, SIGNAL("on_volume_meter_sink(int, float)"), self.on_volume_meter_sink)
        self.connect(self.pa, SIGNAL("on_volume_meter_sink_input(int, float)"), self.on_volume_meter_sink_input)
        self.connect(self.pa, SIGNAL("on_volume_meter_source(int, float)"), self.on_volume_meter_source)

        self.connect(self.pa, SIGNAL("on_card_info(PyQt_PyObject)"), self.on_card_info)
        self.connect(self.pa, SIGNAL("on_card_remove(int)"), self.on_remove_card)

        self.connect(self.pa, SIGNAL("on_module_info(int,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.on_module_info)
        self.pa.requestInfo()
    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())
class MediaPlayerUI(Channel):

    def __init__(self,name, veromix, controller):
        self.controller = controller
        Channel.__init__(self, veromix)
        self.controller.data_updated.connect(self.controller_data_updated)
        self.index = -1
        self._state= None
        self._position = 0
        self._artwork = ""
        self.last_playing_icon = KIcon(self.get_pauseIcon())
        self.layout.setContentsMargins(6,0,6,2)
        self.controller.init_connection()

    def initArrangement(self):
        self.svg_path = self.veromix.applet.package().filePath('images', 'buttons.svgz')
        self.createMiddle()
        self.createSlider()
        self.create_prev_panel()
        self.create_frame()
        self.create_panel()
        self.create_prev_button()
        self.create_play_pause_button()
        self.create_next_button()
        self.create_next_panel()
        self.createPositionLabel()
        self.createLengthLabel()
        self.create_expander()

    def composeArrangement(self):
        self.layout.addItem(self.frame)
        self.frame_layout.addItem(self.panel)
        self.prev_panel_layout.addStretch()
        self.prev_panel_layout.addItem(self.prev)
        self.next_panel_layout.addStretch()
        self.next_panel_layout.addItem(self.next)
        self.prev_panel.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.next_panel.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        self.play.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.middle_layout.addStretch()
        self.middle_layout.addItem(self.play)
        self.panel.setContentsMargins(0,0,0,0)
        self.panel_layout.setContentsMargins(6,0,10,6)
        self.panel_layout.addStretch()
        self.panel_layout.addItem(self.prev_panel)
        self.panel_layout.addItem(self.middle)
        self.panel_layout.addItem(self.next_panel)
        self.panel_layout.addStretch()

    def on_expander_clicked(self):
        self.middle_layout.removeItem(self.slider)
        if (self.extended_panel_shown):
            self.extended_panel_shown = False
            #self.frame_layout.removeItem(self.extended_panel)
            self.extended_panel.setParent(None)
            self.panel_layout.setContentsMargins(6,0,10,6)
            self.extended_panel = None
            self.slider= None
            self.expander.setSvg("widgets/arrows", "left-arrow")
        else:
            self.extended_panel_shown = True
            self.create_settings_widget()
            #self.frame_layout.addItem(self.extended_panel)
            self.panel_layout.setContentsMargins(6,0,10,20)
            self.extended_panel.setPos(0, int(self.frame.size().height() - 15))
            #self.expander.setSvg("widgets/arrows", "up-arrow")
            self.expander.setSvg("widgets/arrows", "down-arrow")
        self.controller.set_fetch_extended_info(self.extended_panel_shown)
        self.veromix.check_geometries()

    def _resize_widgets(self):
        #self.expander.setPos(int(self.panel.size().width() - self.expander.size().width()) ,self.panel.size().height() - self.expander.size().height())
        self.expander.setPos(int(self.panel.size().width() - self.expander.size().width()) ,0)
        if self.extended_panel:
            self.extended_panel.resize(QSizeF(int(self.frame.size().width()), -1))
            #self.extended_panel.setPos(0, int(self.frame.size().height() - 12))

    def create_settings_widget(self):
        self.createLengthLabel()
        self.createPositionLabel()
        self.createSlider()
        self.extended_panel = QGraphicsWidget(self.frame)
        self.extended_panel_layout = QGraphicsLinearLayout(Qt.Horizontal)
        self.extended_panel.setLayout(self.extended_panel_layout)

        self.extended_panel.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum))
        self.position_label.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        self.length_label.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))

        self.extended_panel_layout.addItem(self.position_label)
        self.extended_panel_layout.addItem(self.slider)
        self.extended_panel_layout.addItem(self.length_label)

    def controller_data_updated(self):
        self.update_state()
        self.update_cover()
        if self.extended_panel_shown:
            self.update_position()
            self.update_slider()

    def on_update_configuration(self):
        self.set_middle_size()
        if self.veromix.is_albumart_enabled():
            self.middle.setIcon(KIcon())
        self._on_upate_expander_enabled()

    def update_with_info(self, info):
        pass

## update ui

    def update_state(self):
        state = self.controller.state()
        if self._state != state:
            self._state = state
            if state == MediaPlayer.Playing:
                #self.play.setSvg(self.svg_path, "pause-normal")
                self.play.setIcon(KIcon("media-playback-pause"))
                if self.veromix.is_albumart_enabled():
                    self.middle.setIcon(self.last_playing_icon)
            else:
                #self.play.setSvg(self.svg_path, "play-normal")
                self.play.setIcon(KIcon("media-playback-start"))
                if self.veromix.is_albumart_enabled():
                    self.middle.setIcon(KIcon(self.get_pauseIcon()))

    def update_cover(self):
        if not self.veromix.is_albumart_enabled():
            self.middle.setIcon(KIcon())
            return True
        # FIXME
        val = self.controller._cover_string
        if self._artwork !=  val:
            self._artwork = val
            if val == None:
                self.last_playing_icon = KIcon(self.get_pauseIcon())
            else:
                self.last_playing_icon = QIcon(QPixmap(self.controller.artwork()))
            self.middle.setIcon(self.last_playing_icon)

    def update_position(self):
        v = self.controller.position()
        if v != self._position:
            self._position = v
            pos_str = ( '%d:%02d' % (v / 60, v % 60))
            self.position_label.setBoldText(pos_str)
        v = self.controller.length()
        if v :
            pos_str = ( '%d:%02d' % (v / 60, v % 60))
            self.length_label.setBoldText(pos_str)

    def update_slider(self):
        if self.slider and self.extended_panel_shown:
            if self.controller.state() == MediaPlayer.Stopped:
                self.slider.setValueFromPulse(0)
            else:
                if self.controller.length() > 0 :
                    v = self.controller.position() * 100 / self.controller.length()
#                    if self.slider.check_pulse_timestamp():
                    self.slider.setValueFromPulse(v)

    def on_slider_action_triggered(self, action):
        value = self.slider.nativeWidget().sliderPosition()
        if value > -1 and (action == 7 or action == 3):
            self.controller.seek(value)

## initialize ui

    def context_menu_create_mute(self):
        pass

    def on_contextmenu_clicked(self, action):
        pass

    def context_menu_create_unlock_channels(self):
        action_unlock = QAction(i18n("Show position"), self.popup_menu)
        self.popup_menu.addAction(action_unlock)
        action_unlock.setCheckable(True)
        action_unlock.setChecked(self.extended_panel_shown)
        action_unlock.triggered.connect(self.on_expander_clicked)

    def create_next_panel(self):
        self.next_panel = QGraphicsWidget()
        self.next_panel_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.next_panel_layout.setContentsMargins(0,0,0,0)
        self.next_panel.setLayout(self.next_panel_layout)

    def createPositionLabel(self):
        self.position_label = Label()
        self.position_label.setContentsMargins(0,0,0,0)
        self.position_label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding, True))
        self.position_label.setAlignment(Qt.AlignRight)

    def createLengthLabel(self):
        self.length_label = Label()
        self.length_label.setContentsMargins(0,0,0,0)
        self.length_label.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding, True))
        self.length_label.setAlignment(Qt.AlignLeft)

    def create_prev_panel(self):
        self.prev_panel = Plasma.IconWidget()
        self.prev_panel_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.prev_panel_layout.setContentsMargins(0,0,0,0)
        self.prev_panel.setLayout(self.prev_panel_layout)
        self.prev_panel.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))

    def createMiddle(self):
        self.middle = Plasma.IconWidget()
        self.middle_layout = QGraphicsLinearLayout(Qt.Vertical)
        self.middle_layout.setContentsMargins(0,0,0,0)
        self.middle.setLayout(self.middle_layout)
        self.set_middle_size()
        self.middle.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        if self.veromix.is_albumart_enabled():
            self.middle.setIcon(KIcon(self.get_pauseIcon()))


    def set_middle_size(self):
        if self.veromix.is_albumart_enabled():
            self.CONTROLSBAR_SIZE = 80
        else:
            self.CONTROLSBAR_SIZE = 30
        self.middle.setMinimumHeight(self.CONTROLSBAR_SIZE)
        self.middle.setPreferredHeight(self.CONTROLSBAR_SIZE)
        self.middle.setMaximumHeight(self.CONTROLSBAR_SIZE)

    def create_next_button(self):
        self.next = MuteButton(self)
        self.next.setAbsSize(20)
        #self.next.setSvg(self.svg_path , "next-normal")
        self.next.setIcon(KIcon("media-skip-forward"))
        self.connect(self.next, SIGNAL("clicked()"), self.on_next_cb  )

    def create_prev_button(self):
        self.prev = MuteButton(self)
        self.prev.setAbsSize(20)
        #self.prev.setSvg(self.svg_path, "prev-normal")
        self.prev.setIcon(KIcon("media-skip-backward"))
        self.connect(self.prev, SIGNAL("clicked()"), self.on_prev_cb  )

    def create_play_pause_button(self):
        self.play = MuteButton(self)
        self.play.setAbsSize(-1)
        #self.play.setSvg(self.svg_path, "stop-normal")
        self.play.setIcon(KIcon("media-playback-stop"))
        self.connect(self.play, SIGNAL("clicked()"), self.on_play_cb  )

    def createMute(self):
        pass

# callbacks

    def on_mute_cb(self):
        pass

    def on_next_cb(self):
        self.controller.next_track()

    def on_prev_cb(self):
        self.controller.prev_track()

    def on_play_cb(self):
        if self.controller.state() == MediaPlayer.Playing:
            self.controller.pause()
        else:
            self.controller.play()

# helpers
    def name(self):
        return self.controller_name()

    def controller_name(self):
        return self.controller.name()

    def get_pauseIcon(self):
        name = self.get_application_name()
        app = self.veromix.query_application(str(name))
        if app == None:
            return name
        return app

    def get_application_name(self):
        return self.controller.get_application_name()

    def matches(self, sink):
        sink = self.get_assotiated_sink_input()
        if sink == None:
            return False
        return True

    def get_assotiated_sink_input(self):
        name = str(self.get_application_name()).lower()
        for sink in self.veromix.get_sinkinput_widgets():
            if str(name).lower().find(sink.name()) >= 0:
                return sink
        for sink in self.veromix.get_sinkinput_widgets():
            if str(sink.name()).lower().find(name) >= 0 :
                return sink
        return None

## overrides

    def isNowplaying(self):
        return True

    def isSinkOutput(self):
        return False

    def isSinkInput(self):
        return False

    def createSlider(self):
        Channel.createSlider(self)
        self.slider.setMaximum(100)
        self.slider.volumeChanged.disconnect(self.on_slider_cb)
        self.slider.nativeWidget ().actionTriggered.connect(self.on_slider_action_triggered)

## testing

    def is_nowplaying_player(self):
        return self.controller.is_mpris2_player()

    def is_mpris2_player(self):
        return self.controller.is_mpris2_player()