Example #1
0
class Plotter(QObject):

	def __init__(self, dimensions, main_title, x_title, y_title):
		QObject.__init__(self)

		self.dimensions = dimensions
		self.scene = QGraphicsScene()

		self.main_title = TextItem(main_title, self.scene, self.dimensions, main_title_transform)
		self.x_title = TextItem(x_title, self.scene, self.dimensions, x_title_transform)
		self.y_title = TextItem(y_title, self.scene, self.dimensions, y_title_transform)

		if config.debug.plotter.show_margin_boxes:
			self.debug_margin_boxes = []
			dimensions.changed.connect(self.show_margin_boxes)
			self.show_margin_boxes()

	def show_margin_boxes(self):
		for rect in self.debug_margin_boxes:
			self.scene.removeItem(rect)
		r = []
		r.append(self.scene.addRect(0,0, self.dimensions.left_margin, self.dimensions.height,
				red, red_t))
		r.append(self.scene.addRect(0,0, self.dimensions.width, self.dimensions.top_margin,
				green, green_t))
		r.append(self.scene.addRect(0, self.dimensions.top_margin + self.dimensions.grid_height,
				self.dimensions.width, self.dimensions.bottom_margin,
				blue, blue_t))
		r.append(self.scene.addRect(self.dimensions.width, self.dimensions.height,
				-self.dimensions.right_margin, -self.dimensions.height,
				cyan, cyan_t))
		halfway_height = self.dimensions.top_margin + self.dimensions.grid_height/2
		r.append(self.scene.addLine(0, halfway_height,
				self.dimensions.width, halfway_height,
				QPen(black, 0)))
		halfway_width = self.dimensions.left_margin + self.dimensions.grid_width/2
		r.append(self.scene.addLine(halfway_width, 0,
				halfway_width, self.dimensions.height,
				QPen(black, 0)))
		self.debug_margin_boxes = r
Example #2
0
class OWSieveDiagram(OWWidget):
    name = "Sieve Diagram"
    description = "Visualize the observed and expected frequencies " \
                  "for a combination of values."
    icon = "icons/SieveDiagram.svg"
    priority = 200

    inputs = [("Data", Table, "set_data", Default),
              ("Features", AttributeList, "set_input_features")]
    outputs = [("Selection", Table)]

    graph_name = "canvas"

    want_control_area = False

    settingsHandler = DomainContextHandler()
    attrX = ContextSetting("", exclude_metas=False)
    attrY = ContextSetting("", exclude_metas=False)
    selection = ContextSetting(set())

    def __init__(self):
        # pylint: disable=missing-docstring
        super().__init__()

        self.data = self.discrete_data = None
        self.attrs = []
        self.input_features = None
        self.areas = []
        self.selection = set()

        self.attr_box = gui.hBox(self.mainArea)
        model = VariableListModel()
        model.wrap(self.attrs)
        combo_args = dict(widget=self.attr_box,
                          master=self,
                          contentsLength=12,
                          callback=self.update_attr,
                          sendSelectedValue=True,
                          valueType=str,
                          model=model)
        fixed_size = (QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.attrXCombo = gui.comboBox(value="attrX", **combo_args)
        gui.widgetLabel(self.attr_box, "\u2715", sizePolicy=fixed_size)
        self.attrYCombo = gui.comboBox(value="attrY", **combo_args)
        self.vizrank, self.vizrank_button = SieveRank.add_vizrank(
            self.attr_box, self, "Score Combinations", self.set_attr)
        self.vizrank_button.setSizePolicy(*fixed_size)

        self.canvas = QGraphicsScene()
        self.canvasView = ViewWithPress(self.canvas,
                                        self.mainArea,
                                        handler=self.reset_selection)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        box = gui.hBox(self.mainArea)
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)

    def sizeHint(self):
        return QSize(450, 550)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.update_graph()

    def showEvent(self, event):
        super().showEvent(event)
        self.update_graph()

    def set_data(self, data):
        """
        Discretize continuous attributes, and put all attributes and discrete
        metas into self.attrs, which is used as a model for combos.

        Select the first two attributes unless context overrides this.
        Method `resolve_shown_attributes` is called to use the attributes from
        the input, if it exists and matches the attributes in the data.

        Remove selection; again let the context override this.
        Initialize the vizrank dialog, but don't show it.

        Args:
            data (Table): input data
        """
        if isinstance(data, SqlTable) and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.closeContext()
        self.data = data
        self.areas = []
        self.selection = set()
        if self.data is None:
            self.attrs[:] = []
        else:
            if any(attr.is_continuous for attr in data.domain):
                discretizer = Discretize(method=EqualFreq(n=4),
                                         discretize_classes=True,
                                         discretize_metas=True)
                self.discrete_data = discretizer(data)
            else:
                self.discrete_data = self.data
            self.attrs[:] = [
                var for var in chain(self.discrete_data.domain, (
                    var for var in self.data.domain.metas if var.is_discrete))
            ]
        if self.attrs:
            self.attrX = self.attrs[0].name
            self.attrY = self.attrs[len(self.attrs) > 1].name
        else:
            self.attrX = self.attrY = None
            self.areas = []
            self.selection = set()
        self.openContext(self.data)
        self.resolve_shown_attributes()
        self.update_graph()
        self.update_selection()

        self.vizrank.initialize()
        self.vizrank_button.setEnabled(
            self.data is not None and len(self.data) > 1
            and len(self.data.domain.attributes) > 1)

    def set_attr(self, attr_x, attr_y):
        self.attrX, self.attrY = attr_x.name, attr_y.name
        self.update_attr()

    def update_attr(self):
        """Update the graph and selection."""
        self.selection = set()
        self.update_graph()
        self.update_selection()

    def set_input_features(self, attr_list):
        """
        Handler for the Features signal.

        The method stores the attributes and calls `resolve_shown_attributes`

        Args:
            attr_list (AttributeList): data from the signal
        """
        self.input_features = attr_list
        self.resolve_shown_attributes()
        self.update_selection()

    def resolve_shown_attributes(self):
        """
        Use the attributes from the input signal if the signal is present
        and at least two attributes appear in the domain. If there are
        multiple, use the first two. Combos are disabled if inputs are used.
        """
        self.warning()
        self.attr_box.setEnabled(True)
        if not self.input_features:  # None or empty
            return
        features = [f for f in self.input_features if f in self.attrs]
        if not features:
            self.warning(
                "Features from the input signal are not present in the data")
            return
        old_attrs = self.attrX, self.attrY
        self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
        self.attr_box.setEnabled(False)
        if (self.attrX, self.attrY) != old_attrs:
            self.selection = set()
            self.update_graph()

    def reset_selection(self):
        self.selection = set()
        self.update_selection()

    def select_area(self, area, event):
        """
        Add or remove the clicked area from the selection

        Args:
            area (QRect): the area that is clicked
            event (QEvent): event description
        """
        if event.button() != Qt.LeftButton:
            return
        index = self.areas.index(area)
        if event.modifiers() & Qt.ControlModifier:
            self.selection ^= {index}
        else:
            self.selection = {index}
        self.update_selection()

    def update_selection(self):
        """
        Update the graph (pen width) to show the current selection.
        Filter and output the data.
        """
        if self.areas is None or not self.selection:
            self.send("Selection", None)
            return

        filts = []
        for i, area in enumerate(self.areas):
            if i in self.selection:
                width = 4
                val_x, val_y = area.value_pair
                filts.append(
                    filter.Values([
                        filter.FilterDiscrete(self.attrX, [val_x]),
                        filter.FilterDiscrete(self.attrY, [val_y])
                    ]))
            else:
                width = 1
            pen = area.pen()
            pen.setWidth(width)
            area.setPen(pen)
        if len(filts) == 1:
            filts = filts[0]
        else:
            filts = filter.Values(filts, conjunction=False)
        selection = filts(self.discrete_data)
        if self.discrete_data is not self.data:
            idset = set(selection.ids)
            sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
            selection = self.data[sel_idx]
        self.send("Selection", selection)

    def update_graph(self):
        # Function uses weird names like r, g, b, but it does it with utmost
        # caution, hence
        # pylint: disable=invalid-name
        """Update the graph."""
        def text(txt, *args, **kwargs):
            return CanvasText(self.canvas,
                              "",
                              html_text=to_html(txt),
                              *args,
                              **kwargs)

        def width(txt):
            return text(txt, 0, 0, show=False).boundingRect().width()

        def fmt(val):
            return str(int(val)) if val % 1 == 0 else "{:.2f}".format(val)

        def show_pearson(rect, pearson, pen_width):
            """
            Color the given rectangle according to its corresponding
            standardized Pearson residual.

            Args:
                rect (QRect): the rectangle being drawn
                pearson (float): signed standardized pearson residual
                pen_width (int): pen width (bolder pen is used for selection)
            """
            r = rect.rect()
            x, y, w, h = r.x(), r.y(), r.width(), r.height()
            if w == 0 or h == 0:
                return

            r = b = 255
            if pearson > 0:
                r = g = max(255 - 20 * pearson, 55)
            elif pearson < 0:
                b = g = max(255 + 20 * pearson, 55)
            else:
                r = g = b = 224
            rect.setBrush(QBrush(QColor(r, g, b)))
            pen_color = QColor(255 * (r == 255), 255 * (g == 255),
                               255 * (b == 255))
            pen = QPen(pen_color, pen_width)
            rect.setPen(pen)
            if pearson > 0:
                pearson = min(pearson, 10)
                dist = 20 - 1.6 * pearson
            else:
                pearson = max(pearson, -10)
                dist = 20 - 8 * pearson
            pen.setWidth(1)

            def _offseted_line(ax, ay):
                r = QGraphicsLineItem(x + ax, y + ay, x + (ax or w),
                                      y + (ay or h))
                self.canvas.addItem(r)
                r.setPen(pen)

            ax = dist
            while ax < w:
                _offseted_line(ax, 0)
                ax += dist

            ay = dist
            while ay < h:
                _offseted_line(0, ay)
                ay += dist

        def make_tooltip():
            """Create the tooltip. The function uses local variables from
            the enclosing scope."""

            # pylint: disable=undefined-loop-variable
            def _oper(attr_name, txt):
                if self.data.domain[attr_name] is ddomain[attr_name]:
                    return "="
                return " " if txt[0] in "<≥" else " in "

            return ("<b>{attrX}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)"
                    .format(attrX=to_html(attr_x),
                            xeq=_oper(attr_x, xval_name),
                            xval_name=to_html(xval_name),
                            obs_x=fmt(chi.probs_x[x] * n),
                            n=int(n),
                            p_x=100 * chi.probs_x[x]) + "<br/>" +
                    "<b>{attrY}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)"
                    .format(attrY=to_html(attr_y),
                            yeq=_oper(attr_y, yval_name),
                            yval_name=to_html(yval_name),
                            obs_y=fmt(chi.probs_y[y] * n),
                            n=int(n),
                            p_y=100 * chi.probs_y[y]) + "<hr/>" +
                    """<b>combination of values: </b><br/>
                   &nbsp;&nbsp;&nbsp;expected {exp} ({p_exp:.0f} %)<br/>
                   &nbsp;&nbsp;&nbsp;observed {obs} ({p_obs:.0f} %)""".format(
                        exp=fmt(chi.expected[y, x]),
                        p_exp=100 * chi.expected[y, x] / n,
                        obs=fmt(chi.observed[y, x]),
                        p_obs=100 * chi.observed[y, x] / n))

        for item in self.canvas.items():
            self.canvas.removeItem(item)
        if self.data is None or len(self.data) == 0 or \
                self.attrX is None or self.attrY is None:
            return

        ddomain = self.discrete_data.domain
        attr_x, attr_y = self.attrX, self.attrY
        disc_x, disc_y = ddomain[attr_x], ddomain[attr_y]
        view = self.canvasView

        chi = ChiSqStats(self.discrete_data, attr_x, attr_y)
        n = chi.n
        max_ylabel_w = max((width(val) for val in disc_y.values), default=0)
        max_ylabel_w = min(max_ylabel_w, 200)
        x_off = width(attr_x) + max_ylabel_w
        y_off = 15
        square_size = min(view.width() - x_off - 35,
                          view.height() - y_off - 50)
        square_size = max(square_size, 10)
        self.canvasView.setSceneRect(0, 0, view.width(), view.height())

        curr_x = x_off
        max_xlabel_h = 0
        self.areas = []
        for x, (px, xval_name) in enumerate(zip(chi.probs_x, disc_x.values)):
            if px == 0:
                continue
            width = square_size * px

            curr_y = y_off
            for y in range(len(chi.probs_y) - 1, -1, -1):  # bottom-up order
                py = chi.probs_y[y]
                yval_name = disc_y.values[y]
                if py == 0:
                    continue
                height = square_size * py

                selected = len(self.areas) in self.selection
                rect = CanvasRectangle(self.canvas,
                                       curr_x + 2,
                                       curr_y + 2,
                                       width - 4,
                                       height - 4,
                                       z=-10,
                                       onclick=self.select_area)
                rect.value_pair = x, y
                self.areas.append(rect)
                show_pearson(rect, chi.residuals[y, x], 3 * selected)
                rect.setToolTip(make_tooltip())

                if x == 0:
                    text(yval_name, x_off, curr_y + height / 2,
                         Qt.AlignRight | Qt.AlignVCenter)
                curr_y += height

            xl = text(xval_name, curr_x + width / 2, y_off + square_size,
                      Qt.AlignHCenter | Qt.AlignTop)
            max_xlabel_h = max(int(xl.boundingRect().height()), max_xlabel_h)
            curr_x += width

        bottom = y_off + square_size + max_xlabel_h
        text(attr_y,
             0,
             y_off + square_size / 2,
             Qt.AlignLeft | Qt.AlignVCenter,
             bold=True,
             vertical=True)
        text(attr_x,
             x_off + square_size / 2,
             bottom,
             Qt.AlignHCenter | Qt.AlignTop,
             bold=True)
        xl = text("χ²={:.2f}, p={:.3f}".format(chi.chisq, chi.p), 0, bottom)
        # Assume similar height for both lines
        text("N = " + fmt(chi.n), 0, bottom - xl.boundingRect().height())

    def get_widget_name_extension(self):
        if self.data is not None:
            return "{} vs {}".format(self.attrX, self.attrY)

    def send_report(self):
        self.report_plot()
Example #3
0
class FlowChartView(QWidget):
    """
    Flowchart view
    """
    def __init__(self, parent):
        """
        Constructs FlowChartView widget 

        @param parent:
        @type parent: 
        """
        QWidget.__init__(self, parent)

        self.steps = []
        self.timestamps = []
        self.arrows = []

        self.createWidget()

    def createWidget(self):
        """
        Create the widget
        """
        self.diagramScene = QGraphicsScene(self)

        self.view = QGraphicsView(self.diagramScene)

        self.view.setRenderHint(QPainter.Antialiasing)

        # set the main layout
        layout = QVBoxLayout()

        self.logEdit = QTextEdit()
        self.logEdit.setReadOnly(True)

        hSplitter2 = QSplitter(self)
        hSplitter2.setOrientation(Qt.Vertical)

        hSplitter2.addWidget(self.view)
        hSplitter2.addWidget(self.logEdit)

        hSplitter2.setStretchFactor(0, 1)

        layout.addWidget(hSplitter2)
        self.setLayout(layout)

    def reset(self):
        """
        Clear all 
        """
        #self.diagramScene.clear()

        for stp in self.steps:
            self.diagramScene.removeItem(stp)

        for stp in self.arrows:
            self.diagramScene.removeItem(stp)

        for stamp in self.timestamps:
            self.diagramScene.removeItem(stamp)

        self.diagramScene.clear()
        self.diagramScene.update()
        self.view.resetCachedContent()

        self.steps = []
        self.arrows = []
        self.timestamps = []
        self.logEdit.setText("")

    def addStep(self,
                text,
                color="#A5A2A5",
                width=400,
                height=40,
                data=None,
                textBold=False,
                textItalic=False,
                timestamp="00:00:00"):
        """
        Add step
        """
        # take the last one
        if len(self.steps):
            latestBlock = self.steps[-1:][0]
        else:
            latestBlock = None

        newBlock = BlockItem(self,
                             text,
                             blockColor=color,
                             width=width,
                             height=height,
                             data=data,
                             bold=textBold,
                             italic=textItalic)
        if width == 100:
            newBlock.setPos(400 / 2 - 100 / 2, len(self.steps) * 80)
        elif width == 300:
            newBlock.setPos(400 / 2 - 300 / 2, len(self.steps) * 80)
        else:
            newBlock.setPos(0, len(self.steps) * 80)

        self.steps.append(newBlock)
        self.diagramScene.addItem(newBlock)

        newTimestampBlock = TimestampItem(self, timestamp)
        newTimestampBlock.setPos(-200, len(self.timestamps) * 80)

        self.timestamps.append(newTimestampBlock)
        self.diagramScene.addItem(newTimestampBlock)

        if latestBlock is not None:
            newArrow = LineItem(latestBlock, newBlock)
            self.diagramScene.addItem(newArrow)
            self.arrows.append(newArrow)

        if QtHelper.str2bool(Settings.instance().readValue(
                key='TestRun/auto-scrolling-graph')):
            self.view.centerOn(newBlock)
        return newBlock
Example #4
0
class OWSieveDiagram(OWWidget):
    """
    A two-way contingency table providing information on the relation
    between the observed and expected frequencies of a combination of values
    """
    name = "Sieve Diagram"
    icon = "icons/SieveDiagram.svg"
    priority = 310

    inputs = [("Data", Table, "set_data", Default),
              ("Features", AttributeList, "set_input_features")]
    outputs = [("Selection", Table)]

    graph_name = "canvas"

    want_control_area = False

    settingsHandler = DomainContextHandler()
    attrX = ContextSetting("")
    attrY = ContextSetting("")
    selection = ContextSetting(set())

    def __init__(self):
        # pylint: disable=missing-docstring
        super().__init__()

        self.data = self.discrete_data = None
        self.attrs = []
        self.input_features = None
        self.areas = []
        self.selection = set()

        self.attr_box = gui.hBox(self.mainArea)
        model = VariableListModel()
        model.wrap(self.attrs)
        combo_args = dict(
            widget=self.attr_box, master=self, contentsLength=12,
            callback=self.update_attr, sendSelectedValue=True, valueType=str,
            model=model)
        fixed_size = (QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.attrXCombo = gui.comboBox(value="attrX", **combo_args)
        gui.widgetLabel(self.attr_box, "\u2715", sizePolicy=fixed_size)
        self.attrYCombo = gui.comboBox(value="attrY", **combo_args)
        self.vizrank = SieveRank(self)
        self.vizrank_button = gui.button(
            self.attr_box, self, "Score Combinations", sizePolicy=fixed_size,
            callback=self.vizrank.reshow, enabled=False)
        self.vizrank.pairSelected.connect(self.set_attr)

        self.canvas = QGraphicsScene()
        self.canvasView = ViewWithPress(
            self.canvas, self.mainArea, handler=self.reset_selection)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        box = gui.hBox(self.mainArea)
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)

    def sizeHint(self):
        return QSize(450, 550)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.update_graph()

    def showEvent(self, event):
        super().showEvent(event)
        self.update_graph()

    def closeEvent(self, event):
        self.vizrank.close()
        super().closeEvent(event)

    def hideEvent(self, event):
        self.vizrank.hide()
        super().hideEvent(event)

    def set_data(self, data):
        """
        Discretize continuous attributes, and put all attributes and discrete
        metas into self.attrs, which is used as a model for combos.

        Select the first two attributes unless context overrides this.
        Method `resolve_shown_attributes` is called to use the attributes from
        the input, if it exists and matches the attributes in the data.

        Remove selection; again let the context override this.
        Initialize the vizrank dialog, but don't show it.

        Args:
            data (Table): input data
        """
        if isinstance(data, SqlTable) and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.closeContext()
        self.data = data
        self.areas = []
        self.selection = set()
        if self.data is None:
            self.attrs[:] = []
        else:
            if any(attr.is_continuous for attr in data.domain):
                discretizer = Discretize(
                    method=EqualFreq(n=4),
                    discretize_classes=True, discretize_metas=True)
                self.discrete_data = discretizer(data)
            else:
                self.discrete_data = self.data
            self.attrs[:] = [
                var for var in chain(
                    self.discrete_data.domain,
                    (var for var in self.data.domain.metas if var.is_discrete))
            ]
        if self.attrs:
            self.attrX = self.attrs[0].name
            self.attrY = self.attrs[len(self.attrs) > 1].name
        else:
            self.attrX = self.attrY = None
            self.areas = []
            self.selection = set()
        self.openContext(self.data)
        self.resolve_shown_attributes()
        self.update_graph()
        self.update_selection()

        self.vizrank.initialize()
        self.vizrank_button.setEnabled(
            self.data is not None and len(self.data) > 1 and
            len(self.data.domain.attributes) > 1)

    def set_attr(self, attr_x, attr_y):
        self.attrX, self.attrY = attr_x.name, attr_y.name
        self.update_attr()

    def update_attr(self):
        """Update the graph and selection."""
        self.selection = set()
        self.update_graph()
        self.update_selection()

    def set_input_features(self, attr_list):
        """
        Handler for the Features signal.

        The method stores the attributes and calls `resolve_shown_attributes`

        Args:
            attr_list (AttributeList): data from the signal
        """
        self.input_features = attr_list
        self.resolve_shown_attributes()
        self.update_selection()

    def resolve_shown_attributes(self):
        """
        Use the attributes from the input signal if the signal is present
        and at least two attributes appear in the domain. If there are
        multiple, use the first two. Combos are disabled if inputs are used.
        """
        self.warning()
        self.attr_box.setEnabled(True)
        if not self.input_features:  # None or empty
            return
        features = [f for f in self.input_features if f in self.attrs]
        if not features:
            self.warning(
                "Features from the input signal are not present in the data")
            return
        old_attrs = self.attrX, self.attrY
        self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
        self.attr_box.setEnabled(False)
        if (self.attrX, self.attrY) != old_attrs:
            self.selection = set()
            self.update_graph()

    def reset_selection(self):
        self.selection = set()
        self.update_selection()

    def select_area(self, area, event):
        """
        Add or remove the clicked area from the selection

        Args:
            area (QRect): the area that is clicked
            event (QEvent): event description
        """
        if event.button() != Qt.LeftButton:
            return
        index = self.areas.index(area)
        if event.modifiers() & Qt.ControlModifier:
            self.selection ^= {index}
        else:
            self.selection = {index}
        self.update_selection()

    def update_selection(self):
        """
        Update the graph (pen width) to show the current selection.
        Filter and output the data.
        """
        if self.areas is None or not self.selection:
            self.send("Selection", None)
            return

        filts = []
        for i, area in enumerate(self.areas):
            if i in self.selection:
                width = 4
                val_x, val_y = area.value_pair
                filts.append(
                    filter.Values([
                        filter.FilterDiscrete(self.attrX, [val_x]),
                        filter.FilterDiscrete(self.attrY, [val_y])
                    ]))
            else:
                width = 1
            pen = area.pen()
            pen.setWidth(width)
            area.setPen(pen)
        if len(filts) == 1:
            filts = filts[0]
        else:
            filts = filter.Values(filts, conjunction=False)
        selection = filts(self.discrete_data)
        if self.discrete_data is not self.data:
            idset = set(selection.ids)
            sel_idx = [i for i, id in enumerate(self.data.ids) if id in idset]
            selection = self.data[sel_idx]
        self.send("Selection", selection)

    def update_graph(self):
        # Function uses weird names like r, g, b, but it does it with utmost
        # caution, hence
        # pylint: disable=invalid-name
        """Update the graph."""

        def text(txt, *args, **kwargs):
            return CanvasText(self.canvas, "", html_text=to_html(txt),
                              *args, **kwargs)

        def width(txt):
            return text(txt, 0, 0, show=False).boundingRect().width()

        def fmt(val):
            return str(int(val)) if val % 1 == 0 else "{:.2f}".format(val)

        def show_pearson(rect, pearson, pen_width):
            """
            Color the given rectangle according to its corresponding
            standardized Pearson residual.

            Args:
                rect (QRect): the rectangle being drawn
                pearson (float): signed standardized pearson residual
                pen_width (int): pen width (bolder pen is used for selection)
            """
            r = rect.rect()
            x, y, w, h = r.x(), r.y(), r.width(), r.height()
            if w == 0 or h == 0:
                return

            r = b = 255
            if pearson > 0:
                r = g = max(255 - 20 * pearson, 55)
            elif pearson < 0:
                b = g = max(255 + 20 * pearson, 55)
            else:
                r = g = b = 224
            rect.setBrush(QBrush(QColor(r, g, b)))
            pen_color = QColor(255 * (r == 255), 255 * (g == 255),
                               255 * (b == 255))
            pen = QPen(pen_color, pen_width)
            rect.setPen(pen)
            if pearson > 0:
                pearson = min(pearson, 10)
                dist = 20 - 1.6 * pearson
            else:
                pearson = max(pearson, -10)
                dist = 20 - 8 * pearson
            pen.setWidth(1)

            def _offseted_line(ax, ay):
                r = QGraphicsLineItem(x + ax, y + ay, x + (ax or w),
                                      y + (ay or h))
                self.canvas.addItem(r)
                r.setPen(pen)

            ax = dist
            while ax < w:
                _offseted_line(ax, 0)
                ax += dist

            ay = dist
            while ay < h:
                _offseted_line(0, ay)
                ay += dist

        def make_tooltip():
            """Create the tooltip. The function uses local variables from
            the enclosing scope."""
            # pylint: disable=undefined-loop-variable
            def _oper(attr_name, txt):
                if self.data.domain[attr_name] is ddomain[attr_name]:
                    return "="
                return " " if txt[0] in "<≥" else " in "

            return (
                "<b>{attrX}{xeq}{xval_name}</b>: {obs_x}/{n} ({p_x:.0f} %)".
                format(attrX=to_html(attr_x),
                       xeq=_oper(attr_x, xval_name),
                       xval_name=to_html(xval_name),
                       obs_x=fmt(chi.probs_x[x] * n),
                       n=int(n),
                       p_x=100 * chi.probs_x[x]) +
                "<br/>" +
                "<b>{attrY}{yeq}{yval_name}</b>: {obs_y}/{n} ({p_y:.0f} %)".
                format(attrY=to_html(attr_y),
                       yeq=_oper(attr_y, yval_name),
                       yval_name=to_html(yval_name),
                       obs_y=fmt(chi.probs_y[y] * n),
                       n=int(n),
                       p_y=100 * chi.probs_y[y]) +
                "<hr/>" +
                """<b>combination of values: </b><br/>
                   &nbsp;&nbsp;&nbsp;expected {exp} ({p_exp:.0f} %)<br/>
                   &nbsp;&nbsp;&nbsp;observed {obs} ({p_obs:.0f} %)""".
                format(exp=fmt(chi.expected[y, x]),
                       p_exp=100 * chi.expected[y, x] / n,
                       obs=fmt(chi.observed[y, x]),
                       p_obs=100 * chi.observed[y, x] / n))

        for item in self.canvas.items():
            self.canvas.removeItem(item)
        if self.data is None or len(self.data) == 0 or \
                self.attrX is None or self.attrY is None:
            return

        ddomain = self.discrete_data.domain
        attr_x, attr_y = self.attrX, self.attrY
        disc_x, disc_y = ddomain[attr_x], ddomain[attr_y]
        view = self.canvasView

        chi = ChiSqStats(self.discrete_data, attr_x, attr_y)
        n = chi.n
        max_ylabel_w = max((width(val) for val in disc_y.values), default=0)
        max_ylabel_w = min(max_ylabel_w, 200)
        x_off = width(attr_x) + max_ylabel_w
        y_off = 15
        square_size = min(view.width() - x_off - 35, view.height() - y_off - 50)
        square_size = max(square_size, 10)
        self.canvasView.setSceneRect(0, 0, view.width(), view.height())

        curr_x = x_off
        max_xlabel_h = 0
        self.areas = []
        for x, (px, xval_name) in enumerate(zip(chi.probs_x, disc_x.values)):
            if px == 0:
                continue
            width = square_size * px

            curr_y = y_off
            for y in range(len(chi.probs_y) - 1, -1, -1):  # bottom-up order
                py = chi.probs_y[y]
                yval_name = disc_y.values[y]
                if py == 0:
                    continue
                height = square_size * py

                selected = len(self.areas) in self.selection
                rect = CanvasRectangle(
                    self.canvas, curr_x + 2, curr_y + 2, width - 4, height - 4,
                    z=-10, onclick=self.select_area)
                rect.value_pair = x, y
                self.areas.append(rect)
                show_pearson(rect, chi.residuals[y, x], 3 * selected)
                rect.setToolTip(make_tooltip())

                if x == 0:
                    text(yval_name, x_off, curr_y + height / 2,
                         Qt.AlignRight | Qt.AlignVCenter)
                curr_y += height

            xl = text(xval_name, curr_x + width / 2, y_off + square_size,
                      Qt.AlignHCenter | Qt.AlignTop)
            max_xlabel_h = max(int(xl.boundingRect().height()), max_xlabel_h)
            curr_x += width

        bottom = y_off + square_size + max_xlabel_h
        text(attr_y, 0, y_off + square_size / 2,
             Qt.AlignLeft | Qt.AlignVCenter, bold=True, vertical=True)
        text(attr_x, x_off + square_size / 2, bottom,
             Qt.AlignHCenter | Qt.AlignTop, bold=True)
        xl = text("χ²={:.2f}, p={:.3f}".format(chi.chisq, chi.p),
                  0, bottom)
        # Assume similar height for both lines
        text("N = " + fmt(chi.n), 0, bottom - xl.boundingRect().height())

    def get_widget_name_extension(self):
        if self.data is not None:
            return "{} vs {}".format(self.attrX, self.attrY)

    def send_report(self):
        self.report_plot()
Example #5
0
class OWSieveDiagram(OWWidget):
    name = "Sieve Diagram"
    icon = "icons/SieveDiagram.svg"
    priority = 4200

    inputs = [("Data", Table, "setData", Default),
              ("Features", AttributeList, "setShownAttributes")]
    outputs = []

    settingsList = ["showLines", "showCases", "showInColor"]

    def __init__(self, parent=None, signalManager=None):
        OWWidget.__init__(self, parent, signalManager, "Sieve diagram", True)

        #self.controlArea.setMinimumWidth(250)

        #set default settings
        self.data = None

        self.attrX = ""
        self.attrY = ""
        self.attrCondition = ""
        self.attrConditionValue = ""
        self.showLines = 1
        self.showCases = 0
        self.showInColor = 1
        self.attributeSelectionList = None
        self.stopCalculating = 0

        self.canvas = QGraphicsScene()
        self.canvasView = QGraphicsView(self.canvas, self.mainArea)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        #GUI
        self.attrSelGroup = gui.widgetBox(self.controlArea,
                                          box="Shown attributes")

        self.attrXCombo = gui.comboBox(
            self.attrSelGroup,
            self,
            value="attrX",
            label="X attribute:",
            orientation="horizontal",
            tooltip="Select an attribute to be shown on the X axis",
            callback=self.updateGraph,
            sendSelectedValue=1,
            valueType=str,
            labelWidth=70)
        self.attrYCombo = gui.comboBox(
            self.attrSelGroup,
            self,
            value="attrY",
            label="Y attribute:",
            orientation="horizontal",
            tooltip="Select an attribute to be shown on the Y axis",
            callback=self.updateGraph,
            sendSelectedValue=1,
            valueType=str,
            labelWidth=70)

        gui.separator(self.controlArea)

        self.conditionGroup = gui.widgetBox(self.controlArea, box="Condition")
        self.attrConditionCombo = gui.comboBox(
            self.conditionGroup,
            self,
            value="attrCondition",
            label="Attribute:",
            orientation="horizontal",
            callback=self.updateConditionAttr,
            sendSelectedValue=1,
            valueType=str,
            labelWidth=70)
        self.attrConditionValueCombo = gui.comboBox(self.conditionGroup,
                                                    self,
                                                    value="attrConditionValue",
                                                    label="Value:",
                                                    orientation="horizontal",
                                                    callback=self.updateGraph,
                                                    sendSelectedValue=1,
                                                    valueType=str,
                                                    labelWidth=70)

        gui.separator(self.controlArea)

        box2 = gui.widgetBox(self.controlArea, box="Visual settings")
        gui.checkBox(box2,
                     self,
                     "showLines",
                     "Show lines",
                     callback=self.updateGraph)
        hbox = gui.widgetBox(box2, orientation="horizontal")
        gui.checkBox(hbox,
                     self,
                     "showCases",
                     "Show data examples...",
                     callback=self.updateGraph)
        gui.checkBox(hbox,
                     self,
                     "showInColor",
                     "...in color",
                     callback=self.updateGraph)

        gui.separator(self.controlArea)
        # self.optimizationDlg = OWSieveOptimization(self, self.signalManager)
        # optimizationButtons = gui.widgetBox(self.controlArea, "Dialogs", orientation = "horizontal")
        # gui.button(optimizationButtons, self, "VizRank", callback = self.optimizationDlg.reshow, debuggingEnabled = 0, tooltip = "Find attribute groups with highest value dependency")

        gui.rubber(self.controlArea)

        # self.wdChildDialogs = [self.optimizationDlg]        # used when running widget debugging
        # self.graphButton.clicked.connect(self.saveToFileCanvas)
        self.icons = gui.attributeIconDict
        self.resize(800, 550)
        random.seed()

    def sendReport(self):
        self.startReport("%s [%s, %s]" %
                         (self.windowTitle(), self.attrX, self.attrY))
        self.reportSettings(
            "",
            [("X-Attribute", self.attrX),
             ("Y-Attribute", self.attrY), self.attrCondition != "(None)" and
             ("Condition", "%s = '%s'" %
              (self.attrCondition, self.attrConditionValue))])
        # self.reportImage(lambda *x: OWChooseImageSizeDlg(self.canvas).saveImage(*x))

    # receive new data and update all fields
    def setData(self, data):
        if type(data) == SqlTable and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.information(0)
        self.information(1)
        sameDomain = self.data and data and self.data.domain.checksum(
        ) == data.domain.checksum(
        )  # preserve attribute choice if the domain is the same
        # self.data = self.optimizationDlg.setData(data, 0)
        self.data = data

        self.warning(0, "")
        if data:
            if any(
                    isinstance(attr, ContinuousVariable)
                    for attr in data.domain):
                self.warning(
                    0, "Data contains continuous variables. " +
                    "Discretize the data to use them.")

        if not sameDomain:
            self.initCombos()

        self.setShownAttributes(self.attributeSelectionList)

    ## Attribute selection signal
    def setShownAttributes(self, attrList):
        self.attributeSelectionList = attrList
        if self.data and self.attributeSelectionList and len(attrList) >= 2:
            attrs = [attr.name for attr in self.data.domain]
            if attrList[0] in attrs and attrList[1] in attrs:
                self.attrX = attrList[0]
                self.attrY = attrList[1]
        self.updateGraph()

    # create data subset depending on conditional attribute and value
    def getConditionalData(self, xAttr=None, yAttr=None, dropMissingData=1):
        if not self.data: return None

        if not xAttr: xAttr = self.attrX
        if not yAttr: yAttr = self.attrY
        if not (xAttr and yAttr): return

        if self.attrCondition == "(None)":
            data = self.data[:, [xAttr, yAttr]]
            # data = self.data.select([xAttr, yAttr])
        else:
            # data = orange.Preprocessor_dropMissing(self.data.select([xAttr, yAttr, self.attrCondition]))
            # data = self.data.select({self.attrCondition:self.attrConditionValue})
            fd = Orange.data.filter.FilterDiscrete(
                column=self.attrCondition, values=[self.attrConditionValue])
            filt = Orange.data.filter.Values([fd])
            filt.domain = self.data.domain
            data = filt(self.data)

        # if dropMissingData: return orange.Preprocessor_dropMissing(data)
        #else:
        return data

    # new conditional attribute was set - update graph
    def updateConditionAttr(self):
        self.attrConditionValueCombo.clear()

        if self.attrCondition != "(None)":
            for val in self.data.domain[self.attrCondition].values:
                self.attrConditionValueCombo.addItem(val)
            self.attrConditionValue = str(
                self.attrConditionValueCombo.itemText(0))
        self.updateGraph()

    # initialize lists for shown and hidden attributes
    def initCombos(self):
        self.attrXCombo.clear()
        self.attrYCombo.clear()
        self.attrConditionCombo.clear()
        self.attrConditionCombo.addItem("(None)")
        self.attrConditionValueCombo.clear()

        if not self.data: return
        for i, var in enumerate(self.data.domain):
            if isinstance(var, DiscreteVariable):
                self.attrXCombo.addItem(self.icons[self.data.domain[i]],
                                        self.data.domain[i].name)
                self.attrYCombo.addItem(self.icons[self.data.domain[i]],
                                        self.data.domain[i].name)
                self.attrConditionCombo.addItem(
                    self.icons[self.data.domain[i]], self.data.domain[i].name)
        self.attrCondition = str(self.attrConditionCombo.itemText(0))

        if self.attrXCombo.count() > 0:
            self.attrX = str(self.attrXCombo.itemText(0))
            self.attrY = str(
                self.attrYCombo.itemText(self.attrYCombo.count() > 1))

    def resizeEvent(self, e):
        OWWidget.resizeEvent(self, e)
        self.updateGraph()

    def showEvent(self, ev):
        OWWidget.showEvent(self, ev)
        self.updateGraph()

    ## updateGraph - gets called every time the graph has to be updated
    def updateGraph(self, *args):
        for item in self.canvas.items():
            self.canvas.removeItem(item)  # remove all canvas items
        if not self.data: return
        if not self.attrX or not self.attrY: return

        data = self.getConditionalData()
        if not data or len(data) == 0: return

        valsX = []
        valsY = []
        # contX = orange.ContingencyAttrAttr(self.attrX, self.attrX, data)   # distribution of X attribute
        # contY = orange.ContingencyAttrAttr(self.attrY, self.attrY, data)   # distribution of Y attribute
        contX = get_contingency(data, self.attrX, self.attrX)
        contY = get_contingency(data, self.attrY, self.attrY)

        # compute contingency of x and y attributes
        for entry in contX:
            sum_ = 0
            try:
                for val in entry:
                    sum_ += val
            except:
                pass
            valsX.append(sum_)

        for entry in contY:
            sum_ = 0
            try:
                for val in entry:
                    sum_ += val
            except:
                pass
            valsY.append(sum_)

        # create cartesian product of selected attributes and compute contingency
        # (cart, profit) = FeatureByCartesianProduct(data, [data.domain[self.attrX], data.domain[self.attrY]])
        # tempData = data.select(list(data.domain) + [cart])
        # contXY = orange.ContingencyAttrAttr(cart, cart, tempData)   # distribution of X attribute
        # contXY = get_contingency(tempData, cart, cart)
        contXY = self.getConditionalDistributions(
            data, [data.domain[self.attrX], data.domain[self.attrY]])

        # compute probabilities
        probs = {}
        for i in range(len(valsX)):
            valx = valsX[i]
            for j in range(len(valsY)):
                valy = valsY[j]

                actualProb = 0
                try:
                    actualProb = contXY['%s-%s' %
                                        (data.domain[self.attrX].values[i],
                                         data.domain[self.attrY].values[j])]
                    # for val in contXY['%s-%s' %(i, j)]: actualProb += val
                except:
                    actualProb = 0
                probs['%s-%s' % (data.domain[self.attrX].values[i],
                                 data.domain[self.attrY].values[j])] = ((
                                     data.domain[self.attrX].values[i],
                                     valx), (data.domain[self.attrY].values[j],
                                             valy), actualProb, len(data))

        # get text width of Y attribute name
        text = OWCanvasText(self.canvas,
                            data.domain[self.attrY].name,
                            x=0,
                            y=0,
                            bold=1,
                            show=0,
                            vertical=True)
        xOff = int(text.boundingRect().height() + 40)
        yOff = 50
        sqareSize = min(self.canvasView.width() - xOff - 35,
                        self.canvasView.height() - yOff - 30)
        if sqareSize < 0: return  # canvas is too small to draw rectangles
        self.canvasView.setSceneRect(0, 0, self.canvasView.width(),
                                     self.canvasView.height())

        # print graph name
        if self.attrCondition == "(None)":
            name = "<b>P(%s, %s) &#8800; P(%s)&times;P(%s)</b>" % (
                self.attrX, self.attrY, self.attrX, self.attrY)
        else:
            name = "<b>P(%s, %s | %s = %s) &#8800; P(%s | %s = %s)&times;P(%s | %s = %s)</b>" % (
                self.attrX, self.attrY, self.attrCondition,
                getHtmlCompatibleString(
                    self.attrConditionValue), self.attrX, self.attrCondition,
                getHtmlCompatibleString(
                    self.attrConditionValue), self.attrY, self.attrCondition,
                getHtmlCompatibleString(self.attrConditionValue))
        OWCanvasText(self.canvas,
                     "",
                     xOff + sqareSize / 2,
                     20,
                     Qt.AlignCenter,
                     htmlText=name)
        OWCanvasText(self.canvas,
                     "N = " + str(len(data)),
                     xOff + sqareSize / 2,
                     38,
                     Qt.AlignCenter,
                     bold=0)

        ######################
        # compute chi-square
        chisquare = 0.0
        for i in range(len(valsX)):
            for j in range(len(valsY)):
                ((xAttr, xVal), (yAttr, yVal), actual,
                 sum_) = probs['%s-%s' % (data.domain[self.attrX].values[i],
                                          data.domain[self.attrY].values[j])]
                expected = float(xVal * yVal) / float(sum_)
                if expected == 0: continue
                pearson2 = (actual - expected) * (actual - expected) / expected
                chisquare += pearson2

        ######################
        # draw rectangles
        currX = xOff
        max_ylabel_w = 0

        normX, normY = sum(valsX), sum(valsY)
        for i in range(len(valsX)):
            if valsX[i] == 0: continue
            currY = yOff
            width = int(float(sqareSize * valsX[i]) / float(normX))

            #for j in range(len(valsY)):
            for j in range(len(valsY) - 1, -1,
                           -1):  # this way we sort y values correctly
                ((xAttr, xVal), (yAttr, yVal), actual,
                 sum_) = probs['%s-%s' % (data.domain[self.attrX].values[i],
                                          data.domain[self.attrY].values[j])]
                if valsY[j] == 0: continue
                height = int(float(sqareSize * valsY[j]) / float(normY))

                # create rectangle
                rect = OWCanvasRectangle(self.canvas,
                                         currX + 2,
                                         currY + 2,
                                         width - 4,
                                         height - 4,
                                         z=-10)
                self.addRectIndependencePearson(rect, currX + 2, currY + 2,
                                                width - 4, height - 4,
                                                (xAttr, xVal), (yAttr, yVal),
                                                actual, sum_)

                expected = float(xVal * yVal) / float(sum_)
                pearson = (actual - expected) / sqrt(expected)
                tooltipText = """<b>X Attribute: %s</b><br>Value: <b>%s</b><br>Number of examples (p(x)): <b>%d (%.2f%%)</b><hr>
                                <b>Y Attribute: %s</b><br>Value: <b>%s</b><br>Number of examples (p(y)): <b>%d (%.2f%%)</b><hr>
                                <b>Number Of Examples (Probabilities):</b><br>Expected (p(x)p(y)): <b>%.1f (%.2f%%)</b><br>Actual (p(x,y)): <b>%d (%.2f%%)</b>
                                <hr><b>Statistics:</b><br>Chi-square: <b>%.2f</b><br>Standardized Pearson residual: <b>%.2f</b>""" % (
                    self.attrX, getHtmlCompatibleString(xAttr), xVal,
                    100.0 * float(xVal) / float(sum_), self.attrY,
                    getHtmlCompatibleString(yAttr), yVal,
                    100.0 * float(yVal) / float(sum_), expected,
                    100.0 * float(xVal * yVal) / float(sum_ * sum_), actual,
                    100.0 * float(actual) / float(sum_), chisquare, pearson)
                rect.setToolTip(tooltipText)

                currY += height
                if currX == xOff:
                    xl = OWCanvasText(self.canvas,
                                      "",
                                      xOff - 10,
                                      currY - height / 2,
                                      Qt.AlignRight | Qt.AlignVCenter,
                                      htmlText=getHtmlCompatibleString(
                                          data.domain[self.attrY].values[j]))
                    max_ylabel_w = max(int(xl.boundingRect().width()),
                                       max_ylabel_w)

            OWCanvasText(self.canvas,
                         "",
                         currX + width / 2,
                         yOff + sqareSize + 5,
                         Qt.AlignCenter,
                         htmlText=getHtmlCompatibleString(
                             data.domain[self.attrX].values[i]))
            currX += width

        # show attribute names
        OWCanvasText(self.canvas,
                     self.attrY,
                     max(xOff - 20 - max_ylabel_w, 20),
                     yOff + sqareSize / 2,
                     Qt.AlignRight | Qt.AlignVCenter,
                     bold=1,
                     vertical=True)
        OWCanvasText(self.canvas,
                     self.attrX,
                     xOff + sqareSize / 2,
                     yOff + sqareSize + 15,
                     Qt.AlignCenter,
                     bold=1)

        #self.canvas.update()

    # create a dictionary with all possible pairs of "combination-of-attr-values" : count
    def getConditionalDistributions(self, data, attrs):
        cond_dist = defaultdict(int)
        all_attrs = [data.domain[a] for a in attrs]
        if data.domain.class_var is not None:
            all_attrs.append(data.domain.class_var)

        for i in range(1, len(all_attrs) + 1):
            attr = all_attrs[:i]
            if type(data) == SqlTable:
                # make all possible pairs of attributes + class_var
                attr = [a.to_sql() for a in attr]
                fields = attr + ["COUNT(*)"]
                query = data._sql_query(fields, group_by=attr)
                with data._execute_sql_query(query) as cur:
                    res = cur.fetchall()
                for r in res:
                    str_values = [
                        a.repr_val(a.to_val(x))
                        for a, x in zip(all_attrs, r[:-1])
                    ]
                    str_values = [
                        x if x != '?' else 'None' for x in str_values
                    ]
                    cond_dist['-'.join(str_values)] = r[-1]
            else:
                for indices in product(*(range(len(a.values)) for a in attr)):
                    vals = []
                    conditions = []
                    for k, ind in enumerate(indices):
                        vals.append(attr[k].values[ind])
                        fd = Orange.data.filter.FilterDiscrete(
                            column=attr[k], values=[attr[k].values[ind]])
                        conditions.append(fd)
                    filt = Orange.data.filter.Values(conditions)
                    filtdata = filt(data)
                    cond_dist['-'.join(vals)] = len(filtdata)
        return cond_dist

    ######################################################################
    ## show deviations from attribute independence with standardized pearson residuals
    def addRectIndependencePearson(self, rect, x, y, w, h, xAttr_xVal,
                                   yAttr_yVal, actual, sum):
        xAttr, xVal = xAttr_xVal
        yAttr, yVal = yAttr_yVal
        expected = float(xVal * yVal) / float(sum)
        pearson = (actual - expected) / sqrt(expected)

        if pearson > 0:  # if there are more examples that we would expect under the null hypothesis
            intPearson = floor(pearson)
            pen = QPen(QColor(0, 0, 255), 1)
            rect.setPen(pen)
            b = 255
            r = g = 255 - intPearson * 20
            r = g = max(r, 55)  #
        elif pearson < 0:
            intPearson = ceil(pearson)
            pen = QPen(QColor(255, 0, 0), 1)
            rect.setPen(pen)
            r = 255
            b = g = 255 + intPearson * 20
            b = g = max(b, 55)
        else:
            pen = QPen(QColor(255, 255, 255), 1)
            r = g = b = 255  # white
        color = QColor(r, g, b)
        brush = QBrush(color)
        rect.setBrush(brush)

        if self.showCases and w > 6 and h > 6:
            if self.showInColor:
                if pearson > 0: c = QColor(0, 0, 255)
                else: c = QColor(255, 0, 0)
            else: c = Qt.black
            for i in range(int(actual)):
                OWCanvasEllipse(self.canvas,
                                random.randint(x + 1, x + w - 4),
                                random.randint(y + 1, y + h - 4),
                                3,
                                3,
                                penColor=c,
                                brushColor=c,
                                z=100)

        if pearson > 0:
            pearson = min(pearson, 10)
            kvoc = 1 - 0.08 * pearson  #  if pearson in [0..10] --> kvoc in [1..0.2]
        else:
            pearson = max(pearson, -10)
            kvoc = 1 - 0.4 * pearson

        self.addLines(x, y, w, h, kvoc, pen)

    ##################################################
    # add lines
    def addLines(self, x, y, w, h, diff, pen):
        if not self.showLines: return
        if w == 0 or h == 0: return

        # create lines
        dist = 20  # original distance between two lines in pixels
        dist = dist * diff
        temp = dist
        while (temp < w):
            OWCanvasLine(self.canvas, temp + x, y, temp + x, y + h, 1,
                         pen.color())
            temp += dist

        temp = dist
        while (temp < h):
            OWCanvasLine(self.canvas, x, y + temp, x + w, y + temp, 1,
                         pen.color())
            temp += dist

    def saveToFileCanvas(self):
        sizeDlg = OWChooseImageSizeDlg(self.canvas, parent=self)
        sizeDlg.exec_()

    def closeEvent(self, ce):
        # self.optimizationDlg.hide()
        QDialog.closeEvent(self, ce)
Example #6
0
class OWSieveDiagram(OWWidget):
    name = "Sieve Diagram"
    icon = "icons/SieveDiagram.svg"
    priority = 4200

    inputs = [("Data", Table, "setData", Default),
              ("Features", AttributeList, "setShownAttributes")]
    outputs = []

    settingsList = ["showLines", "showCases", "showInColor"]
    def __init__(self,parent=None, signalManager = None):
        OWWidget.__init__(self, parent, signalManager, "Sieve diagram", True)

        #self.controlArea.setMinimumWidth(250)

        #set default settings
        self.data = None

        self.attrX = ""
        self.attrY = ""
        self.attrCondition = ""
        self.attrConditionValue = ""
        self.showLines = 1
        self.showCases = 0
        self.showInColor = 1
        self.attributeSelectionList = None
        self.stopCalculating = 0

        self.canvas = QGraphicsScene()
        self.canvasView = QGraphicsView(self.canvas, self.mainArea)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        #GUI
        self.attrSelGroup = gui.widgetBox(self.controlArea, box = "Shown attributes")

        self.attrXCombo = gui.comboBox(self.attrSelGroup, self, value="attrX", label="X attribute:", orientation="horizontal", tooltip = "Select an attribute to be shown on the X axis", callback = self.updateGraph, sendSelectedValue = 1, valueType = str, labelWidth = 70)
        self.attrYCombo = gui.comboBox(self.attrSelGroup, self, value="attrY", label="Y attribute:", orientation="horizontal", tooltip = "Select an attribute to be shown on the Y axis", callback = self.updateGraph, sendSelectedValue = 1, valueType = str, labelWidth = 70)

        gui.separator(self.controlArea)

        self.conditionGroup = gui.widgetBox(self.controlArea, box = "Condition")
        self.attrConditionCombo      = gui.comboBox(self.conditionGroup, self, value="attrCondition", label="Attribute:", orientation="horizontal", callback = self.updateConditionAttr, sendSelectedValue = 1, valueType = str, labelWidth = 70)
        self.attrConditionValueCombo = gui.comboBox(self.conditionGroup, self, value="attrConditionValue", label="Value:", orientation="horizontal", callback = self.updateGraph, sendSelectedValue = 1, valueType = str, labelWidth = 70)

        gui.separator(self.controlArea)

        box2 = gui.widgetBox(self.controlArea, box = "Visual settings")
        gui.checkBox(box2, self, "showLines", "Show lines", callback = self.updateGraph)
        hbox = gui.widgetBox(box2, orientation = "horizontal")
        gui.checkBox(hbox, self, "showCases", "Show data examples...", callback = self.updateGraph)
        gui.checkBox(hbox, self, "showInColor", "...in color", callback = self.updateGraph)

        gui.separator(self.controlArea)
        # self.optimizationDlg = OWSieveOptimization(self, self.signalManager)
        # optimizationButtons = gui.widgetBox(self.controlArea, "Dialogs", orientation = "horizontal")
        # gui.button(optimizationButtons, self, "VizRank", callback = self.optimizationDlg.reshow, debuggingEnabled = 0, tooltip = "Find attribute groups with highest value dependency")

        gui.rubber(self.controlArea)

        # self.wdChildDialogs = [self.optimizationDlg]        # used when running widget debugging
        # self.graphButton.clicked.connect(self.saveToFileCanvas)
        self.icons = gui.attributeIconDict
        self.resize(800, 550)
        random.seed()

    def sendReport(self):
        self.startReport("%s [%s, %s]" % (self.windowTitle(), self.attrX, self.attrY))
        self.reportSettings("",
                            [("X-Attribute", self.attrX), ("Y-Attribute", self.attrY),
                             self.attrCondition != "(None)" and ("Condition", "%s = '%s'" % (self.attrCondition, self.attrConditionValue))])
        # self.reportImage(lambda *x: OWChooseImageSizeDlg(self.canvas).saveImage(*x))


    # receive new data and update all fields
    def setData(self, data):
        if type(data) == SqlTable and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.information(0)
        self.information(1)
        sameDomain = self.data and data and self.data.domain.checksum() == data.domain.checksum() # preserve attribute choice if the domain is the same
        # self.data = self.optimizationDlg.setData(data, 0)
        self.data = data

        self.warning(0, "")
        if data:
            if any(isinstance(attr, ContinuousVariable) for attr in data.domain):
                self.warning(0, "Data contains continuous variables. " +
                             "Discretize the data to use them.")

        if not sameDomain:
            self.initCombos()

        self.setShownAttributes(self.attributeSelectionList)

    ## Attribute selection signal
    def setShownAttributes(self, attrList):
        self.attributeSelectionList = attrList
        if self.data and self.attributeSelectionList and len(attrList) >= 2:
            attrs = [attr.name for attr in self.data.domain]
            if attrList[0] in attrs and attrList[1] in attrs:
                self.attrX = attrList[0]
                self.attrY = attrList[1]
        self.updateGraph()



    # create data subset depending on conditional attribute and value
    def getConditionalData(self, xAttr = None, yAttr = None, dropMissingData = 1):
        if not self.data: return None

        if not xAttr: xAttr = self.attrX
        if not yAttr: yAttr = self.attrY
        if not (xAttr and yAttr): return

        if self.attrCondition == "(None)":
            data = self.data[:, [xAttr, yAttr]]
            # data = self.data.select([xAttr, yAttr])
        else:
            # data = orange.Preprocessor_dropMissing(self.data.select([xAttr, yAttr, self.attrCondition]))
            # data = self.data.select({self.attrCondition:self.attrConditionValue})
            fd = Orange.data.filter.FilterDiscrete(column=self.attrCondition, values=[self.attrConditionValue])
            filt = Orange.data.filter.Values([fd])
            filt.domain = self.data.domain
            data = filt(self.data)

        # if dropMissingData: return orange.Preprocessor_dropMissing(data)
        #else:
        return data

    # new conditional attribute was set - update graph
    def updateConditionAttr(self):
        self.attrConditionValueCombo.clear()

        if self.attrCondition != "(None)":
            for val in self.data.domain[self.attrCondition].values:
                self.attrConditionValueCombo.addItem(val)
            self.attrConditionValue = str(self.attrConditionValueCombo.itemText(0))
        self.updateGraph()

    # initialize lists for shown and hidden attributes
    def initCombos(self):
        self.attrXCombo.clear()
        self.attrYCombo.clear()
        self.attrConditionCombo.clear()
        self.attrConditionCombo.addItem("(None)")
        self.attrConditionValueCombo.clear()

        if not self.data: return
        for i, var in enumerate(self.data.domain):
            if isinstance(var, DiscreteVariable):
                self.attrXCombo.addItem(self.icons[self.data.domain[i]], self.data.domain[i].name)
                self.attrYCombo.addItem(self.icons[self.data.domain[i]], self.data.domain[i].name)
                self.attrConditionCombo.addItem(self.icons[self.data.domain[i]], self.data.domain[i].name)
        self.attrCondition = str(self.attrConditionCombo.itemText(0))

        if self.attrXCombo.count() > 0:
            self.attrX = str(self.attrXCombo.itemText(0))
            self.attrY = str(self.attrYCombo.itemText(self.attrYCombo.count() > 1))

    def resizeEvent(self, e):
        OWWidget.resizeEvent(self,e)
        self.updateGraph()

    def showEvent(self, ev):
        OWWidget.showEvent(self, ev)
        self.updateGraph()

    ## updateGraph - gets called every time the graph has to be updated
    def updateGraph(self, *args):
        for item in self.canvas.items():
            self.canvas.removeItem(item)    # remove all canvas items
        if not self.data: return
        if not self.attrX or not self.attrY: return

        data = self.getConditionalData()
        if not data or len(data) == 0: return

        valsX = []
        valsY = []
        # contX = orange.ContingencyAttrAttr(self.attrX, self.attrX, data)   # distribution of X attribute
        # contY = orange.ContingencyAttrAttr(self.attrY, self.attrY, data)   # distribution of Y attribute
        contX = get_contingency(data, self.attrX, self.attrX)
        contY = get_contingency(data, self.attrY, self.attrY)

        # compute contingency of x and y attributes
        for entry in contX:
            sum_ = 0
            try:
                for val in entry: sum_ += val
            except: pass
            valsX.append(sum_)

        for entry in contY:
            sum_ = 0
            try:
                for val in entry: sum_ += val
            except: pass
            valsY.append(sum_)

        # create cartesian product of selected attributes and compute contingency
        # (cart, profit) = FeatureByCartesianProduct(data, [data.domain[self.attrX], data.domain[self.attrY]])
        # tempData = data.select(list(data.domain) + [cart])
        # contXY = orange.ContingencyAttrAttr(cart, cart, tempData)   # distribution of X attribute
        # contXY = get_contingency(tempData, cart, cart)
        contXY = self.getConditionalDistributions(data, [data.domain[self.attrX], data.domain[self.attrY]])

        # compute probabilities
        probs = {}
        for i in range(len(valsX)):
            valx = valsX[i]
            for j in range(len(valsY)):
                valy = valsY[j]

                actualProb = 0
                try:
                    actualProb = contXY['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                    # for val in contXY['%s-%s' %(i, j)]: actualProb += val
                except:
                    actualProb = 0
                probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])] = ((data.domain[self.attrX].values[i], valx), (data.domain[self.attrY].values[j], valy), actualProb, len(data))

        # get text width of Y attribute name
        text = OWCanvasText(self.canvas, data.domain[self.attrY].name, x  = 0, y = 0, bold = 1, show = 0, vertical=True)
        xOff = int(text.boundingRect().height() + 40)
        yOff = 50
        sqareSize = min(self.canvasView.width() - xOff - 35, self.canvasView.height() - yOff - 30)
        if sqareSize < 0: return    # canvas is too small to draw rectangles
        self.canvasView.setSceneRect(0, 0, self.canvasView.width(), self.canvasView.height())

        # print graph name
        if self.attrCondition == "(None)":
            name  = "<b>P(%s, %s) &#8800; P(%s)&times;P(%s)</b>" %(self.attrX, self.attrY, self.attrX, self.attrY)
        else:
            name = "<b>P(%s, %s | %s = %s) &#8800; P(%s | %s = %s)&times;P(%s | %s = %s)</b>" %(self.attrX, self.attrY, self.attrCondition, getHtmlCompatibleString(self.attrConditionValue), self.attrX, self.attrCondition, getHtmlCompatibleString(self.attrConditionValue), self.attrY, self.attrCondition, getHtmlCompatibleString(self.attrConditionValue))
        OWCanvasText(self.canvas, "" , xOff+ sqareSize/2, 20, Qt.AlignCenter, htmlText = name)
        OWCanvasText(self.canvas, "N = " + str(len(data)), xOff+ sqareSize/2, 38, Qt.AlignCenter, bold = 0)

        ######################
        # compute chi-square
        chisquare = 0.0
        for i in range(len(valsX)):
            for j in range(len(valsY)):
                ((xAttr, xVal), (yAttr, yVal), actual, sum_) = probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                expected = float(xVal*yVal)/float(sum_)
                if expected == 0: continue
                pearson2 = (actual - expected)*(actual - expected) / expected
                chisquare += pearson2

        ######################
        # draw rectangles
        currX = xOff
        max_ylabel_w = 0

        normX, normY = sum(valsX), sum(valsY)
        for i in range(len(valsX)):
            if valsX[i] == 0: continue
            currY = yOff
            width = int(float(sqareSize * valsX[i])/float(normX))
            
            #for j in range(len(valsY)):
            for j in range(len(valsY)-1, -1, -1):   # this way we sort y values correctly
                ((xAttr, xVal), (yAttr, yVal), actual, sum_) = probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                if valsY[j] == 0: continue
                height = int(float(sqareSize * valsY[j])/float(normY))

                # create rectangle
                rect = OWCanvasRectangle(self.canvas, currX+2, currY+2, width-4, height-4, z = -10)
                self.addRectIndependencePearson(rect, currX+2, currY+2, width-4, height-4, (xAttr, xVal), (yAttr, yVal), actual, sum_)

                expected = float(xVal*yVal)/float(sum_)
                pearson = (actual - expected) / sqrt(expected)
                tooltipText = """<b>X Attribute: %s</b><br>Value: <b>%s</b><br>Number of examples (p(x)): <b>%d (%.2f%%)</b><hr>
                                <b>Y Attribute: %s</b><br>Value: <b>%s</b><br>Number of examples (p(y)): <b>%d (%.2f%%)</b><hr>
                                <b>Number Of Examples (Probabilities):</b><br>Expected (p(x)p(y)): <b>%.1f (%.2f%%)</b><br>Actual (p(x,y)): <b>%d (%.2f%%)</b>
                                <hr><b>Statistics:</b><br>Chi-square: <b>%.2f</b><br>Standardized Pearson residual: <b>%.2f</b>""" %(self.attrX, getHtmlCompatibleString(xAttr), xVal, 100.0*float(xVal)/float(sum_), self.attrY, getHtmlCompatibleString(yAttr), yVal, 100.0*float(yVal)/float(sum_), expected, 100.0*float(xVal*yVal)/float(sum_*sum_), actual, 100.0*float(actual)/float(sum_), chisquare, pearson )
                rect.setToolTip(tooltipText)

                currY += height
                if currX == xOff:
                    xl = OWCanvasText(self.canvas, "", xOff - 10, currY - height/2, Qt.AlignRight | Qt.AlignVCenter, htmlText = getHtmlCompatibleString(data.domain[self.attrY].values[j]))
                    max_ylabel_w = max(int(xl.boundingRect().width()), max_ylabel_w)

            OWCanvasText(self.canvas, "", currX + width/2, yOff + sqareSize + 5, Qt.AlignCenter, htmlText = getHtmlCompatibleString(data.domain[self.attrX].values[i]))
            currX += width

        # show attribute names
        OWCanvasText(self.canvas, self.attrY, max(xOff-20-max_ylabel_w, 20), yOff + sqareSize/2, Qt.AlignRight | Qt.AlignVCenter, bold = 1, vertical=True)
        OWCanvasText(self.canvas, self.attrX, xOff + sqareSize/2, yOff + sqareSize + 15, Qt.AlignCenter, bold = 1)

        #self.canvas.update()

    # create a dictionary with all possible pairs of "combination-of-attr-values" : count
    def getConditionalDistributions(self, data, attrs):
        cond_dist = defaultdict(int)
        all_attrs = [data.domain[a] for a in attrs]
        if data.domain.class_var is not None:
            all_attrs.append(data.domain.class_var)

        for i in range(1, len(all_attrs) + 1):
            attr = all_attrs[:i]
            if type(data) == SqlTable:
                # make all possible pairs of attributes + class_var
                attr = [a.to_sql() for a in attr]
                fields = attr + ["COUNT(*)"]
                query = data._sql_query(fields, group_by=attr)
                with data._execute_sql_query(query) as cur:
                    res = cur.fetchall()
                for r in res:
                    str_values =[a.repr_val(a.to_val(x)) for a, x in zip(all_attrs, r[:-1])]
                    str_values = [x if x != '?' else 'None' for x in str_values]
                    cond_dist['-'.join(str_values)] = r[-1]
            else:
                for indices in product(*(range(len(a.values)) for a in attr)):
                    vals = []
                    conditions = []
                    for k, ind in enumerate(indices):
                        vals.append(attr[k].values[ind])
                        fd = Orange.data.filter.FilterDiscrete(column=attr[k], values=[attr[k].values[ind]])
                        conditions.append(fd)
                    filt = Orange.data.filter.Values(conditions)
                    filtdata = filt(data)
                    cond_dist['-'.join(vals)] = len(filtdata)
        return cond_dist

    ######################################################################
    ## show deviations from attribute independence with standardized pearson residuals
    def addRectIndependencePearson(self, rect, x, y, w, h, xAttr_xVal, yAttr_yVal, actual, sum):
        xAttr, xVal = xAttr_xVal
        yAttr, yVal = yAttr_yVal
        expected = float(xVal*yVal)/float(sum)
        pearson = (actual - expected) / sqrt(expected)

        if pearson > 0:     # if there are more examples that we would expect under the null hypothesis
            intPearson = floor(pearson)
            pen = QPen(QColor(0,0,255), 1); rect.setPen(pen)
            b = 255
            r = g = 255 - intPearson*20
            r = g = max(r, 55)  #
        elif pearson < 0:
            intPearson = ceil(pearson)
            pen = QPen(QColor(255,0,0), 1)
            rect.setPen(pen)
            r = 255
            b = g = 255 + intPearson*20
            b = g = max(b, 55)
        else:
            pen = QPen(QColor(255,255,255), 1)
            r = g = b = 255         # white
        color = QColor(r,g,b)
        brush = QBrush(color); rect.setBrush(brush)

        if self.showCases and w > 6 and h > 6:
            if self.showInColor:
                if pearson > 0: c = QColor(0,0,255)
                else: c = QColor(255, 0,0)
            else: c = Qt.black
            for i in range(int(actual)):
                OWCanvasEllipse(self.canvas, random.randint(x+1, x + w-4), random.randint(y+1, y + h-4), 3, 3, penColor = c, brushColor = c, z = 100)

        if pearson > 0:
            pearson = min(pearson, 10)
            kvoc = 1 - 0.08 * pearson       #  if pearson in [0..10] --> kvoc in [1..0.2]
        else:
            pearson = max(pearson, -10)
            kvoc = 1 - 0.4*pearson

        self.addLines(x,y,w,h, kvoc, pen)


    ##################################################
    # add lines
    def addLines(self, x,y,w,h, diff, pen):
        if not self.showLines: return
        if w == 0 or h == 0: return

        # create lines
        dist = 20   # original distance between two lines in pixels
        dist = dist * diff
        temp = dist
        while (temp < w):
            OWCanvasLine(self.canvas, temp+x, y, temp+x, y+h, 1, pen.color())
            temp += dist

        temp = dist
        while (temp < h):
            OWCanvasLine(self.canvas, x, y+temp, x+w, y+temp, 1, pen.color())
            temp += dist

    def saveToFileCanvas(self):
        sizeDlg = OWChooseImageSizeDlg(self.canvas, parent=self)
        sizeDlg.exec_()

    def closeEvent(self, ce):
        # self.optimizationDlg.hide()
        QDialog.closeEvent(self, ce)
class NostraIllustCheckDlg(QtGui.QDialog, FORM_CLASS):
    def __init__(self, theCanvas, theLayer, selFeatureIds, parent=None):
        super(NostraIllustCheckDlg, self).__init__(parent)
        self.setupUi(self)
        self.mTheCanvas = theCanvas
        self.mTheLayer = theLayer
        self.mSelFeatureIds = selFeatureIds
        self.mAllFeatureIds = []
        self.highlightList = []
        uri = QgsDataSourceURI(self.mTheLayer.source())
        self.conn = psycopg2.connect('''host='%s' dbname='%s' user='******' password='******' ''' %\
            (uri.host(), uri.database(), uri.username(), uri.password()))
        self.pg = self.conn.cursor()

        # members shown in graphic view.
        self.mScene = QGraphicsScene()
        self.graphicsViewShowImage.setScene(self.mScene)
        self.mPixmapList = []

        featureIter = self.mTheLayer.getFeatures(QgsFeatureRequest().setFlags(
            QgsFeatureRequest.NoGeometry))
        inti = 0
        theFeature = QgsFeature()
        while (featureIter.nextFeature(theFeature)):
            inti += 1
            self.mAllFeatureIds.append(theFeature.id())

        self.spinBoxFeatureIndex.setValue(1)
        self.spinBoxFeatureIndex.setMinimum(1)
        self.spinBoxFeatureIndex.setMaximum(inti)

        errMsg = ['']
        self.initComboBoxOutlinkid()
        self.selectFeatureById(errMsg,
                               self.mSelFeatureIds[0],
                               bZoomToSelected=False)
        self.comboBoxOutlinkid.setFocus()

        self.pushButtonPrev.clicked.connect(self.onPushButtonPrev)
        self.pushButtonNext.clicked.connect(self.onPushButtonNext)
        self.connect(self.comboBoxOutlinkid,
                     QtCore.SIGNAL('activated(QString)'),
                     self.comboBoxOutlinkidChanged)

    def resizeEvent(self, event):
        self.showImageInGraphicsView()
        return

    def disableAllControls(self):
        self.pushButtonPrev.setEnabled(False)
        self.pushButtonPrev.setEnabled(False)
        self.comboBoxOutlinkid.setEnabled(False)
        self.textEditFeatureInfo.setEnabled(False)
        return

    def initComboBoxOutlinkid(self):
        while (self.comboBoxOutlinkid.count() > 0):
            self.comboBoxOutlinkid.removeItem(0)
        for oneFeatureId in self.mSelFeatureIds:
            featureIter = self.mTheLayer.getFeatures(
                QgsFeatureRequest(oneFeatureId).setFlags(
                    QgsFeatureRequest.NoGeometry))
            theFeature = QgsFeature()
            if featureIter.nextFeature(theFeature) == False:
                return
            if self.mIsMyFeature(theFeature):
                strTemp = "%.0f, %.0f" % (theFeature.attribute('arc1'),
                                          theFeature.attribute('arc2'))
                self.comboBoxOutlinkid.addItem(strTemp)

    def showFeatureDetail(self, errMsg, theFeature):
        strFeatureInfo = self.getFeatureInfoString(theFeature)
        self.textEditFeatureInfo.setText(strFeatureInfo)
        if self.mIsMyFeature(theFeature) == False:
            return

        errMsg = ['']
        day_pic = theFeature.attribute('day_pic')
        arrowimg = theFeature.attribute('arrowimg')
        basePath = r"\\tangpinghui\20151010_nostra_junctionview\output_jpg_and_png"
        day_pic_path = os.path.join(basePath, day_pic + ".psd\Main_Day.jpg")
        arrowimg_path = ''
        tempPath = os.path.join(basePath, day_pic + ".psd")
        for curDir, subDirs, fileNames in os.walk(tempPath):
            for oneFile in fileNames:
                if oneFile.find(arrowimg) != -1 and oneFile.find('_en') != -1:
                    arrowimg_path = os.path.join(curDir, oneFile)

        self.mPixmapList = []
        patternPixmap = QPixmap()
        patternPixmap.load(day_pic_path)
        self.mPixmapList.append(patternPixmap)
        arrowPixmap = QPixmap()
        arrowPixmap.load(arrowimg_path)
        self.mPixmapList.append(arrowPixmap)
        self.showImageInGraphicsView()
        return

    def showImageInGraphicsView(self):
        # remove all items in member QGraphicsScene.
        for oneItem in self.mScene.items():
            self.mScene.removeItem(oneItem)

        for onePixmap in self.mPixmapList:
            self.mScene.addPixmap(
                self.getPixMapSizedByWidgt(onePixmap,
                                           self.graphicsViewShowImage))

        self.mScene.setSceneRect(0, 0,
                                 self.graphicsViewShowImage.width() - 5,
                                 self.graphicsViewShowImage.height() - 5)
        return

    def getFeatureInfoString(self, theFeature):
        fieldList = theFeature.fields()
        attrList = theFeature.attributes()
        strFeatureInfo = "field count: %d\n" % len(fieldList)
        for oneField, oneAttr in zip(fieldList, attrList):
            if isinstance(oneAttr, float):
                strFeatureInfo += "%s: %.0f\n" % (oneField.name(), oneAttr)
            else:
                strFeatureInfo += "%s: %s\n" % (oneField.name(), oneAttr)
        return strFeatureInfo

    def comboBoxOutlinkidChanged(self, txt):
        errMsg = ['']
        inti = self.comboBoxOutlinkid.currentIndex()
        self.selectFeatureById(errMsg,
                               self.mSelFeatureIds[inti],
                               bZoomToSelected=False)
        if errMsg[0] <> '':
            QMessageBox.information(self, "Nostra Illust Check",
                                    """error:\n%s""" % errMsg[0])
            return
        return

    def onPushButtonPrev(self):
        self.spinBoxFeatureIndex.setValue(self.spinBoxFeatureIndex.value() - 1)
        prevFeatureId = self.mAllFeatureIds[self.spinBoxFeatureIndex.value() -
                                            1]
        errMsg = ['']
        self.selectFeatureById(errMsg, prevFeatureId)
        if errMsg[0] <> '':
            QMessageBox.information(self, "Nostra Illust Check",
                                    """error:\n%s""" % errMsg[0])
            return
        return

    def onPushButtonNext(self):
        self.spinBoxFeatureIndex.setValue(self.spinBoxFeatureIndex.value() + 1)
        nextFeatureId = self.mAllFeatureIds[self.spinBoxFeatureIndex.value() -
                                            1]
        errMsg = ['']
        self.selectFeatureById(errMsg, nextFeatureId)
        if errMsg[0] <> '':
            QMessageBox.information(self, "Nostra Illust Check",
                                    """error:\n%s""" % errMsg[0])
            return
        return

    def selectFeatureById(self, errMsg, featureId, bZoomToSelected=True):
        self.mTheLayer.removeSelection()
        self.mTheLayer.select(featureId)
        theFeature = self.mTheLayer.selectedFeatures()[0]
        self.showFeatureDetail(errMsg, theFeature)
        if errMsg[0] <> '':
            return
        if bZoomToSelected == True:
            center = self.mTheCanvas.panToSelected(self.mTheLayer)
            self.mTheCanvas.refresh()
        self.clearHighlight()
        arc1 = "%.0f" % theFeature.attribute('arc1')
        sqlcmd = """
SET bytea_output TO escape;
select st_asbinary(the_geom) from temp_topo_link where routeid=%s
""" % arc1
        self.pg.execute(sqlcmd)
        rows = self.pg.fetchall()
        for row in rows:
            qgsGeometry = QgsGeometry()
            qgsGeometry.fromWkb(str(row[0]))
            self.highlightByGeometry(qgsGeometry, QColor(249, 27, 15, 168))

        arc2 = "%.0f" % theFeature.attribute('arc2')
        sqlcmd = """
SET bytea_output TO escape;
select st_asbinary(the_geom) from temp_topo_link where routeid=%s
""" % arc2
        self.pg.execute(sqlcmd)
        rows = self.pg.fetchall()
        for row in rows:
            qgsGeometry = QgsGeometry()
            qgsGeometry.fromWkb(str(row[0]))
            self.highlightByGeometry(qgsGeometry, QColor(22, 151, 0, 168))

        new_arc1 = "%.0f" % theFeature.attribute('new_arc1')
        if new_arc1 is not None and new_arc1 <> arc1:
            sqlcmd = """
SET bytea_output TO escape;
select st_asbinary(the_geom) from temp_topo_link where routeid=%s
""" % new_arc1
            self.pg.execute(sqlcmd)
            rows = self.pg.fetchall()
            for row in rows:
                qgsGeometry = QgsGeometry()
                qgsGeometry.fromWkb(str(row[0]))
                self.highlightByGeometry(qgsGeometry,
                                         QColor(253, 158, 153, 128))

        new_arc2 = "%.0f" % theFeature.attribute('new_arc2')
        if new_arc2 is not None and new_arc2 <> arc2:
            sqlcmd = """
SET bytea_output TO escape;
select st_asbinary(the_geom) from temp_topo_link where routeid=%s
""" % new_arc2
            self.pg.execute(sqlcmd)
            rows = self.pg.fetchall()
            for row in rows:
                qgsGeometry = QgsGeometry()
                qgsGeometry.fromWkb(str(row[0]))
                self.highlightByGeometry(qgsGeometry,
                                         QColor(147, 255, 155, 128))

        return

    def getPixMapSizedByWidgt(self, pixmap, theWidgt):
        pixmapWidth = pixmap.width()
        pixmapHeight = pixmap.height()
        gpViewWidth = theWidgt.width()
        gpViewHeight = theWidgt.height()

        destWidth = 0
        destHeight = 0
        if pixmapWidth > gpViewWidth - 5:
            destWidth = gpViewWidth - 5
        else:
            destWidth = pixmapWidth

        if pixmapHeight > gpViewHeight - 5:
            destHeight = gpViewHeight - 5
        else:
            destHeight = pixmapHeight

        return pixmap.scaled(destWidth, destHeight,
                             QtCore.Qt.IgnoreAspectRatio,
                             QtCore.Qt.SmoothTransformation)

    def mIsMyFeature(self, theFeature):
        return NostraIllustCheckDlg.isMyFeature(theFeature)

    @staticmethod
    def isMyFeature(theFeature):
        try:
            gid = theFeature.attribute('gid')
            arc1 = theFeature.attribute('arc1')
            arc2 = theFeature.attribute('arc2')
            day_pic = theFeature.attribute('day_pic')
            night_pic = theFeature.attribute('night_pic')
            arrowimg = theFeature.attribute('arrowimg')
            lon = theFeature.attribute('lon')
            lat = theFeature.attribute('lat')
            new_arc1 = theFeature.attribute('new_arc1')
            new_arc2 = theFeature.attribute('new_arc2')
        except KeyError, kErr:
            return False
        except Exception, ex:
            return False
Example #8
0
class PaintArea(QGraphicsView):
    def __init__(self, parent):
        QGraphicsView.__init__(self)
        self.init(parent)
        self.initShape()
        self.initCall = 2
        self.minimumWidth = 550
        self.setEnabled(False)

    def init(self, parent):
        self.timeline = parent
        self.pixmap = None
        self.image = None

    def initShape(self):
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setAlignment(Qt.AlignLeft)
        self.clicStart = None
        self.selectionRect = None

    def resizeEvent(self, sizEvent):
        if self.selectionRect and self.scene:
          self.scene.removeItem(self.selectionRect)
          self.selectionRect = None
          self.clicStart = None

        self.width = sizEvent.size().width()
        self.height = sizEvent.size().height()

        if self.initCall > 0:
          self.timeline.options.setMaximumWidth(self.timeline.options.minimumSizeHint().width())
          self.initCall -= 1

        if self.width < self.minimumWidth:
          self.timeline.setStateInfo('Unable to draw - Not enought width')
        else:
          self.timeline.workerThread.render()
          

    def mousePressEvent(self, mouseEvent):
      self.clicStart = mouseEvent.pos()
      if self.clicStart.x() < self.timeline.draw.yLeftMargin - 1:
        self.clicStart.setX(self.timeline.draw.yLeftMargin - 1)
      if self.clicStart.x() > self.timeline.ploter.width - self.timeline.m + 1:
        self.clicStart.setX(self.timeline.ploter.width - self.timeline.m + 1)
        
      if self.selectionRect:
        for item in self.scene.items():
          if str(type(item)) == "<class 'PyQt4.QtGui.QGraphicsRectItem'>":
            self.scene.removeItem(item)
        self.selectionRect = None
        self.timeline.options.zoomButton.setEnabled(False)
        self.timeline.options.exportButton.setEnabled(False)
        self.timeline.options.selectedNodes.setText('Nothing selected')
        if self.timeline.selDateMin:
          self.timeline.options.selStartTime.setText('From ' + str(self.timeline.fromUSec(self.timeline.baseDateMin).strftime('%d.%m.%Y %H:%M:%S')))
          self.timeline.options.selEndTime.setText('To ' + str(self.timeline.fromUSec(self.timeline.baseDateMax).strftime('%d.%m.%Y %H:%M:%S')))
        else:
          self.timeline.options.selStartTime.setText('No selection start time')
          self.timeline.options.selEndTime.setText('No selection end time')
        self.timeline.options.selectedNodes.setText('Nothing selected')
 

    def mouseMoveEvent(self, dragMoveEvent):
      if self.clicStart:
        if self.clicStart.x() < dragMoveEvent.x():
          x = self.clicStart.x()
          w = dragMoveEvent.x() - self.clicStart.x()
        else:
          x = dragMoveEvent.x()
          w = self.clicStart.x() - dragMoveEvent.x()

# Limit rectangle to selectable area
        if x < self.timeline.draw.yLeftMargin - 1:
          x = self.timeline.draw.yLeftMargin - 1
          w = self.clicStart.x() - x
        if x > self.timeline.ploter.width - self.timeline.m + 1:
          x = self.timeline.ploter.width - self.timeline.m + 1
          w = 0
        if x + w > self.timeline.ploter.width - self.timeline.m + 1:
          w = ((self.timeline.ploter.width - self.timeline.m + 1) - x)
        
        y = (self.timeline.m / 3) - 1
        h = self.height - self.timeline.m - self.timeline.m / 3 + 2
        if self.selectionRect and self.scene:
          self.scene.removeItem(self.selectionRect)
        self.selectionRect = self.scene.addRect(QRectF(x, y, w, h), QPen(Qt.DashDotDotLine))

    def mouseReleaseEvent(self, mouseEvent):
      if self.selectionRect:
        if self.clicStart.x() > mouseEvent.x():
          x1 = mouseEvent.x()
          x2 = self.clicStart.x()
        else:
          x1 = self.clicStart.x()
          x2 = mouseEvent.x()
        self.timeline.nodesInRange(x1, x2)
        start = self.timeline.draw.findXTime(x1)
        if start:
          self.timeline.options.selStartTime.setText('From ' + str(start.strftime('%d.%m.%Y %H:%M:%S')))
        end = self.timeline.draw.findXTime(x2)
        if end:
          self.timeline.options.selEndTime.setText('To ' + str(end.strftime('%d.%m.%Y %H:%M:%S')))
class ImagesPreviewer(foundations.ui.common.QWidgetFactory(uiFile=UI_FILE)):
    """
	| This class provides the Application images previewer.
	| It defines methods to navigate through the list of given images ( List of images paths ),
		zoom in / out and fit the displayed image, etc...
	"""
    def __init__(self, parent, paths=None, *args, **kwargs):
        """
		This method initializes the class.

		:param parent: Object parent. ( QObject )
		:param paths: Images paths. ( Tuple / List )
		:param \*args: Arguments. ( \* )
		:param \*\*kwargs: Keywords arguments. ( \*\* )
		"""

        LOGGER.debug("> Initializing '{0}()' class.".format(
            self.__class__.__name__))

        super(ImagesPreviewer, self).__init__(parent, *args, **kwargs)

        # --- Setting class attributes. ---
        self.__container = parent
        self.__paths = None
        self.paths = paths

        self.__uiResourcesDirectory = "resources"
        self.__uiResourcesDirectory = os.path.join(os.path.dirname(__file__),
                                                   self.__uiResourcesDirectory)
        self.__uiPreviousImage = "Previous.png"
        self.__uiNextImage = "Next.png"
        self.__uiZoomOutImage = "Zoom_Out.png"
        self.__uiZoomInImage = "Zoom_In.png"

        # Ensure the ui object is destroyed on close to avoid memory leaks.
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.__graphicsSceneBackgroundColor = QColor(32, 32, 32)
        self.__minimumZoomFactor = 0.05
        self.__maximumZoomFactor = 25
        self.__displayGraphicsItemMargin = 32
        self.__graphicsSceneWidth = QApplication.desktop().screenGeometry(
            QApplication.desktop().primaryScreen()).width() * (
                1 / self.__minimumZoomFactor * 1.75)
        self.__graphicsSceneHeight = QApplication.desktop().screenGeometry(
            QApplication.desktop().primaryScreen()).height() * (
                1 / self.__minimumZoomFactor * 1.75)
        self.__wheelZoomFactor = 350.0
        self.__keyZoomFactor = 1.20

        self.__graphicsView = None
        self.__graphicsScene = None
        self.__displayGraphicsItem = None

        ImagesPreviewer.__initializeUi(self)

        self.loadImage()

    #******************************************************************************************************************
    #***	Attributes properties.
    #******************************************************************************************************************
    @property
    def container(self):
        """
		This method is the property for **self.__container** attribute.

		:return: self.__container. ( QObject )
		"""

        return self.__container

    @container.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def container(self, value):
        """
		This method is the setter method for **self.__container** attribute.

		:param value: Attribute value. ( QObject )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "container"))

    @container.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def container(self):
        """
		This method is the deleter method for **self.__container** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "container"))

    @property
    def paths(self):
        """
		This method is the property for **self.__paths** attribute.

		:return: self.__paths. ( Tuple / List )
		"""

        return self.__paths

    @paths.setter
    @foundations.exceptions.handleExceptions(AssertionError)
    def paths(self, value):
        """
		This method is the setter method for **self.__paths** attribute.

		:param value: Attribute value. ( Tuple / List )
		"""

        if value is not None:
            assert type(value) in (
                tuple, list
            ), "'{0}' attribute: '{1}' type is not 'tuple' or 'list'!".format(
                "paths", value)
            for element in value:
                assert type(
                    element
                ) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format(
                    "paths", element)
        self.__paths = value

    @paths.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def paths(self):
        """
		This method is the deleter method for **self.__paths** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "paths"))

    @property
    def uiResourcesDirectory(self):
        """
		This method is the property for **self.__uiResourcesDirectory** attribute.

		:return: self.__uiResourcesDirectory. ( String )
		"""

        return self.__uiResourcesDirectory

    @uiResourcesDirectory.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiResourcesDirectory(self, value):
        """
		This method is the setter method for **self.__uiResourcesDirectory** attribute.

		:param value: Attribute value. ( String )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "uiResourcesDirectory"))

    @uiResourcesDirectory.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiResourcesDirectory(self):
        """
		This method is the deleter method for **self.__uiResourcesDirectory** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "uiResourcesDirectory"))

    @property
    def uiPreviousImage(self):
        """
		This method is the property for **self.__uiPreviousImage** attribute.

		:return: self.__uiPreviousImage. ( String )
		"""

        return self.__uiPreviousImage

    @uiPreviousImage.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiPreviousImage(self, value):
        """
		This method is the setter method for **self.__uiPreviousImage** attribute.

		:param value: Attribute value. ( String )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "uiPreviousImage"))

    @uiPreviousImage.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiPreviousImage(self):
        """
		This method is the deleter method for **self.__uiPreviousImage** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "uiPreviousImage"))

    @property
    def uiNextImage(self):
        """
		This method is the property for **self.__uiNextImage** attribute.

		:return: self.__uiNextImage. ( String )
		"""

        return self.__uiNextImage

    @uiNextImage.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiNextImage(self, value):
        """
		This method is the setter method for **self.__uiNextImage** attribute.

		:param value: Attribute value. ( String )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "uiNextImage"))

    @uiNextImage.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiNextImage(self):
        """
		This method is the deleter method for **self.__uiNextImage** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "uiNextImage"))

    @property
    def uiZoomOutImage(self):
        """
		This method is the property for **self.__uiZoomOutImage** attribute.

		:return: self.__uiZoomOutImage. ( String )
		"""

        return self.__uiZoomOutImage

    @uiZoomOutImage.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiZoomOutImage(self, value):
        """
		This method is the setter method for **self.__uiZoomOutImage** attribute.

		:param value: Attribute value. ( String )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "uiZoomOutImage"))

    @uiZoomOutImage.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiZoomOutImage(self):
        """
		This method is the deleter method for **self.__uiZoomOutImage** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "uiZoomOutImage"))

    @property
    def uiZoomInImage(self):
        """
		This method is the property for **self.__uiZoomInImage** attribute.

		:return: self.__uiZoomInImage. ( String )
		"""

        return self.__uiZoomInImage

    @uiZoomInImage.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiZoomInImage(self, value):
        """
		This method is the setter method for **self.__uiZoomInImage** attribute.

		:param value: Attribute value. ( String )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "uiZoomInImage"))

    @uiZoomInImage.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def uiZoomInImage(self):
        """
		This method is the deleter method for **self.__uiZoomInImage** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "uiZoomInImage"))

    @property
    def graphicsSceneBackgroundColor(self):
        """
		This method is the property for **self.__graphicsSceneBackgroundColor** attribute.

		:return: self.__graphicsSceneBackgroundColor. ( QColors )
		"""

        return self.__graphicsSceneBackgroundColor

    @graphicsSceneBackgroundColor.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneBackgroundColor(self, value):
        """
		This method is the setter method for **self.__graphicsSceneBackgroundColor** attribute.

		:param value: Attribute value. ( QColors )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "graphicsSceneBackgroundColor"))

    @graphicsSceneBackgroundColor.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneBackgroundColor(self):
        """
		This method is the deleter method for **self.__graphicsSceneBackgroundColor** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "graphicsSceneBackgroundColor"))

    @property
    def graphicsSceneWidth(self):
        """
		This method is the property for **self.__graphicsSceneWidth** attribute.

		:return: self.__graphicsSceneWidth. ( Integer )
		"""

        return self.__graphicsSceneWidth

    @graphicsSceneWidth.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneWidth(self, value):
        """
		This method is the setter method for **self.__graphicsSceneWidth** attribute.

		:param value: Attribute value. ( Integer )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "graphicsSceneWidth"))

    @graphicsSceneWidth.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneWidth(self):
        """
		This method is the deleter method for **self.__graphicsSceneWidth** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "graphicsSceneWidth"))

    @property
    def graphicsSceneHeight(self):
        """
		This method is the property for **self.__graphicsSceneHeight** attribute.

		:return: self.__graphicsSceneHeight. ( Object )
		"""

        return self.__graphicsSceneHeight

    @graphicsSceneHeight.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneHeight(self, value):
        """
		This method is the setter method for **self.__graphicsSceneHeight** attribute.

		:param value: Attribute value. ( Object )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "graphicsSceneHeight"))

    @graphicsSceneHeight.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsSceneHeight(self):
        """
		This method is the deleter method for **self.__graphicsSceneHeight** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "graphicsSceneHeight"))

    @property
    def minimumZoomFactor(self):
        """
		This method is the property for **self.__minimumZoomFactor** attribute.

		:return: self.__minimumZoomFactor. ( Float )
		"""

        return self.__minimumZoomFactor

    @minimumZoomFactor.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def minimumZoomFactor(self, value):
        """
		This method is the setter method for **self.__minimumZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "minimumZoomFactor"))

    @minimumZoomFactor.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def minimumZoomFactor(self):
        """
		This method is the deleter method for **self.__minimumZoomFactor** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "minimumZoomFactor"))

    @property
    def maximumZoomFactor(self):
        """
		This method is the property for **self.__maximumZoomFactor** attribute.

		:return: self.__maximumZoomFactor. ( Float )
		"""

        return self.__maximumZoomFactor

    @maximumZoomFactor.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def maximumZoomFactor(self, value):
        """
		This method is the setter method for **self.__maximumZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "maximumZoomFactor"))

    @maximumZoomFactor.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def maximumZoomFactor(self):
        """
		This method is the deleter method for **self.__maximumZoomFactor** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "maximumZoomFactor"))

    @property
    def wheelZoomFactor(self):
        """
		This method is the property for **self.__wheelZoomFactor** attribute.

		:return: self.__wheelZoomFactor. ( Float )
		"""

        return self.__wheelZoomFactor

    @wheelZoomFactor.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def wheelZoomFactor(self, value):
        """
		This method is the setter method for **self.__wheelZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "wheelZoomFactor"))

    @wheelZoomFactor.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def wheelZoomFactor(self):
        """
		This method is the deleter method for **self.__wheelZoomFactor** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "wheelZoomFactor"))

    @property
    def keyZoomFactor(self):
        """
		This method is the property for **self.__keyZoomFactor** attribute.

		:return: self.__keyZoomFactor. ( Float )
		"""

        return self.__keyZoomFactor

    @keyZoomFactor.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def keyZoomFactor(self, value):
        """
		This method is the setter method for **self.__keyZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "keyZoomFactor"))

    @keyZoomFactor.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def keyZoomFactor(self):
        """
		This method is the deleter method for **self.__keyZoomFactor** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "keyZoomFactor"))

    @property
    def graphicsView(self):
        """
		This method is the property for **self.__graphicsView** attribute.

		:return: self.__graphicsView. ( QGraphicsView )
		"""

        return self.__graphicsView

    @graphicsView.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsView(self, value):
        """
		This method is the setter method for **self.__graphicsView** attribute.

		:param value: Attribute value. ( QGraphicsView )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "graphicsView"))

    @graphicsView.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsView(self):
        """
		This method is the deleter method for **self.__graphicsView** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "graphicsView"))

    @property
    def graphicsScene(self):
        """
		This method is the property for **self.__graphicsScene** attribute.

		:return: self.__graphicsScene. ( QGraphicsScene )
		"""

        return self.__graphicsScene

    @graphicsScene.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsScene(self, value):
        """
		This method is the setter method for **self.__graphicsScene** attribute.

		:param value: Attribute value. ( QGraphicsScene )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "graphicsScene"))

    @graphicsScene.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def graphicsScene(self):
        """
		This method is the deleter method for **self.__graphicsScene** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "graphicsScene"))

    @property
    def displayGraphicsItem(self):
        """
		This method is the property for **self.__displayGraphicsItem** attribute.

		:return: self.__displayGraphicsItem. ( QGraphicsItem )
		"""

        return self.__displayGraphicsItem

    @displayGraphicsItem.setter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def displayGraphicsItem(self, value):
        """
		This method is the setter method for **self.__displayGraphicsItem** attribute.

		:param value: Attribute value. ( QGraphicsItem )
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is read only!".format(
                self.__class__.__name__, "displayGraphicsItem"))

    @displayGraphicsItem.deleter
    @foundations.exceptions.handleExceptions(
        foundations.exceptions.ProgrammingError)
    def displayGraphicsItem(self):
        """
		This method is the deleter method for **self.__displayGraphicsItem** attribute.
		"""

        raise foundations.exceptions.ProgrammingError(
            "{0} | '{1}' attribute is not deletable!".format(
                self.__class__.__name__, "displayGraphicsItem"))

    #******************************************************************************************************************
    #***	Class methods.
    #******************************************************************************************************************
    def show(self):
        """
		This method reimplements the :meth:`QWidget.show` method.
		"""

        super(ImagesPreviewer, self).show()

        foundations.ui.common.centerWidgetOnScreen(self)

    def closeEvent(self, event):
        """
		This method reimplements the :meth:`QWidget.closeEvent` method.

		:param event: QEvent ( QEvent )
		"""

        LOGGER.debug(
            "> Removing '{0}' from Images Previewers list.".format(self))
        self.__container.imagesPreviewers.remove(self)

        event.accept()

    def wheelEvent(self, event):
        """
		This method reimplements the :meth:`QWidget.wheelEvent` method.

		:param event: QEvent ( QEvent )
		"""

        self.scaleView(pow(1.5, event.delta() / self.__wheelZoomFactor))

    def keyPressEvent(self, event):
        """
		This method reimplements the :meth:`QWidget.keyPressEvent` method.

		:param event: QEvent ( QEvent )
		"""

        key = event.key()
        if key == Qt.Key_Plus:
            self.scaleView(self.__keyZoomFactor)
        elif key == Qt.Key_Minus:
            self.scaleView(1 / self.__keyZoomFactor)
        else:
            super(ImagesPreviewer, self).keyPressEvent(event)

    def __initializeUi(self):
        """
		This method initializes the Widget ui.
		"""

        LOGGER.debug("> Initializing '{0}' ui.".format(
            self.__class__.__name__))

        self.Previous_Image_pushButton.setIcon(
            QIcon(
                os.path.join(self.__uiResourcesDirectory,
                             self.__uiPreviousImage)))
        self.Next_Image_pushButton.setIcon(
            QIcon(os.path.join(self.__uiResourcesDirectory,
                               self.__uiNextImage)))
        self.Zoom_In_pushButton.setIcon(
            QIcon(
                os.path.join(self.__uiResourcesDirectory,
                             self.__uiZoomInImage)))
        self.Zoom_Out_pushButton.setIcon(
            QIcon(
                os.path.join(self.__uiResourcesDirectory,
                             self.__uiZoomOutImage)))
        len(self.__paths) <= 1 and self.Navigation_frame.hide()

        LOGGER.debug("> Initializing graphics View.")
        self.__graphicsView = QGraphicsView()
        self.__graphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.__graphicsView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.__graphicsView.setTransformationAnchor(
            QGraphicsView.AnchorUnderMouse)
        self.__graphicsView.setDragMode(QGraphicsView.ScrollHandDrag)
        # Reimplementing QGraphicsView wheelEvent method.
        self.__graphicsView.wheelEvent = self.wheelEvent

        LOGGER.debug("> Initializing graphics scene.")
        self.__graphicsScene = QGraphicsScene(self.__graphicsView)
        self.__graphicsScene.setItemIndexMethod(QGraphicsScene.NoIndex)
        self.__graphicsScene.setSceneRect(
            -(float(self.__graphicsSceneWidth)) / 2,
            -(float(self.__graphicsSceneHeight)) / 2,
            float(self.__graphicsSceneWidth),
            float(self.__graphicsSceneHeight))

        self.__graphicsView.setScene(self.__graphicsScene)
        self.__graphicsView.setBackgroundBrush(
            QBrush(self.__graphicsSceneBackgroundColor))

        self.Images_Previewer_frame_gridLayout.addWidget(self.__graphicsView)

        # Signals / Slots.
        self.__container.engine.imagesCaches.QImage.contentAdded.connect(
            self.__engine_imagesCaches_QImage__contentAdded)
        self.Previous_Image_pushButton.clicked.connect(
            self.__Previous_Image_pushButton__clicked)
        self.Next_Image_pushButton.clicked.connect(
            self.__Next_Image_pushButton__clicked)
        self.Zoom_Out_pushButton.clicked.connect(
            self.__Zoom_Out_pushButton__clicked)
        self.Zoom_In_pushButton.clicked.connect(
            self.__Zoom_In_pushButton__clicked)
        self.Zoom_Fit_pushButton.clicked.connect(
            self.__Zoom_Fit_pushButton__clicked)

    def __Images_Informations_label_setUi(self):
        """
		This method sets the **Images_Informations_label** Widget ui.
		"""

        if not self.__displayGraphicsItem:
            return

        image = self.__displayGraphicsItem.image
        self.Images_Informations_label.setText(
            "{0} - {1}x{2} px - {3} bit".format(
                os.path.basename(image.data.path), image.data.width,
                image.data.height, image.data.bpp / 4))

    def __engine_imagesCaches_QImage__contentAdded(self, content):
        """
		This method is triggered by the Application **QImage** images cache when content has been added.

		:param content: Cache added content. ( List )
		"""

        if not self.__paths:
            return

        path = foundations.common.getFirstItem(content)
        if not path in self.__paths:
            return

        image = self.__container.engine.imagesCaches.QImage.getContent(path)
        self.__setDisplayGraphicsItem(image)

    def __Previous_Image_pushButton__clicked(self, checked):
        """
		This method is triggered when **Previous_Image_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

        self.loopThroughImages(True)

    def __Next_Image_pushButton__clicked(self, checked):
        """
		This method is triggered when **Next_Image_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

        self.loopThroughImages()

    def __Zoom_In_pushButton__clicked(self, checked):
        """
		This method is triggered when **Zoom_In_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

        self.scaleView(self.__keyZoomFactor)

    def __Zoom_Out_pushButton__clicked(self, checked):
        """
		This method is triggered when **Zoom_Out_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

        self.scaleView(1 / self.__keyZoomFactor)

    def __Zoom_Fit_pushButton__clicked(self, checked):
        """
		This method is triggered when **Zoom_Fit_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

        self.fitImage()

    def __clearGraphicsScene(self):
        """
		This method clears the View.
		"""

        for graphicsItem in self.__graphicsScene.items():
            self.__graphicsScene.removeItem(graphicsItem)

    def __setDisplayGraphicsItem(self, image):
        """
		This method sets the View using given image.

		:param image: Image to display. ( Qimage )
		"""

        self.__clearGraphicsScene()

        LOGGER.debug("> Initializing graphics item.")
        self.__displayGraphicsItem = Image_QGraphicsItem(image=image)
        self.__graphicsScene.addItem(self.__displayGraphicsItem)

        self.__Images_Informations_label_setUi()

    def loadImage(self, index=0):
        """
		This method loads the display image in the View.

		:param index: Index to load. ( Integer )
		:return: Method success. ( Boolean )
		"""

        if not self.__paths:
            return False

        image = sibl_gui.ui.common.getImage(self.__paths[index])
        self.__setDisplayGraphicsItem(image)

        return True

    def scaleView(self, scaleFactor):
        """
		This method scales the Previewer view.

		:param scaleFactor: Float ( Float )
		:return: Method success. ( Boolean )
		"""

        graphicsView = self.findChild(QGraphicsView)
        factor = graphicsView.matrix().scale(scaleFactor, scaleFactor).mapRect(
            QRectF(0, 0, 1, 1)).width()
        if factor < self.__minimumZoomFactor or factor > self.__maximumZoomFactor:
            return False

        graphicsView.scale(scaleFactor, scaleFactor)
        return True

    def fitWindow(self):
        """
		This method fits the View window.
		
		:return: Method success. ( Boolean )
		"""

        if not self.__displayGraphicsItem:
            return False

        desktopWidth = QApplication.desktop().screenGeometry(
            QApplication.desktop().primaryScreen()).width()
        desktopHeight = QApplication.desktop().screenGeometry(
            QApplication.desktop().primaryScreen()).height()
        width = min(desktopWidth * 0.80, self.__displayGraphicsItem.width)
        height = min(desktopHeight * 0.80, self.__displayGraphicsItem.height)
        self.resize(width, height)

        foundations.ui.common.centerWidgetOnScreen(self)

        return True

    def fitImage(self):
        """
		This method fits the image to the View.
		
		:return: Method success. ( Boolean )
		"""

        if not self.__displayGraphicsItem:
            return False

        self.__graphicsView.fitInView(
            QRectF(
                -(self.__displayGraphicsItem.width / 2) -
                (self.__displayGraphicsItemMargin / 2),
                -(self.__displayGraphicsItem.height / 2) -
                (self.__displayGraphicsItemMargin / 2),
                self.__displayGraphicsItem.width +
                self.__displayGraphicsItemMargin,
                self.__displayGraphicsItem.height +
                self.__displayGraphicsItemMargin), Qt.KeepAspectRatio)
        return True

    def loopThroughImages(self, backward=False):
        """
		This method loops through View images.

		:param backward: Looping backward. ( Boolean )
		:return: Method success. ( Boolean )
		"""

        index = self.__paths.index(self.__displayGraphicsItem.image.data.path)
        index += not backward and 1 or -1
        if index < 0:
            index = len(self.__paths) - 1
        elif index > len(self.__paths) - 1:
            index = 0
        self.loadImage(index)
        return True
Example #10
0
class WsnViewer(QtGui.QMainWindow, Ui_WsnViewerUI):
    def __init__(self, parent=None):
        super(WsnViewer, self).__init__(parent)
        self.setupUi (self)
        self.m_nodeNumber = 10
        self.m_maxRfRange = 70
        self.m_macAddr = MacAddress ()
        self.m_nodeLoc = Coordonate ()
        self.m_nodeList = self.m_macAddr.GetListAddr(10)
        self.m_edgeList = 0 
        self.m_edgeDict = {}
        self.m_nodeLocDict = {}
        
        self.m_scene = QGraphicsScene (self.graphicsView)
        #self.m_paramWin = Ui_paramWindow ()
        self.m_graph = nx.Graph()
        self.m_data = list ()
        self.DataReader = Data ()
        
        self.graphicsView.setDragMode (self.graphicsView.RubberBandDrag)
        self.graphicsView.setRenderHints (QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        self.graphicsView.setScene (self.m_scene)
        self.ConnectActions ()
    
    def ConnectActions(self):
        self.actionQuit.triggered.connect (QtGui.qApp.quit)
        self.actionOpen.triggered.connect (self.OpenWsnTopology)
        self.actionRandom_topology.triggered.connect (self.GenerateRandWsn)
        self.actionTools.triggered.connect(self.SetRadioRange)
        self.actionSave.triggered.connect(self.Save)
        
    def OpenWsnTopology (self):
        fileName = QtGui.QFileDialog.getOpenFileName(
                        self,
                        "Ouvrir un fichier des positions des noeuds",
                        QtCore.QDir.homePath(),
                        "Fichiers texte (*.txt)"
                    )
        if (fileName):
            self.m_data = self.DataReader.ReadFile(fileName)
            
    def GenerateRandWsn (self):
        self.m_nodeList = self.m_macAddr.GetListAddr(self.m_nodeNumber)
        #print(self.m_nodeList)
        self.m_edgeList, self.m_nodeLocDict = self.m_nodeLoc.EdgeCreate (self.m_nodeList)
        #print(self.m_nodeLocDict)
        #print(self.m_edgeList)
        self.m_scene.clear()
        self.DrawGraph ()
             
    def main (self):
        self.show()
        
    def OpenDialog(self):
        self.dlg = OpenFileDialog ()
        self.dlg.show()
        
    def DrawGraph (self):
        for edge in self.m_edgeList:
            self.m_edgeDict [edge] = EdgeItem (self.m_nodeLocDict, edge[0], edge[1])
            self.m_scene.addItem(self.m_edgeDict [edge])
        for node in self.m_nodeLocDict.iterkeys():
            nodeItem = NodeItem (self.m_scene, self.m_nodeLocDict, node)
            #nodeItem.m_signalLinkNode.nodeItemSignal.connect (self.UpdateNodeLocDict)
            self.m_scene.addItem (nodeItem)
    
    def Save (self):
        filename = QtGui.QFileDialog.getSaveFileName (
                        self,
                        "Ouvrir un fichier des positions des noeuds",
                        QtCore.QDir.homePath(),
                        "Fichiers texte (*.txt)"
                    )
        if self.m_edgeList and filename:
            self.DataReader.WriteWSn(self.m_edgeList, self.m_nodeLocDict, filename)
    
    def UpdateNodeLocDict (self, nodeName, nodePos):
        self.m_nodeLocDict [nodeName] = nodePos
        for node, pos in self.m_nodeLocDict.items():
            if node != nodeName:
                edge = (nodeName, node)
                if self.Distance(nodePos, pos) <= self.m_maxRfRange:
                    if not edge in self.m_edgeList and not edge[::-1] in self.m_edgeList:
                        self.m_edgeList.append (edge)
                        self.m_edgeDict [edge] = EdgeItem (self.m_nodeLocDict, edge[0], edge[1])
                        self.m_scene.addItem(self.m_edgeDict [edge])
                        #print ("%s %s %s" % (node, pos[0], pos[1]))
                else:
                    self.RemoveLink (edge)
                    self.UpdateNodeList (edge)
                    #print ("%s %s %s" % (node, pos[0], pos[1]))
               
    def UpdateNodeList (self, edge):
        while edge in self.m_edgeList:
            self.m_edgeList.remove (edge)
        
        #while edge[::-1] in self.m_edgeList:
        #    self.m_edgeList.remove (edge[::-1])
            
    def RemoveLink (self, edge):
        if edge in self.m_edgeDict:
            self.m_scene.removeItem (self.m_edgeDict [edge])
            del self.m_edgeDict [edge]
            self.m_scene.update()
            print ("%s %s" % (edge[0], edge[1]))
            
        
    def UpdateScene (self, edge):
        self.m_scene.addItem(EdgeItem (self.m_nodeLocDict, edge[0], edge[1]))
                
    def Distance (self, u, v):
        x0, y0 = u
        x1, y1 = v
        return math.sqrt((x0 - x1)**2 + (y0 - y1)**2)
    
    def SetRadioRange (self):
        self.rfWindow = RfSettingWindow ()
        resultat = self.rfWindow.exec_()
        
        if resultat:
            self.rfWindow.SetRadioValues ()
            self.m_nodeNumber = self.rfWindow.GetNodeNumber ()
            self.m_maxRfRange = self.rfWindow.GetRfRange ()
            self.GenerateRandWsn ()
class SpotguideShowImageDlg(QtGui.QDialog, FORM_CLASS):
    def __init__(self, theCanvas, theLayer, selFeatureIds, parent=None):
        super(SpotguideShowImageDlg, self).__init__(parent)
        self.setupUi(self)
        self.mTheCanvas = theCanvas
        self.mTheLayer = theLayer
        self.mSelFeatureIds = selFeatureIds
        self.mAllFeatureIds = []
        self.highlightList = []

        # members shown in graphic view.
        self.mScene = QGraphicsScene()
        self.graphicsViewShowImage.setScene(self.mScene)
        self.mPixmapList = []

        featureIter = self.mTheLayer.getFeatures(QgsFeatureRequest().setFlags(
            QgsFeatureRequest.NoGeometry))
        inti = 0
        theFeature = QgsFeature()
        while (featureIter.nextFeature(theFeature) and inti < 512):
            inti += 1
            self.mAllFeatureIds.append(theFeature.id())

        self.spinBoxFeatureIndex.setValue(1)
        self.spinBoxFeatureIndex.setMinimum(1)
        self.spinBoxFeatureIndex.setMaximum(inti)

        errMsg = ['']
        self.initComboBoxOutlinkid()
        self.selectFeatureById(errMsg,
                               self.mSelFeatureIds[0],
                               bZoomToSelected=False)
        self.comboBoxOutlinkid.setFocus()

        self.pushButtonPrev.clicked.connect(self.onPushButtonPrev)
        self.pushButtonNext.clicked.connect(self.onPushButtonNext)
        self.connect(self.comboBoxOutlinkid,
                     QtCore.SIGNAL('activated(QString)'),
                     self.comboBoxOutlinkidChanged)

    def resizeEvent(self, event):
        self.showImageInGraphicsView()
        return

    def disableAllControls(self):
        self.pushButtonPrev.setEnabled(False)
        self.pushButtonPrev.setEnabled(False)
        self.comboBoxOutlinkid.setEnabled(False)
        self.textEditFeatureInfo.setEnabled(False)
        return

    def initComboBoxOutlinkid(self):
        while (self.comboBoxOutlinkid.count() > 0):
            self.comboBoxOutlinkid.removeItem(0)
        for oneFeatureId in self.mSelFeatureIds:
            featureIter = self.mTheLayer.getFeatures(
                QgsFeatureRequest(oneFeatureId).setFlags(
                    QgsFeatureRequest.NoGeometry))
            theFeature = QgsFeature()
            if featureIter.nextFeature(theFeature) == False:
                return
            if self.mIsMyFeature(theFeature):
                self.comboBoxOutlinkid.addItem(
                    str(theFeature.attribute('out_link_id')))

    def showFeatureDetail(self, errMsg, theFeature):
        strFeatureInfo = self.getFeatureInfoString(theFeature)
        self.textEditFeatureInfo.setText(strFeatureInfo)
        if self.mIsMyFeature(theFeature) == False:
            return

        errMsg = ['']
        pattern_dat, arrow_dat = self.getSpotguidePictures(
            errMsg, self.mTheLayer, theFeature)
        if errMsg[0] != '':
            #QMessageBox.information(self, "Show Spotguide", errMsg[0])
            return

        self.mPixmapList = []
        if pattern_dat is not None:
            patternDatParser = DatParser()
            patternDatParser.initFromMemory(errMsg,
                                            pattern_dat)  # pattern picture
            patternPixmap = QPixmap()
            patternPixmap.loadFromData(
                patternDatParser.getDatContentByIndex(errMsg, 0))
            self.mPixmapList.append(patternPixmap)

        if arrow_dat is not None:
            arrowDatParser = DatParser()
            arrowDatParser.initFromMemory(errMsg, arrow_dat)  # arrow picture
            arrowPixmap = QPixmap()
            arrowPixmap.loadFromData(
                arrowDatParser.getDatContentByIndex(errMsg, 0))
            if arrowDatParser.hasPointlist():
                # draw the point list on the arrow picture
                vecCoors = arrowDatParser.getPointListCoordinatesByIndex(
                    errMsg, arrowDatParser.getPointlistIndex())
                if errMsg[0] != '':
                    QMessageBox.information(self, "Show Spotguide", errMsg[0])
                    return
                with QPainter(arrowPixmap) as thePainter:
                    for oneXYPair in vecCoors:
                        thePainter.setPen(QPen(QColor(255, 0, 0)))
                        thePainter.drawPoint(oneXYPair[0], oneXYPair[1])
                        thePainter.drawPoint(oneXYPair[0] - 1, oneXYPair[1])
                        thePainter.drawPoint(oneXYPair[0] + 1, oneXYPair[1])
                        thePainter.drawPoint(oneXYPair[0], oneXYPair[1] - 1)
                        thePainter.drawPoint(oneXYPair[0], oneXYPair[1] + 1)

                # append pointlist information to the text box.
                strPointList = arrowDatParser.getPointListStringByIndex(
                    errMsg, arrowDatParser.getPointlistIndex())
                if errMsg[0] != '':
                    QMessageBox.information(self, "Show Spotguide", errMsg[0])
                    return
                strTemp = self.textEditFeatureInfo.toPlainText()
                strTemp += """\n\npointlist:\n"""
                strTemp += strPointList
                self.textEditFeatureInfo.setText(strTemp)
            self.mPixmapList.append(arrowPixmap)

        self.showImageInGraphicsView()
        return

    def showImageInGraphicsView(self):
        # remove all items in member QGraphicsScene.
        for oneItem in self.mScene.items():
            self.mScene.removeItem(oneItem)

        for onePixmap in self.mPixmapList:
            self.mScene.addPixmap(
                self.getPixMapSizedByWidgt(onePixmap,
                                           self.graphicsViewShowImage))

        self.mScene.setSceneRect(0, 0,
                                 self.graphicsViewShowImage.width() - 5,
                                 self.graphicsViewShowImage.height() - 5)
        return

    def getFeatureInfoString(self, theFeature):
        fieldList = theFeature.fields()
        attrList = theFeature.attributes()
        strFeatureInfo = "field count: %d\n" % len(fieldList)
        for oneField, oneAttr in zip(fieldList, attrList):
            if isinstance(oneAttr, float):
                strFeatureInfo += "%s: %.0f\n" % (oneField.name(), oneAttr)
            else:
                strFeatureInfo += "%s: %s\n" % (oneField.name(), oneAttr)
        return strFeatureInfo

    # result[0] is pattern dat
    # result[1] is arrow dat
    def getSpotguidePictures(self, errMsg, layer, theFeature):
        try:
            uri = QgsDataSourceURI(layer.source())
            conn = psycopg2.connect('''host='%s' dbname='%s' user='******' password='******' ''' %\
                (uri.host(), uri.database(), uri.username(), uri.password()))
            pg = conn.cursor()

            # all these lane's keys must be found
            # if anyone is not found, a 'KeyError' exception will be thrown.
            in_link_id = theFeature.attribute('in_link_id')
            node_id = theFeature.attribute('node_id')
            out_link_id = theFeature.attribute('out_link_id')
            passlink_count = theFeature.attribute('passlink_count')
            pattern_id = theFeature.attribute('pattern_id')
            arrow_id = theFeature.attribute('arrow_id')

            # spotguide record filter.
            strFilter = '''in_link_id=%s and node_id=%s and out_link_id=%s and 
                           passlink_count=%s and pattern_id=%s and arrow_id=%s''' % \
                        (in_link_id, node_id, out_link_id, passlink_count, pattern_id, arrow_id)

            sqlcmd = \
"""SET bytea_output TO escape;
select pattern_dat, arrow_dat
from %s
where %s
""" % (uri.table(), strFilter)
            pg.execute(sqlcmd)
            row = pg.fetchone()
            return row[0], row[1]
        except KeyError, kErr:
            errMsg[0] = """Selected feature is not a rdb spotguide feature."""
            return None, None
        except Exception, ex:
            errMsg[0] = ex.message
            return None, None
Example #12
0
class MainForm(QDialog):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        
        self.filename = QString()
        self.scene = QGraphicsScene(self)
        self.view = GraphicsView(self.scene)

        self.view.setScene(self.scene)
        buttonLayout = QVBoxLayout()
        for text, slot in (
                ("&Open", self.addPixmap),
                ("&Quit", self.accept)):
            button = QPushButton(text)
            self.connect(button, SIGNAL("clicked()"), slot)
            if text == "&Quit":
                buttonLayout.addStretch(1)
            buttonLayout.addWidget(button)
        buttonLayout.addStretch()
        layout = QHBoxLayout()
        layout.addWidget(self.view, 1)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)
        self.setWindowTitle("Petal Picker")

    def addPixmap(self):
        path = (QFileInfo(self.filename).path()
                if not self.filename.isEmpty() else ".")
        fname = QFileDialog.getOpenFileName(self,
                "Add Pixmap", path,
                "Pixmap Files (*.bmp *.jpg *.png *.xpm)")
        if fname.isEmpty(): return
        self.createPixmapItem(QPixmap(fname))


    def createPixmapItem(self, pixmap, matrix=QMatrix()):
        global PIX
        item = QGraphicsPixmapItem(pixmap)
        PIX = item
#        item.setFlags(QGraphicsItem.ItemIsSelectable|
#                      QGraphicsItem.ItemIsMovable)
        item.setMatrix(matrix)
        self.scene.clearSelection()
        
        for i in self.scene.items():
            self.scene.removeItem(i)
        
        self.scene.addItem(item)
        self.view.fitInView(item,Qt.KeepAspectRatio)
        
        
        global DIRTY
        DIRTY = True
        
    def accept(self):
        global DIRTY
        if not DIRTY: QDialog.accept(self)
        if DIRTY:
            if self.confirm(): QDialog.accept(self)
            else: return

    def confirm(self):
        global DIRTY
        if (DIRTY and QMessageBox.question(self,
                            "Really Quit?",
                            "Really Quit?",
                            QMessageBox.Yes|QMessageBox.No) ==
            QMessageBox.Yes): return True
        else: return False
Example #13
0
class GraphicsWidget(QWidget):

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

        self._ui = Ui_Form()
        self._ui.setupUi(self)

        self.commits = {}
        self.commits["first"] =  ["aaaaa", "bbbbb", "ccccc", "ddddd"]
        self.commits["second"] = ["ggggg", "bbbbb", "xxxxx", "aaaaa"]

        self.scene = QGraphicsScene(self)
        self.view = self._ui.graphicsView
        self.view.setScene(self.scene)
        self.view.setRenderHint(QPainter.Antialiasing)
        self.matching_commits = False
        self.view.setAcceptDrops(True)

        self.populate()

    def populate(self):
        self.temp_del_items = []
        self.commit_items = []

        self.filter = my_env_filter()
        self.scene.installEventFilter(self.filter)

        self.hints = Hints(0, -60)
        self.scene.addItem(self.hints)
        item_x = 0
        color = GREEN
        for branch in self.commits:
            item_y = 0

            for commit_name in self.commits[branch]:
                commit = Commit(commit_name)
                commit_background_item = CommitItem(item_x, item_y, 
                                                    color, commit, branch,
                                                    background_item=True)
                arrow = Arrow(item_x, item_y, commit_background_item)
                commit_display_item = CommitItem(item_x, item_y,
                                                 color, commit, branch,
                                                 background_item=False)

                self.scene.addItem(commit_display_item)
                self.scene.addItem(commit_background_item)
                self.commit_items.append(commit_background_item)

                item_y += COMMIT_HEIGHT + ARROW_HEIGHT

            item_x += 270
            color = BLUE

        self.connect_signals()

    def clear_scene(self):
        self.temp_del_items = self.scene.items()
        for item in self.temp_del_items:
            self.scene.removeItem(item)
#            del item

    def set_matching_commits_mode(self, bool):
        print "setting matching"
        self.matching_commits = bool
        if bool:
            self.hints.setup_display(step=1)
            self.hints.update()
        else:
            self.commit_item_finished_hovering()

    def connect_signals(self):
        self.connect(self.filter, SIGNAL("setMatchingMode(bool)"),
                     self.set_matching_commits_mode)

        for commit_item in self.commit_items:
            self.connect(commit_item,
                         SIGNAL("hoveringOverCommitItem(QString*)"),
                         self.commit_item_hovered)
            self.connect(commit_item,
                         SIGNAL("finishedHovering(void)"),
                         self.commit_item_finished_hovering)
            self.connect(commit_item,
                         SIGNAL("commitItemInserted(QString*, QString*)"),
                         self.insert_commit)

    def insert_commit(self, name, branch):
        self.clear_scene()
        self.commits[str(branch)].append(name)
        print self.commits
        self.populate()

    def commit_item_hovered(self, commit_name):
        if self.matching_commits:
            self.hints.setup_display(step=2)
            self.hints.update()
            for commit_item in self.commit_items:
                if commit_item.get_name() != commit_name:
                    commit_item.gray(True)

    def commit_item_finished_hovering(self):
        for commit_item in self.commit_items:
            commit_item.gray(False)
Example #14
0
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!"))
Example #15
0
class DualImageView(QGraphicsView):
    VERTICAL = 0
    HORIZONTAL = 1
    IMAGE_A = 0
    IMAGE_B = 1

    # no argument signal
    images_changed = pyqtSignal()
    annotations_changed = pyqtSignal()
    annotation_selected = pyqtSignal(int)
    no_selection = pyqtSignal()

    # click/point signals
    image_a_click = pyqtSignal(int,int)
    image_b_click = pyqtSignal(int,int)    

    # keyboard
    key_event = pyqtSignal(int)

    def __init__(self, main_win):
        super(QGraphicsView,self).__init__(main_win)
        self.parent_ = main_win
        self.main_win_ = main_win
        self.setInteractive(True)

        self.setStyleSheet("QGraphicsView { border: none; }")
        
        self.scene_ = QGraphicsScene(0,0,0,0,self.parent_)
        self.image_item_ = self.scene_.addPixmap(QPixmap())
        self.image_item_.setPos(0,0)
        #self.ann_group_ = QGraphicsItemGroup()
        #self.ann_group_.setPos(0,0)
        #self.scene_.addItem(self.ann_group_)
        self.setScene(self.scene_)
        self.scene_.selectionChanged.connect(self.on_selection_changed)
        
        # TODO: handle orientation
        self.orientation_ = DualImageView.VERTICAL
        self.images_ = [None, None]
        self.composite_ = None
        self.annotations_ = []
        self.dim_ = 0
        self.offset_ = np.array([0,0])
        self.cancel_click_ = False
        
        self.images_changed.connect(self.on_images_changed)
        self.annotations_changed.connect(self.on_annotations_changed)

    def on_selection_changed(self):
        log.debug("on_selection_changed")
        selected = self.scene_.selectedItems()
        if len(selected) > 0:
            self.cancel_click_ = True
            selected = self.scene_.selectedItems()[0]
            idx = -1
            for a in self.annotations_:
                idx += 1
                if a.item == selected:
                    log.debug(" emitting selection {0}".format(idx))
                    self.annotation_selected.emit(idx)
        else:
            self.no_selection.emit()

    @property
    def image_b_offset(self):
        return np.array([0,self.dim_],dtype=np.int32)

    def point_in_image(self, p):
        if p[1] < self.dim_:
            return 0
        else:
            return 1

    def point_to_image(self, which, p):
        if which == DualImageView.IMAGE_B:
            return p - self.image_b_offset
        return p

    def image_to_view(self, which, p):
        if which == DualImageView.IMAGE_B:
            return p + self.image_b_offset
        return p

    def on_images_changed(self):
        imga = self.images_[0]
        imgb = self.images_[1]
        width = max(imga.shape[1],imgb.shape[1])
        heighta = imga.shape[0]
        heightb = imgb.shape[0]
        height = heighta + heightb
        self.dim_ = heighta
        self.offset_ = np.array([0,heighta])
        # this assumes rgb images :-(
        comp = np.empty((height,width,imga.shape[2]),dtype=imga.dtype)
        comp[0:heighta,:imga.shape[1],:] = imga
        comp[heighta:(heighta+heightb),:imgb.shape[1],:] = imgb
        self.composite_ = comp
        qimg = qn.array2qimage(self.composite_)
        pix = QPixmap.fromImage(qimg)
        self.image_item_.setPixmap(pix)
        self.scene_.setSceneRect(0,0, width, height)
        self.repaint()
        
    def on_annotations_changed(self):
        #log.debug("on_annotations_changed")
        # self.scene_.removeItem(self.ann_group_)
        # self.ann_group_ = QGraphicsItemGroup()
        # self.ann_group_.setHandlesChildEvents(False)
        # self.ann_group_.setPos(0,0)
        # for a in self.annotations_:
        #     log.debug(" adding item")
        #     self.ann_group_.addToGroup(a.get_item())
        # self.scene_.addItem(self.ann_group_)
        self.repaint()

    def transform_raw_pt(self, ev):
        pt = self.mapToScene(ev.x(), ev.y())
        return np.array([int(pt.x()), int(pt.y())], dtype=np.int32)

    def clear(self):
        self.images_ = [None,None]
        self.composite_ = None
        self.annotations_ = None
        self.images_changed.emit()
        self.annotations_changed.emit()
        
    def set_images(self, img_pair):
        self.images_ = img_pair
        self.images_changed.emit()

    # @property
    # def annotations(self):
    #     return self.annotations_
    
    # @annotations.setter
    # def annotations(self, anns):
    #     self.annotations_ = anns
    #     self.annotations_changed.emit()

    # def set_annotations(self, anns):
    #     self.annotations_ = anns
    #     self.annotations_changed.emit()

    def paintEvent(self, ev):
        painter = QPainter(self.viewport())
        painter.fillRect(0,0,self.viewport().width(),self.viewport().height(), QColor(0,0,0))
        painter.end()
        QGraphicsView.paintEvent(self, ev)

    def annotation(self, idx):
        return self.annotations_[idx]

    def clear_annotations(self):
        for a in self.annotations_:
            self.scene_.removeItem(a.item)
        self.annotations_ = []
        self.annotations_changed.emit()

    def add_annotation(self, ann):
        ann.changed.connect(self.on_annotations_changed)
        self.annotations_.append(ann)
        self.scene_.addItem(ann.item)
        self.annotations_changed.emit()
        return len(self.annotations_) - 1
        
    def remove_last_annotation(self):
        self.scene_.removeItem(self.annotations_[-1].item)
        del self.annotations_[-1]
        self.annotations_changed.emit()

    def remove_annotation(self, idx):
        self.scene_.removeItem(self.annotations_[idx].item)
        del self.annotations_[idx]
        self.annotations_changed.emit()

    def mousePressEvent(self, ev):
        super(DualImageView,self).mousePressEvent(ev)
        if self.cancel_click_:
            return
        log.debug("mouse pressed: " + str(ev))
        self.img_local_pt = self.transform_raw_pt(ev)

    def mouseReleaseEvent(self, ev):
        super(DualImageView,self).mouseReleaseEvent(ev)
        if self.cancel_click_:
            self.cancel_click_ = False
            return
        log.debug("mouse released: " + str(ev))
        rel_pt = self.transform_raw_pt(ev)
        delta = rel_pt - self.img_local_pt
        if abs(delta[0]) < 3 and abs(delta[1] < 3):
            # it was a successful click
            self.mouseClicked(self.img_local_pt)
        else: 
            # recognize this as a rectangle drag
            self.mouseDragged(self.img_local_pt, delta)

    def mouseDragged(self, pt, delta):
        log.debug("mouse dragged: {0}, {1}".format(pt,delta))

    def mouseClicked(self, pt):
        log.debug("mouse clicked: {0}".format(pt))
        if pt[1] < self.dim_:
            self.image_a_click.emit(pt[0],pt[1])
        else:
            self.image_b_click.emit(pt[0],pt[1] - self.dim_)

    # handle the keyboard events here!
    def keyPressEvent(self, ev):
        pass

    def keyReleaseEvent(self, ev):
        k = ev.key()
        self.key_event.emit(k)
class ImagesPreviewer(foundations.ui.common.QWidgetFactory(uiFile=UI_FILE)):
	"""
	| This class provides the Application images previewer.
	| It defines methods to navigate through the list of given images ( List of images paths ),
		zoom in / out and fit the displayed image, etc...
	"""

	def __init__(self, parent, paths=None, *args, **kwargs):
		"""
		This method initializes the class.

		:param parent: Object parent. ( QObject )
		:param paths: Images paths. ( Tuple / List )
		:param \*args: Arguments. ( \* )
		:param \*\*kwargs: Keywords arguments. ( \*\* )
		"""

		LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))

		super(ImagesPreviewer, self).__init__(parent, *args, **kwargs)

		# --- Setting class attributes. ---
		self.__container = parent
		self.__paths = None
		self.paths = paths

		self.__uiResourcesDirectory = "resources"
		self.__uiResourcesDirectory = os.path.join(os.path.dirname(__file__), self.__uiResourcesDirectory)
		self.__uiPreviousImage = "Previous.png"
		self.__uiNextImage = "Next.png"
		self.__uiZoomOutImage = "Zoom_Out.png"
		self.__uiZoomInImage = "Zoom_In.png"

		# Ensure the ui object is destroyed on close to avoid memory leaks.
		self.setAttribute(Qt.WA_DeleteOnClose)

		self.__graphicsSceneBackgroundColor = QColor(32, 32, 32)
		self.__minimumZoomFactor = 0.05
		self.__maximumZoomFactor = 25
		self.__displayGraphicsItemMargin = 32
		self.__graphicsSceneWidth = QApplication.desktop().screenGeometry(
								QApplication.desktop().primaryScreen()).width() * (1 / self.__minimumZoomFactor * 1.75)
		self.__graphicsSceneHeight = QApplication.desktop().screenGeometry(
								QApplication.desktop().primaryScreen()).height() * (1 / self.__minimumZoomFactor * 1.75)
		self.__wheelZoomFactor = 350.0
		self.__keyZoomFactor = 1.20

		self.__graphicsView = None
		self.__graphicsScene = None
		self.__displayGraphicsItem = None

		ImagesPreviewer.__initializeUi(self)

		self.loadImage()

	#******************************************************************************************************************
	#***	Attributes properties.
	#******************************************************************************************************************
	@property
	def container(self):
		"""
		This method is the property for **self.__container** attribute.

		:return: self.__container. ( QObject )
		"""

		return self.__container

	@container.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def container(self, value):
		"""
		This method is the setter method for **self.__container** attribute.

		:param value: Attribute value. ( QObject )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "container"))

	@container.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def container(self):
		"""
		This method is the deleter method for **self.__container** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "container"))

	@property
	def paths(self):
		"""
		This method is the property for **self.__paths** attribute.

		:return: self.__paths. ( Tuple / List )
		"""

		return self.__paths

	@paths.setter
	@foundations.exceptions.handleExceptions(AssertionError)
	def paths(self, value):
		"""
		This method is the setter method for **self.__paths** attribute.

		:param value: Attribute value. ( Tuple / List )
		"""

		if value is not None:
			assert type(value) in (tuple, list), "'{0}' attribute: '{1}' type is not 'tuple' or 'list'!".format("paths", value)
			for element in value:
				assert type(element) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format(
				"paths", element)
		self.__paths = value

	@paths.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def paths(self):
		"""
		This method is the deleter method for **self.__paths** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "paths"))

	@property
	def uiResourcesDirectory(self):
		"""
		This method is the property for **self.__uiResourcesDirectory** attribute.

		:return: self.__uiResourcesDirectory. ( String )
		"""

		return self.__uiResourcesDirectory

	@uiResourcesDirectory.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiResourcesDirectory(self, value):
		"""
		This method is the setter method for **self.__uiResourcesDirectory** attribute.

		:param value: Attribute value. ( String )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "uiResourcesDirectory"))

	@uiResourcesDirectory.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiResourcesDirectory(self):
		"""
		This method is the deleter method for **self.__uiResourcesDirectory** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiResourcesDirectory"))

	@property
	def uiPreviousImage(self):
		"""
		This method is the property for **self.__uiPreviousImage** attribute.

		:return: self.__uiPreviousImage. ( String )
		"""

		return self.__uiPreviousImage

	@uiPreviousImage.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiPreviousImage(self, value):
		"""
		This method is the setter method for **self.__uiPreviousImage** attribute.

		:param value: Attribute value. ( String )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "uiPreviousImage"))

	@uiPreviousImage.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiPreviousImage(self):
		"""
		This method is the deleter method for **self.__uiPreviousImage** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiPreviousImage"))

	@property
	def uiNextImage(self):
		"""
		This method is the property for **self.__uiNextImage** attribute.

		:return: self.__uiNextImage. ( String )
		"""

		return self.__uiNextImage

	@uiNextImage.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiNextImage(self, value):
		"""
		This method is the setter method for **self.__uiNextImage** attribute.

		:param value: Attribute value. ( String )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "uiNextImage"))

	@uiNextImage.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiNextImage(self):
		"""
		This method is the deleter method for **self.__uiNextImage** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiNextImage"))

	@property
	def uiZoomOutImage(self):
		"""
		This method is the property for **self.__uiZoomOutImage** attribute.

		:return: self.__uiZoomOutImage. ( String )
		"""

		return self.__uiZoomOutImage

	@uiZoomOutImage.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiZoomOutImage(self, value):
		"""
		This method is the setter method for **self.__uiZoomOutImage** attribute.

		:param value: Attribute value. ( String )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "uiZoomOutImage"))

	@uiZoomOutImage.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiZoomOutImage(self):
		"""
		This method is the deleter method for **self.__uiZoomOutImage** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiZoomOutImage"))

	@property
	def uiZoomInImage(self):
		"""
		This method is the property for **self.__uiZoomInImage** attribute.

		:return: self.__uiZoomInImage. ( String )
		"""

		return self.__uiZoomInImage

	@uiZoomInImage.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiZoomInImage(self, value):
		"""
		This method is the setter method for **self.__uiZoomInImage** attribute.

		:param value: Attribute value. ( String )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "uiZoomInImage"))

	@uiZoomInImage.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def uiZoomInImage(self):
		"""
		This method is the deleter method for **self.__uiZoomInImage** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "uiZoomInImage"))

	@property
	def graphicsSceneBackgroundColor(self):
		"""
		This method is the property for **self.__graphicsSceneBackgroundColor** attribute.

		:return: self.__graphicsSceneBackgroundColor. ( QColors )
		"""

		return self.__graphicsSceneBackgroundColor

	@graphicsSceneBackgroundColor.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneBackgroundColor(self, value):
		"""
		This method is the setter method for **self.__graphicsSceneBackgroundColor** attribute.

		:param value: Attribute value. ( QColors )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "graphicsSceneBackgroundColor"))

	@graphicsSceneBackgroundColor.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneBackgroundColor(self):
		"""
		This method is the deleter method for **self.__graphicsSceneBackgroundColor** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "graphicsSceneBackgroundColor"))

	@property
	def graphicsSceneWidth(self):
		"""
		This method is the property for **self.__graphicsSceneWidth** attribute.

		:return: self.__graphicsSceneWidth. ( Integer )
		"""

		return self.__graphicsSceneWidth

	@graphicsSceneWidth.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneWidth(self, value):
		"""
		This method is the setter method for **self.__graphicsSceneWidth** attribute.

		:param value: Attribute value. ( Integer )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "graphicsSceneWidth"))

	@graphicsSceneWidth.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneWidth(self):
		"""
		This method is the deleter method for **self.__graphicsSceneWidth** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "graphicsSceneWidth"))

	@property
	def graphicsSceneHeight(self):
		"""
		This method is the property for **self.__graphicsSceneHeight** attribute.

		:return: self.__graphicsSceneHeight. ( Object )
		"""

		return self.__graphicsSceneHeight

	@graphicsSceneHeight.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneHeight(self, value):
		"""
		This method is the setter method for **self.__graphicsSceneHeight** attribute.

		:param value: Attribute value. ( Object )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "graphicsSceneHeight"))

	@graphicsSceneHeight.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsSceneHeight(self):
		"""
		This method is the deleter method for **self.__graphicsSceneHeight** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "graphicsSceneHeight"))

	@property
	def minimumZoomFactor(self):
		"""
		This method is the property for **self.__minimumZoomFactor** attribute.

		:return: self.__minimumZoomFactor. ( Float )
		"""

		return self.__minimumZoomFactor

	@minimumZoomFactor.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def minimumZoomFactor(self, value):
		"""
		This method is the setter method for **self.__minimumZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "minimumZoomFactor"))

	@minimumZoomFactor.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def minimumZoomFactor(self):
		"""
		This method is the deleter method for **self.__minimumZoomFactor** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "minimumZoomFactor"))

	@property
	def maximumZoomFactor(self):
		"""
		This method is the property for **self.__maximumZoomFactor** attribute.

		:return: self.__maximumZoomFactor. ( Float )
		"""

		return self.__maximumZoomFactor

	@maximumZoomFactor.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def maximumZoomFactor(self, value):
		"""
		This method is the setter method for **self.__maximumZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "maximumZoomFactor"))

	@maximumZoomFactor.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def maximumZoomFactor(self):
		"""
		This method is the deleter method for **self.__maximumZoomFactor** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "maximumZoomFactor"))

	@property
	def wheelZoomFactor(self):
		"""
		This method is the property for **self.__wheelZoomFactor** attribute.

		:return: self.__wheelZoomFactor. ( Float )
		"""

		return self.__wheelZoomFactor

	@wheelZoomFactor.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def wheelZoomFactor(self, value):
		"""
		This method is the setter method for **self.__wheelZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "wheelZoomFactor"))

	@wheelZoomFactor.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def wheelZoomFactor(self):
		"""
		This method is the deleter method for **self.__wheelZoomFactor** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "wheelZoomFactor"))

	@property
	def keyZoomFactor(self):
		"""
		This method is the property for **self.__keyZoomFactor** attribute.

		:return: self.__keyZoomFactor. ( Float )
		"""

		return self.__keyZoomFactor

	@keyZoomFactor.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def keyZoomFactor(self, value):
		"""
		This method is the setter method for **self.__keyZoomFactor** attribute.

		:param value: Attribute value. ( Float )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "keyZoomFactor"))

	@keyZoomFactor.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def keyZoomFactor(self):
		"""
		This method is the deleter method for **self.__keyZoomFactor** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "keyZoomFactor"))

	@property
	def graphicsView(self):
		"""
		This method is the property for **self.__graphicsView** attribute.

		:return: self.__graphicsView. ( QGraphicsView )
		"""

		return self.__graphicsView

	@graphicsView.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsView(self, value):
		"""
		This method is the setter method for **self.__graphicsView** attribute.

		:param value: Attribute value. ( QGraphicsView )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "graphicsView"))

	@graphicsView.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsView(self):
		"""
		This method is the deleter method for **self.__graphicsView** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "graphicsView"))

	@property
	def graphicsScene(self):
		"""
		This method is the property for **self.__graphicsScene** attribute.

		:return: self.__graphicsScene. ( QGraphicsScene )
		"""

		return self.__graphicsScene

	@graphicsScene.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsScene(self, value):
		"""
		This method is the setter method for **self.__graphicsScene** attribute.

		:param value: Attribute value. ( QGraphicsScene )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "graphicsScene"))

	@graphicsScene.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def graphicsScene(self):
		"""
		This method is the deleter method for **self.__graphicsScene** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "graphicsScene"))

	@property
	def displayGraphicsItem(self):
		"""
		This method is the property for **self.__displayGraphicsItem** attribute.

		:return: self.__displayGraphicsItem. ( QGraphicsItem )
		"""

		return self.__displayGraphicsItem

	@displayGraphicsItem.setter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def displayGraphicsItem(self, value):
		"""
		This method is the setter method for **self.__displayGraphicsItem** attribute.

		:param value: Attribute value. ( QGraphicsItem )
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "displayGraphicsItem"))

	@displayGraphicsItem.deleter
	@foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
	def displayGraphicsItem(self):
		"""
		This method is the deleter method for **self.__displayGraphicsItem** attribute.
		"""

		raise foundations.exceptions.ProgrammingError(
		"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "displayGraphicsItem"))

	#******************************************************************************************************************
	#***	Class methods.
	#******************************************************************************************************************
	def show(self):
		"""
		This method reimplements the :meth:`QWidget.show` method.
		"""

		super(ImagesPreviewer, self).show()

		foundations.ui.common.centerWidgetOnScreen(self)

	def closeEvent(self, event):
		"""
		This method reimplements the :meth:`QWidget.closeEvent` method.

		:param event: QEvent ( QEvent )
		"""

		LOGGER.debug("> Removing '{0}' from Images Previewers list.".format(self))
		self.__container.imagesPreviewers.remove(self)

		event.accept()

	def wheelEvent(self, event):
		"""
		This method reimplements the :meth:`QWidget.wheelEvent` method.

		:param event: QEvent ( QEvent )
		"""

		self.scaleView(pow(1.5, event.delta() / self.__wheelZoomFactor))

	def keyPressEvent(self, event):
		"""
		This method reimplements the :meth:`QWidget.keyPressEvent` method.

		:param event: QEvent ( QEvent )
		"""

		key = event.key()
		if key == Qt.Key_Plus:
			self.scaleView(self.__keyZoomFactor)
		elif key == Qt.Key_Minus:
			self.scaleView(1 / self.__keyZoomFactor)
		else:
			super(ImagesPreviewer, self).keyPressEvent(event)

	def __initializeUi(self):
		"""
		This method initializes the Widget ui.
		"""

		LOGGER.debug("> Initializing '{0}' ui.".format(self.__class__.__name__))

		self.Previous_Image_pushButton.setIcon(QIcon(os.path.join(self.__uiResourcesDirectory, self.__uiPreviousImage)))
		self.Next_Image_pushButton.setIcon(QIcon(os.path.join(self.__uiResourcesDirectory, self.__uiNextImage)))
		self.Zoom_In_pushButton.setIcon(QIcon(os.path.join(self.__uiResourcesDirectory, self.__uiZoomInImage)))
		self.Zoom_Out_pushButton.setIcon(QIcon(os.path.join(self.__uiResourcesDirectory, self.__uiZoomOutImage)))
		len(self.__paths) <= 1 and self.Navigation_frame.hide()

		LOGGER.debug("> Initializing graphics View.")
		self.__graphicsView = QGraphicsView()
		self.__graphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
		self.__graphicsView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
		self.__graphicsView.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
		self.__graphicsView.setDragMode(QGraphicsView.ScrollHandDrag)
		# Reimplementing QGraphicsView wheelEvent method.
		self.__graphicsView.wheelEvent = self.wheelEvent

		LOGGER.debug("> Initializing graphics scene.")
		self.__graphicsScene = QGraphicsScene(self.__graphicsView)
		self.__graphicsScene.setItemIndexMethod(QGraphicsScene.NoIndex)
		self.__graphicsScene.setSceneRect(-(float(self.__graphicsSceneWidth)) / 2,
										- (float(self.__graphicsSceneHeight)) / 2,
										float(self.__graphicsSceneWidth),
										float(self.__graphicsSceneHeight))

		self.__graphicsView.setScene(self.__graphicsScene)
		self.__graphicsView.setBackgroundBrush(QBrush(self.__graphicsSceneBackgroundColor))

		self.Images_Previewer_frame_gridLayout.addWidget(self.__graphicsView)

		# Signals / Slots.
		self.__container.engine.imagesCaches.QImage.contentAdded.connect(self.__engine_imagesCaches_QImage__contentAdded)
		self.Previous_Image_pushButton.clicked.connect(self.__Previous_Image_pushButton__clicked)
		self.Next_Image_pushButton.clicked.connect(self.__Next_Image_pushButton__clicked)
		self.Zoom_Out_pushButton.clicked.connect(self.__Zoom_Out_pushButton__clicked)
		self.Zoom_In_pushButton.clicked.connect(self.__Zoom_In_pushButton__clicked)
		self.Zoom_Fit_pushButton.clicked.connect(self.__Zoom_Fit_pushButton__clicked)

	def __Images_Informations_label_setUi(self):
		"""
		This method sets the **Images_Informations_label** Widget ui.
		"""

		if not self.__displayGraphicsItem:
			return

		image = self.__displayGraphicsItem.image
		self.Images_Informations_label.setText("{0} - {1}x{2} px - {3} bit".format(os.path.basename(image.data.path),
		 																		image.data.width,
																				image.data.height,
																				image.data.bpp / 4))

	def __engine_imagesCaches_QImage__contentAdded(self, content):
		"""
		This method is triggered by the Application **QImage** images cache when content has been added.

		:param content: Cache added content. ( List )
		"""

		if not self.__paths:
			return

		path = foundations.common.getFirstItem(content)
		if not path in self.__paths:
			return

		image = self.__container.engine.imagesCaches.QImage.getContent(path)
		self.__setDisplayGraphicsItem(image)

	def __Previous_Image_pushButton__clicked(self, checked):
		"""
		This method is triggered when **Previous_Image_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

		self.loopThroughImages(True)

	def __Next_Image_pushButton__clicked(self, checked):
		"""
		This method is triggered when **Next_Image_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

		self.loopThroughImages()

	def __Zoom_In_pushButton__clicked(self, checked):
		"""
		This method is triggered when **Zoom_In_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

		self.scaleView(self.__keyZoomFactor)

	def __Zoom_Out_pushButton__clicked(self, checked):
		"""
		This method is triggered when **Zoom_Out_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

		self.scaleView(1 / self.__keyZoomFactor)

	def __Zoom_Fit_pushButton__clicked(self, checked):
		"""
		This method is triggered when **Zoom_Fit_pushButton** Widget is clicked.

		:param checked: Checked state. ( Boolean )
		"""

		self.fitImage()

	def __clearGraphicsScene(self):
		"""
		This method clears the View.
		"""

		for graphicsItem in self.__graphicsScene.items():
			self.__graphicsScene.removeItem(graphicsItem)

	def __setDisplayGraphicsItem(self, image):
		"""
		This method sets the View using given image.

		:param image: Image to display. ( Qimage )
		"""

		self.__clearGraphicsScene()

		LOGGER.debug("> Initializing graphics item.")
		self.__displayGraphicsItem = Image_QGraphicsItem(image=image)
		self.__graphicsScene.addItem(self.__displayGraphicsItem)

		self.__Images_Informations_label_setUi()

	def loadImage(self, index=0):
		"""
		This method loads the display image in the View.

		:param index: Index to load. ( Integer )
		:return: Method success. ( Boolean )
		"""

		if not self.__paths:
			return False

		image = sibl_gui.ui.common.getImage(self.__paths[index])
		self.__setDisplayGraphicsItem(image)

		return True

	def scaleView(self, scaleFactor):
		"""
		This method scales the Previewer view.

		:param scaleFactor: Float ( Float )
		:return: Method success. ( Boolean )
		"""

		graphicsView = self.findChild(QGraphicsView)
		factor = graphicsView.matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width()
		if factor < self.__minimumZoomFactor or factor > self.__maximumZoomFactor:
			return False

		graphicsView.scale(scaleFactor, scaleFactor)
		return True

	def fitWindow(self):
		"""
		This method fits the View window.
		
		:return: Method success. ( Boolean )
		"""

		if not self.__displayGraphicsItem:
			return False

		desktopWidth = QApplication.desktop().screenGeometry(QApplication.desktop().primaryScreen()).width()
		desktopHeight = QApplication.desktop().screenGeometry(QApplication.desktop().primaryScreen()).height()
		width = min(desktopWidth * 0.80, self.__displayGraphicsItem.width)
		height = min(desktopHeight * 0.80, self.__displayGraphicsItem.height)
		self.resize(width, height)

		foundations.ui.common.centerWidgetOnScreen(self)

		return True

	def fitImage(self):
		"""
		This method fits the image to the View.
		
		:return: Method success. ( Boolean )
		"""

		if not self.__displayGraphicsItem:
			return False

		self.__graphicsView.fitInView(
		QRectF(-(self.__displayGraphicsItem.width / 2) - (self.__displayGraphicsItemMargin / 2),
				- (self.__displayGraphicsItem.height / 2) - (self.__displayGraphicsItemMargin / 2),
				self.__displayGraphicsItem.width + self.__displayGraphicsItemMargin,
				self.__displayGraphicsItem.height + self.__displayGraphicsItemMargin),
				Qt.KeepAspectRatio)
		return True

	def loopThroughImages(self, backward=False):
		"""
		This method loops through View images.

		:param backward: Looping backward. ( Boolean )
		:return: Method success. ( Boolean )
		"""

		index = self.__paths.index(self.__displayGraphicsItem.image.data.path)
		index += not backward and 1 or -1
		if index < 0:
			index = len(self.__paths) - 1
		elif index > len(self.__paths) - 1:
			index = 0
		self.loadImage(index)
		return True
Example #17
0
class PlotPreview(QDialog):
    def __init__(self, thread, parent):
        QDialog.__init__(self, parent)
        self.ui = Ui_PlotPreview()
        self.ui.setupUi(self)
        self.parent = parent
        icon = QIcon()
        icon.addPixmap(QPixmap(":/icons/reload.png"), QIcon.Normal, QIcon.Off)
        self.update_btn = QPushButton(icon, "Update")
        self.ui.buttonBox.addButton(self.update_btn, QDialogButtonBox.ApplyRole)
        self.update_btn.clicked.connect(self.render_image)
        self._pix = None
        self._image_list = None
        self._thread = thread
        self.scene = QGraphicsScene()
        self.scene.setSceneRect(0,0,1,1)
        self.ui.imageView.setScene(self.scene)
        self.pix_item = None
        self.ui.imageView.setEnabled(False)
        self.ui.imageView.setInteractive(False)
        self.ui.imageView.setDragMode(QGraphicsView.ScrollHandDrag)
        self.pic_w = None
        self.pic_c = None
        self.show_pic_w = None
        self.show_pic_c = None
        timer = QTimer(self)
        timer.setSingleShot(True)
        timer.setInterval(500)
        self.timer = timer
        timer.timeout.connect(self.render_image)
        if parameters.instance.use_OpenGL:
            self.ui.imageView.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))

    def __del__(self):
        self.ui.imageView.setScene(None)
        cleanQObject(self)

    @property
    def thread(self):
        '''Thread object drawing the img'''
        return self._thread

    @thread.setter
    def thread(self, value):
        if self._thread != value:
            self._thread = value

    @property
    def pix(self):
        '''Image to be previewed.

        :returntype: `QImage`'''
        return self._pix

    @pix.setter
    def pix(self, pix):
        self.ui.imageView.setEnabled(True)
        self._pix = pix
        if self.pix_item is not None:
            self.scene.removeItem(self.pix_item)
        self.pix_item = self.scene.addPixmap(QPixmap.fromImage(pix))
        self.scene.setSceneRect(QRectF(self.pix.rect()))
        if self.show_pic_w:
            self.show_pic_w.close()
            self.show_pic_w = None
        if self.pic_w:
            self.show_pic_w = QLabel(self, Qt.Window)
            #self.show_pic_w.setAttribute(Qt.WA_DeleteOnClose)
            self.show_pic_w.setPicture(self.pic_w)
            self.show_pic_w.show()
            self.show_pic_w.raise_()
        if self.show_pic_c:
            self.show_pic_c.close()
            self.show_pic_c = None
        if self.pic_c:
            self.show_pic_c = QLabel(self, Qt.Window)
            #self.show_pic_c.setAttribute(Qt.WA_DeleteOnClose)
            self.show_pic_c.setPicture(self.pic_c)
            self.show_pic_c.show()
            self.show_pic_c.raise_()
        log_debug("Received image")

    @property
    def image_list(self):
        '''List of images to paint'''
        return tuple(self._image_list)

    @image_list.setter
    def image_list(self, value):
        value = list(value)
        if self._image_list != value:
            self._image_list = value
            self.ui.imageList.clear()
            for img in value:
                self.ui.imageList.addItem(img)
            self.ui.autoUpdate.setEnabled(True)
            self.ui.zoomIn.setEnabled(True)
            self.ui.zoomOut.setEnabled(True)
            self.ui.zoom1.setEnabled(True)
            self.ui.zoomFit.setEnabled(True)
            self.ui.imageList.setEnabled(True)

    @pyqtSignature("")
    def on_zoomIn_clicked(self):
        self.ui.imageView.scale(2,2)

    @pyqtSignature("")
    def on_zoomOut_clicked(self):
        self.ui.imageView.scale(.5,.5)

    @pyqtSignature("")
    def on_zoom1_clicked(self):
        self.ui.imageView.setTransform(QTransform())

    @pyqtSignature("")
    def on_zoomFit_clicked(self):
        self.ui.imageView.fitInView(QRectF(self.pix.rect()), Qt.KeepAspectRatio)

    @pyqtSignature("")
    def request_render_image(self):
        if self.isVisible():
            if parameters.instance.use_thread:
                self.timer.start()
            else:
                self.render_image()

    @pyqtSignature("")
    def render_image(self):
        if self.isVisible():
            i = self.ui.imageList.currentIndex()
            log_debug("Launch computing for image %d" % i)
            if self.thread.render_single(i) is None:
                QMessageBox.information(self, "Failed rendering image", "The renderer is busy and could not render the image.\nTry again later")
            else:
                self.ui.imageView.setEnabled(False)

    @pyqtSignature("int")
    def on_imageList_currentIndexChanged(self, value):
        self.render_image()

    @pyqtSignature("bool")
    def on_autoUpdate_toggled(self, value):
        self.parent.auto_update = value
        if value:
            self.request_render_image()

    def showEvent(self, event):
        self.render_image()

    def reject(self):
        self.close()

    def closeEvent(self, event):
        self.parent.preview_button.setChecked(False)
        if self.show_pic_w is not None:
            self.show_pic_w.close()
            self.show_pic_w = None
        if self.show_pic_c is not None:
            self.show_pic_c.close()
            self.show_pic_c = None
Example #18
0
class Level(QFrame):
    def __init__(self, parent):
        QFrame.__init__(self,parent)

        self.filename = QString()
        self.copiedItem = QByteArray()
        self.pasteOffset = 5
        self.prevPoint = QPoint()
        self.addOffset = 5
        
        self.screenSize = (320, 240)
        self.bgColor = QColor(244,244,244)
        '''0.Portrait 1.Landscape'''
        self.orientation = 0
        self.currentItem = None
        

        self.printer = QPrinter(QPrinter.HighResolution)
        self.printer.setPageSize(QPrinter.Letter)


        '''1.Header'''
        self.levelBar = LevelBar(self)
        
        '''3.Tiler'''
        self.tiler = TileMapGrid(self)#Tiler(self)
        self.tiler.setMinimumHeight(100)
        #self.tiler.currentChanged.connect(self.closeDesigner)
        #self.tiler.setTabsClosable(True)
        #self.tiler.setTabShape(0)
        #self.tiler.hide()
        #self.levelLayout.addWidget(self.levelBar) 
        
        '''2.view'''
        viewLayoutWidget = QFrame()
        viewLayoutWidget.setFrameShape(QFrame.StyledPanel)
        viewLayout = QHBoxLayout(viewLayoutWidget)
        #viewLayout.setMargin(10)
        self.view = LevelView(viewLayoutWidget)
        '''scene'''
        self.scene = QGraphicsScene(self)
        #self.scene.selectionChanged.connect(self.setConnect)
        #self.view.setStyleSheet("border: 1px solid red;")
        
        self.setBackgroundColor(self.bgColor)
        self.setScreenSize(self.screenSize)
        
        self.view.setScene(self.scene)
        self.view.setAlignment(Qt.AlignCenter)
        self.scroll_off = 1
        self.setScrollBar()
        
        viewLayout.setMargin(0)
        viewLayout.addWidget(self.view)

        self.wrapped = [] # Needed to keep wrappers alive
        layout = QVBoxLayout(self)
        layout.addWidget(self.levelBar)
        layout.addWidget(viewLayoutWidget)
        layout.addWidget(self.tiler)
        layout.setMargin(0)
        self.setLayout(layout)
        
    def setScrollBar(self):
        if(self.scroll_off):
            self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.scroll_off = 0
        else:
            self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
            self.scroll_off = 1
        
    def setBackgroundColor(self,color):
        self.bgColor = color
        self.scene.setBackgroundBrush(QBrush(color))
        
    def setScreenSize(self,size):
        self.screenSize = size
        self.setOrientation(self.orientation)
        
    def setOrientation(self,idx):
        self.orientation = idx
        if(idx == 0):
            self.view.setMaximumSize(self.screenSize[1], self.screenSize[0])
            self.scene.setSceneRect(0, 0, self.screenSize[1], self.screenSize[0])
        else:
            self.view.setMaximumSize(self.screenSize[0], self.screenSize[1])
            self.scene.setSceneRect(0, 0, self.screenSize[0], self.screenSize[1])
        
    def offerSave(self):
        if (Dirty and QMessageBox.question(self,
                            "Designer - Unsaved Changes",
                            "Save unsaved changes?",
                            QMessageBox.Yes|QMessageBox.No) == 
           QMessageBox.Yes):
            self.save()


    def position(self):
        point = self.mapFromGlobal(QCursor.pos())
        if not self.view.geometry().contains(point):
            coord = random.randint(36, 144)
            point = QPoint(coord, coord)
        else:
            if point == self.prevPoint:
                point += QPoint(self.addOffset, self.addOffset)
                self.addOffset += 5
            else:
                self.addOffset = 5
                self.prevPoint = point
        return self.view.mapToScene(point)
    
    def selectedItem(self):
        items = self.scene.selectedItems()
        if len(items) == 1:
            return items[0]
        return None
    
    def current(self,item):
        self.scene.clearSelection()
        sceneItems = self.scene.items()
        for items in sceneItems:
            if(items != item):
                item.setSelected(False)
                if(item.isConnected()):
                    self.propertyBar.disconnectText(item)
                    item.setConnected(False)
        self.currentItem = item
        self.currentItem.setConnected(True)
        self.currentItem.setSelected(True)
        self.propertyBar.connectText(self.currentItem)
        self.propertyBar.initText(self.currentItem)


    def addText(self):
        item = TextItem("SomeText", self.position())
        self.connect(item, SIGNAL("current"),self.current)
        self.connect(item, SIGNAL("copy"),self.copy)
        self.connect(item, SIGNAL("cut"),self.cut)
        self.connect(item, SIGNAL("paste"),self.paste)
        self.connect(item, SIGNAL("delete"),self.delete)
        #self.current(item)
        self.scene.addItem(item)
        

    def copy(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copiedItem.clear()
        self.pasteOffset = 5
        stream = QDataStream(self.copiedItem, QIODevice.WriteOnly)
        self.writeItemToStream(stream, item)


    def cut(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copy()
        self.scene.removeItem(item)
        del item


    def paste(self):
        if self.copiedItem.isEmpty():
            return
        stream = QDataStream(self.copiedItem, QIODevice.ReadOnly)
        item = self.readItemFromStream(stream, self.pasteOffset)
        self.pasteOffset += 5
        #self.scene.addItem(item)
        
    def delete(self):
        items = self.scene.selectedItems()
        if (len(items) and QMessageBox.question(self,
                "Designer - Delete",
                "Delete {0} item{1}?".format(len(items),
                "s" if len(items) != 1 else ""),
                QMessageBox.Yes|QMessageBox.No) ==
                QMessageBox.Yes):
            while items:
                item = items.pop()
                self.scene.removeItem(item)
                del item
                
    def readItemFromStream(self, stream, offset=0):
        type = QString()
        position = QPointF()
        matrix = QMatrix()
        stream >> type >> position >> matrix
        if offset:
            position += QPointF(offset, offset)
        if type == "Text":
            text = QString()
            font = QFont()
            stream >> text >> font
            self.scene.addItem(TextItem(text, position, font, matrix))
        elif type == "Box":
            rect = QRectF()
            stream >> rect
            style = Qt.PenStyle(stream.readInt16())
            self.scene.addItem(BoxItem(position, style, matrix))
        elif type == "Pixmap":
            pixmap = QPixmap()
            stream >> pixmap
            self.scene.addItem(self.createPixmapItem(pixmap, position, matrix))


    def writeItemToStream(self, stream, item):
        if isinstance(item, QGraphicsTextItem):
            stream << QString("Text") << item.pos() \
                   << item.matrix() << item.toPlainText() << item.font()
        elif isinstance(item, QGraphicsPixmapItem):
            stream << QString("Pixmap") << item.pos() \
                   << item.matrix() << item.pixmap()
        elif isinstance(item, BoxItem):
            stream << QString("Box") << item.pos() \
                   << item.matrix() << item.rect
            stream.writeInt16(item.style)

    def rotate(self):
        for item in self.scene.selectedItems():
            item.rotate(30)


    def print_(self):
        dialog = QPrintDialog(self.printer)
        if dialog.exec_():
            painter = QPainter(self.printer)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setRenderHint(QPainter.TextAntialiasing)
            self.scene.clearSelection()
            #self.removeBorders()
            self.scene.render(painter)
            #self.addBorders()


    def open(self):
        self.offerSave()
        path = (QFileInfo(self.filename).path()
                if not self.filename.isEmpty() else ".")
        fname = QFileDialog.getOpenFileName(self,
                "Page Designer - Open", path,
                "Page Designer Files (*.pgd)")
        if fname.isEmpty():
            return
        self.filename = fname
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.ReadOnly):
                raise IOError, unicode(fh.errorString())
            items = self.scene.items()
            while items:
                item = items.pop()
                self.scene.removeItem(item)
                del item
            self.addBorders()
            stream = QDataStream(fh)
            stream.setVersion(QDataStream.Qt_4_2)
            magic = stream.readInt32()
            if magic != MagicNumber:
                raise IOError, "not a valid .pgd file"
            fileVersion = stream.readInt16()
            if fileVersion != FileVersion:
                raise IOError, "unrecognised .pgd file version"
            while not fh.atEnd():
                self.readItemFromStream(stream)
        except IOError, e:
            QMessageBox.warning(self, "Page Designer -- Open Error",
                    "Failed to open {0}: {1}".format(self.filename, e))
        finally:
Example #19
0
class OWSieveDiagram(OWWidget):
    name = "Sieve Diagram"
    description = "A two-way contingency table providing information on the " \
                  "relation between the observed and expected frequencies " \
                  "of a combination of feature values under the assumption of independence."
    icon = "icons/SieveDiagram.svg"
    priority = 4200

    inputs = [("Data", Table, "set_data", Default),
              ("Features", AttributeList, "set_input_features")]
    outputs = [("Selection", Table)]

    graph_name = "canvas"

    want_control_area = False

    settingsHandler = DomainContextHandler()
    attrX = ContextSetting("")
    attrY = ContextSetting("")
    selection = ContextSetting(set())

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

        self.data = None
        self.input_features = None
        self.attrs = []

        self.attr_box = gui.hBox(self.mainArea)
        model = VariableListModel()
        model.wrap(self.attrs)
        self.attrXCombo = gui.comboBox(
            self.attr_box, self, value="attrX", contentsLength=12,
            callback=self.change_attr, sendSelectedValue=True, valueType=str)
        self.attrXCombo.setModel(model)
        gui.widgetLabel(self.attr_box, "\u2715").\
            setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.attrYCombo = gui.comboBox(
            self.attr_box, self, value="attrY", contentsLength=12,
            callback=self.change_attr, sendSelectedValue=True, valueType=str)
        self.attrYCombo.setModel(model)

        self.canvas = QGraphicsScene()
        self.canvasView = ViewWithPress(self.canvas, self.mainArea,
                                         handler=self.reset_selection)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        box = gui.hBox(self.mainArea)
        box.layout().addWidget(self.graphButton)
        box.layout().addWidget(self.report_button)

    def sizeHint(self):
        return QSize(450, 550)

    def set_data(self, data):
        if type(data) == SqlTable and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.closeContext()
        self.data = data
        self.areas = []
        if self.data is None:
            self.attrs[:] = []
        else:
            self.attrs[:] = [
                var for var in chain(self.data.domain,
                                     self.data.domain.metas)
                if var.is_discrete
            ]
        if self.attrs:
            self.attrX = self.attrs[0].name
            self.attrY = self.attrs[len(self.attrs) > 1].name
        else:
            self.attrX = self.attrY = None
        self.openContext(self.data)

        self.information(0, "")
        if data and any(attr.is_continuous for attr in data.domain):
            self.information(0, "Data contains continuous variables. "
                                "Discretize the data to use them.")
        self.resolve_shown_attributes()
        self.update_selection()

    def change_attr(self):
        self.selection = set()
        self.updateGraph()
        self.update_selection()

    def set_input_features(self, attrList):
        self.input_features = attrList
        self.resolve_shown_attributes()
        self.update_selection()

    def resolve_shown_attributes(self):
        self.warning(1)
        self.attr_box.setEnabled(True)
        if self.input_features:  # non-None and non-empty!
            features = [f for f in self.input_features if f in self.attrs]
            if not features:
                self.warning(1, "Features from the input signal "
                                "are not present in the data")
            else:
                old_attrs = self.attrX, self.attrY
                self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
                self.attr_box.setEnabled(False)
                if (self.attrX, self.attrY) != old_attrs:
                    self.selection = set()
        # else: do nothing; keep current features, even if input with the
        # features just changed to None
        self.updateGraph()

    def resizeEvent(self, e):
        OWWidget.resizeEvent(self,e)
        self.updateGraph()

    def showEvent(self, ev):
        OWWidget.showEvent(self, ev)
        self.updateGraph()

    def reset_selection(self):
        self.selection = set()
        self.update_selection()

    def select_area(self, area, ev):
        if ev.button() != Qt.LeftButton:
            return
        index = self.areas.index(area)
        if ev.modifiers() & Qt.ControlModifier:
            self.selection ^= {index}
        else:
            self.selection = {index}
        self.update_selection()

    def update_selection(self):
        if self.areas is None or not self.selection:
            self.send("Selection", None)
            return

        filters = []
        for i, area in enumerate(self.areas):
            if i in self.selection:
                width = 4
                val_x, val_y = area.value_pair
                filters.append(
                    filter.Values([
                        filter.FilterDiscrete(self.attrX, [val_x]),
                        filter.FilterDiscrete(self.attrY, [val_y])
                    ]))
            else:
                width = 1
            pen = area.pen()
            pen.setWidth(width)
            area.setPen(pen)
        if len(filters) == 1:
            filters = filters[0]
        else:
            filters = filter.Values(filters, conjunction=False)
        self.send("Selection", filters(self.data))

    # -----------------------------------------------------------------------
    # Everything from here on is ancient and has been changed only according
    # to what has been changed above. Some clean-up may be in order some day
    #
    def updateGraph(self, *args):
        for item in self.canvas.items():
            self.canvas.removeItem(item)
        if self.data is None or len(self.data) == 0 or \
                self.attrX is None or self.attrY is None:
            return
        data = self.data[:, [self.attrX, self.attrY]]
        valsX = []
        valsY = []
        contX = get_contingency(data, self.attrX, self.attrX)
        contY = get_contingency(data, self.attrY, self.attrY)
        # compute contingency of x and y attributes
        for entry in contX:
            sum_ = 0
            try:
                for val in entry: sum_ += val
            except: pass
            valsX.append(sum_)

        for entry in contY:
            sum_ = 0
            try:
                for val in entry: sum_ += val
            except: pass
            valsY.append(sum_)

        contXY, _ = get_conditional_distribution(
            data, [data.domain[self.attrX], data.domain[self.attrY]])
        # compute probabilities
        probs = {}
        for i in range(len(valsX)):
            valx = valsX[i]
            for j in range(len(valsY)):
                valy = valsY[j]
                try:
                    actualProb = contXY['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                    # for val in contXY['%s-%s' %(i, j)]: actualProb += val
                except:
                    actualProb = 0
                probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])] = ((data.domain[self.attrX].values[i], valx), (data.domain[self.attrY].values[j], valy), actualProb, len(data))

        #get text width of Y labels
        max_ylabel_w = 0
        for j in range(len(valsY)):
            xl = CanvasText(self.canvas, "", 0, 0, html_text= getHtmlCompatibleString(data.domain[self.attrY].values[j]), show=False)
            max_ylabel_w = max(int(xl.boundingRect().width()), max_ylabel_w)
        max_ylabel_w = min(max_ylabel_w, 200) #upper limit for label widths
        # get text width of Y attribute name
        text = CanvasText(self.canvas, data.domain[self.attrY].name, x  = 0, y = 0, bold = 1, show = 0, vertical=True)
        xOff = int(text.boundingRect().height() + max_ylabel_w)
        yOff = 55
        sqareSize = min(self.canvasView.width() - xOff - 35, self.canvasView.height() - yOff - 50)
        sqareSize = max(sqareSize, 10)
        self.canvasView.setSceneRect(0, 0, self.canvasView.width(), self.canvasView.height())

        # print graph name
        name  = "<b>P(%s, %s) &#8800; P(%s)&times;P(%s)</b>" %(self.attrX, self.attrY, self.attrX, self.attrY)
        CanvasText(self.canvas, "", xOff + sqareSize / 2, 20, Qt.AlignCenter, html_text= name)
        CanvasText(self.canvas, "N = " + str(len(data)), xOff + sqareSize / 2, 38, Qt.AlignCenter, bold = 0)

        ######################
        # compute chi-square
        chisquare = 0.0
        for i in range(len(valsX)):
            for j in range(len(valsY)):
                ((xAttr, xVal), (yAttr, yVal), actual, sum_) = probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                expected = float(xVal*yVal)/float(sum_)
                if expected == 0: continue
                pearson2 = (actual - expected)*(actual - expected) / expected
                chisquare += pearson2

        ######################
        # draw rectangles
        currX = xOff
        max_xlabel_h = 0
        normX, normY = sum(valsX), sum(valsY)
        self.areas = []
        for i in range(len(valsX)):
            if valsX[i] == 0: continue
            currY = yOff
            width = int(float(sqareSize * valsX[i])/float(normX))

            for j in range(len(valsY)-1, -1, -1):   # this way we sort y values correctly
                ((xAttr, xVal), (yAttr, yVal), actual, sum_) = probs['%s-%s' %(data.domain[self.attrX].values[i], data.domain[self.attrY].values[j])]
                if valsY[j] == 0: continue
                height = int(float(sqareSize * valsY[j])/float(normY))

                # create rectangle
                selected = len(self.areas) in self.selection
                rect = CanvasRectangle(
                    self.canvas, currX+2, currY+2, width-4, height-4, z = -10,
                    onclick=self.select_area)
                rect.value_pair = i, j
                self.areas.append(rect)
                self.addRectIndependencePearson(rect, currX+2, currY+2, width-4, height-4, (xAttr, xVal), (yAttr, yVal), actual, sum_,
                    width=1 + 3 * selected,  # Ugly! This is needed since
                    # resize redraws the graph! When this is handled by resizing
                    # just the viewer, update_selection will take care of this
                    )

                expected = float(xVal*yVal)/float(sum_)
                pearson = (actual - expected) / sqrt(expected)
                tooltipText = """<b>X Attribute: %s</b><br>Value: <b>%s</b><br>Number of instances (p(x)): <b>%d (%.2f%%)</b><hr>
                                <b>Y Attribute: %s</b><br>Value: <b>%s</b><br>Number of instances (p(y)): <b>%d (%.2f%%)</b><hr>
                                <b>Number Of Instances (Probabilities):</b><br>Expected (p(x)p(y)): <b>%.1f (%.2f%%)</b><br>Actual (p(x,y)): <b>%d (%.2f%%)</b>
                                <hr><b>Statistics:</b><br>Chi-square: <b>%.2f</b><br>Standardized Pearson residual: <b>%.2f</b>""" %(self.attrX, getHtmlCompatibleString(xAttr), xVal, 100.0*float(xVal)/float(sum_), self.attrY, getHtmlCompatibleString(yAttr), yVal, 100.0*float(yVal)/float(sum_), expected, 100.0*float(xVal*yVal)/float(sum_*sum_), actual, 100.0*float(actual)/float(sum_), chisquare, pearson )
                rect.setToolTip(tooltipText)

                currY += height
                if currX == xOff:
                    CanvasText(self.canvas, "", xOff, currY - height / 2, Qt.AlignRight | Qt.AlignVCenter, html_text= getHtmlCompatibleString(data.domain[self.attrY].values[j]))

            xl = CanvasText(self.canvas, "", currX + width / 2, yOff + sqareSize, Qt.AlignHCenter | Qt.AlignTop, html_text= getHtmlCompatibleString(data.domain[self.attrX].values[i]))
            max_xlabel_h = max(int(xl.boundingRect().height()), max_xlabel_h)

            currX += width

        # show attribute names
        CanvasText(self.canvas, self.attrY, 0, yOff + sqareSize / 2, Qt.AlignLeft | Qt.AlignVCenter, bold = 1, vertical=True)
        CanvasText(self.canvas, self.attrX, xOff + sqareSize / 2, yOff + sqareSize + max_xlabel_h, Qt.AlignHCenter | Qt.AlignTop, bold = 1)


    ######################################################################
    ## show deviations from attribute independence with standardized pearson residuals
    def addRectIndependencePearson(self, rect, x, y, w, h, xAttr_xVal, yAttr_yVal, actual, sum, width):
        xAttr, xVal = xAttr_xVal
        yAttr, yVal = yAttr_yVal
        expected = float(xVal*yVal)/float(sum)
        pearson = (actual - expected) / sqrt(expected)

        if pearson > 0:     # if there are more examples that we would expect under the null hypothesis
            intPearson = floor(pearson)
            pen = QPen(QColor(0,0,255), width); rect.setPen(pen)
            b = 255
            r = g = 255 - intPearson*20
            r = g = max(r, 55)  #
        elif pearson < 0:
            intPearson = ceil(pearson)
            pen = QPen(QColor(255,0,0), width)
            rect.setPen(pen)
            r = 255
            b = g = 255 + intPearson*20
            b = g = max(b, 55)
        else:
            pen = QPen(QColor(255,255,255), width)
            r = g = b = 255         # white
        color = QColor(r,g,b)
        brush = QBrush(color)
        rect.setBrush(brush)

        if pearson > 0:
            pearson = min(pearson, 10)
            kvoc = 1 - 0.08 * pearson       #  if pearson in [0..10] --> kvoc in [1..0.2]
        else:
            pearson = max(pearson, -10)
            kvoc = 1 - 0.4*pearson

        pen.setWidth(1)
        self.addLines(x,y,w,h, kvoc, pen)


    ##################################################
    # add lines
    def addLines(self, x, y, w, h, diff, pen):
        if w == 0 or h == 0:
            return

        dist = 20 * diff  # original distance between two lines in pixels
        temp = dist
        canvas = self.canvas
        while temp < w:
            r = QGraphicsLineItem(temp + x, y, temp + x, y + h, None)
            canvas.addItem(r)
            r.setPen(pen)
            temp += dist

        temp = dist
        while temp < h:
            r = QGraphicsLineItem(x, y + temp, x + w, y + temp, None)
            canvas.addItem(r)
            r.setPen(pen)
            temp += dist

    def closeEvent(self, ce):
        QDialog.closeEvent(self, ce)

    def get_widget_name_extension(self):
        if self.data is not None:
            return "{} vs {}".format(self.attrX, self.attrY)

    def send_report(self):
        self.report_plot()
class QtImageViewer(QGraphicsView):
    """ PyQt image viewer widget for a QPixmap in a QGraphicsView scene with mouse zooming and panning.

    Displays a QImage or QPixmap (QImage is internally converted to a QPixmap).
    To display any other image format, you must first convert it to a QImage or QPixmap.

    Some useful image format conversion utilities:
        qimage2ndarray: NumPy ndarray <==> QImage    (https://github.com/hmeine/qimage2ndarray)
        ImageQt: PIL Image <==> QImage  (https://github.com/python-pillow/Pillow/blob/master/PIL/ImageQt.py)

    """

    # Mouse button signals emit image scene (x, y) coordinates.
    # !!! For image (row, column) matrix indexing, row = y and column = x.
    hover = pyqtSignal(float, float)
    leftMouseButtonPressed = pyqtSignal(float, float, object)
    rightMouseButtonPressed = pyqtSignal(float, float)
    leftMouseButtonReleased = pyqtSignal(float, float)
    leftMouseButtonDoubleClicked = pyqtSignal(float, float)
    rightMouseButtonDoubleClicked = pyqtSignal(float, float)
    areaSelected = pyqtSignal(float, float, float, float)
    pixelSelected = pyqtSignal(float, float)

    def __init__(self, parent=None, center=None, thumbnail=False):
        QGraphicsView.__init__(self, parent)

        # Image is displayed as a QPixmap in a QGraphicsScene attached to this QGraphicsView.
        self.scene = QGraphicsScene()
        self.setScene(self.scene)

        # Store a local handle to the scene's current image pixmap.
        self._pixmapHandle = None

        # Image aspect ratio mode.
        # !!! ONLY applies to full image. Aspect ratio is always ignored when zooming.
        #   Qt.IgnoreAspectRatio: Scale image to fit viewport.
        #   Qt.KeepAspectRatio: Scale image to fit inside viewport, preserving aspect ratio.
        #   Qt.KeepAspectRatioByExpanding: Scale image to fill the viewport, preserving aspect ratio.
        self.aspectRatioMode = Qt.KeepAspectRatio

        # Scroll bar behaviour.
        #   Qt.ScrollBarAlwaysOff: Never shows a scroll bar.
        #   Qt.ScrollBarAlwaysOn: Always shows a scroll bar.
        #   Qt.ScrollBarAsNeeded: Shows a scroll bar only when zoomed.
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)

        # Stack of QRectF zoom boxes in scene coordinates.
        self.zoomStack = []
        self.dragSelect = False
        # Flags for enabling/disabling mouse interaction.
        self.canZoom = True
        self.canPan = True
        self.center = center
        self.thumbnail = thumbnail

    def hasImage(self):
        """ Returns whether or not the scene contains an image pixmap.
        """
        return self._pixmapHandle is not None

    def clearImage(self):
        """ Removes the current image pixmap from the scene if it exists.
        """
        if self.hasImage():
            self.scene.removeItem(self._pixmapHandle)
            self.scene.clear()
            self._pixmapHandle = None

    def pixmap(self):
        """ Returns the scene's current image pixmap as a QPixmap, or else None if no image exists.
        :rtype: QPixmap | None
        """
        if self.hasImage():
            return self._pixmapHandle.pixmap()
        return None

    def image(self):
        """ Returns the scene's current image pixmap as a QImage, or else None if no image exists.
        :rtype: QImage | None
        """
        if self.hasImage():
            return self._pixmapHandle.pixmap().toImage()
        return None

    def setImage(self, image):
        """ Set the scene's current image pixmap to the input QImage or QPixmap.
        Raises a RuntimeError if the input image has type other than QImage or QPixmap.
        :type image: QImage | QPixmap
        """
        if type(image) is QPixmap:
            pixmap = image
        elif type(image) is QImage:
            pixmap = QPixmap.fromImage(image)
        else:
            raise RuntimeError(
                "ImageViewer.setImage: Argument must be a QImage or QPixmap.")
        if self.hasImage():
            self._pixmapHandle.setPixmap(pixmap)
        else:
            self._pixmapHandle = self.scene.addPixmap(pixmap)
        self.setSceneRect(QRectF(
            pixmap.rect()))  # Set scene size to image size.
        self.updateViewer()

    def loadImageFromFile(self, fileName=""):
        """ Load an image from file.
        Without any arguments, loadImageFromFile() will popup a file dialog to choose the image file.
        With a fileName argument, loadImageFromFile(fileName) will attempt to load the specified image file directly.
        """
        if len(fileName) == 0:
            if QT_VERSION_STR[0] == '4':
                fileName = QFileDialog.getOpenFileName(self,
                                                       "Open image file.")
            elif QT_VERSION_STR[0] == '5':
                fileName, dummy = QFileDialog.getOpenFileName(
                    self, "Open image file.")
        if len(fileName) and os.path.isfile(fileName):
            image = QImage(fileName)
            self.setImage(image)

    def updateViewer(self):
        """ Show current zoom (if showing entire image, apply current aspect ratio mode).
        """
        if not self.hasImage():
            return
        if len(self.zoomStack) and self.sceneRect().contains(
                self.zoomStack[-1]):
            self.fitInView(self.zoomStack[-1], Qt.IgnoreAspectRatio
                           )  # Show zoomed rect (ignore aspect ratio).
        else:
            self.zoomStack = [
            ]  # Clear the zoom stack (in case we got here because of an invalid zoom).
            if self.thumbnail == True:
                self.fitInView(
                    self.sceneRect(), self.aspectRatioMode
                )  # Show entire image (use current aspect ratio mode).

    def resizeEvent(self, event):
        """ Maintain current zoom on resize.
        """
        self.updateViewer()

    def mouseMoveEvent(self, event):
        if self.hasImage():
            scenePos = self.mapToScene(event.pos())
            self.hover.emit(scenePos.x(), scenePos.y())
            if event.buttons() == Qt.RightButton:
                self.rightMouseButtonPressed.emit(scenePos.x(), scenePos.y())
            else:
                pass
        QGraphicsView.mouseMoveEvent(self, event)

    def mousePressEvent(self, event):
        """ Start mouse pan or zoom mode.
        """
        scenePos = self.mapToScene(event.pos())
        if event.button() == Qt.LeftButton:
            if self.canPan:
                self.setDragMode(QGraphicsView.ScrollHandDrag)
            if self.hasImage():
                self.leftMouseButtonPressed.emit(scenePos.x(), scenePos.y(),
                                                 self)
        elif event.button() == Qt.RightButton:
            if self.dragSelect:
                if self.hasImage():
                    self.setDragMode(QGraphicsView.RubberBandDrag)
                    self.viewport().setCursor(QCursor(Qt.CrossCursor))
                    self.rightMouseButtonPressed.emit(scenePos.x(),
                                                      scenePos.y())
        QGraphicsView.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """ Stop mouse pan or zoom mode (apply zoom if valid).
        """
        QGraphicsView.mouseReleaseEvent(self, event)
        scenePos = self.mapToScene(event.pos())
        if event.button() == Qt.LeftButton:
            self.setDragMode(QGraphicsView.NoDrag)
            if self.hasImage():
                self.leftMouseButtonReleased.emit(scenePos.x(), scenePos.y())
        elif event.button() == Qt.RightButton:
            self.setDragMode(QGraphicsView.NoDrag)
            if self.hasImage():
                self.viewport().setCursor(QCursor(Qt.OpenHandCursor))
                if self.dragSelect:
                    viewBBox = self.zoomStack[-1] if len(
                        self.zoomStack) else self.sceneRect()
                    selectionBBox = self.scene.selectionArea().boundingRect(
                    ).intersected(viewBBox)
                    print(selectionBBox)
                    self.areaSelected.emit(selectionBBox.x(),
                                           selectionBBox.y(),
                                           selectionBBox.width(),
                                           selectionBBox.height())
                else:
                    self.pixelSelected.emit(scenePos.x(), scenePos.y())

    def mouseDoubleClickEvent(self, event):
        """ Show entire image.
        """
        scenePos = self.mapToScene(event.pos())
        if event.button() == Qt.LeftButton:
            self.leftMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
        elif event.button() == Qt.RightButton:
            if self.canZoom:
                self.zoomStack = []  # Clear zoom stack.
                self.updateViewer()
            self.rightMouseButtonDoubleClicked.emit(scenePos.x(), scenePos.y())
        QGraphicsView.mouseDoubleClickEvent(self, event)

    def wheelEvent(self, event):
        try:
            if self.thumbnail == True:
                return
            zoomInFactor = 1.2
            zoomOutFactor = .8
            # Zoom
            if event.angleDelta().y() > 0:
                zoomFactor = zoomInFactor
            else:
                zoomFactor = zoomOutFactor
            self.scale(zoomFactor, zoomFactor)
            # Get the new position
            newPos = self.mapToScene(event.pos())
            self.centerOn(newPos)
        except Exception as e:
            logging.info(e)

    def zoomToArea(self, center, scale):
        if self.hasImage():
            self.setTransform(QTransform())
            pos = QPoint(center[0], center[1])
            self.centerOn(pos)
            self.scale(scale, scale)

    def resetZoom(self):
        self.setTransform(QTransform())
        self.fitInView(self.sceneRect(), self.aspectRatioMode
                       )  # Show entire image (use current aspect ratio mode).

    def showEvent(self, event):
        self.resetZoom()
Example #21
0
class OWSieveDiagram(OWWidget):
    name = "Sieve Diagram"
    description = "A two-way contingency table providing information in " \
                  "relation to expected frequency of combination of feature " \
                  "values under independence."
    icon = "icons/SieveDiagram.svg"
    priority = 4200

    inputs = [("Data", Table, "set_data", Default),
              ("Features", AttributeList, "set_input_features")]
    outputs = [("Selection", Table)]

    graph_name = "canvas"

    want_control_area = False

    settingsHandler = DomainContextHandler()
    attrX = ContextSetting("")
    attrY = ContextSetting("")
    selection = ContextSetting(set())

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

        self.data = None
        self.input_features = None
        self.attrs = []

        self.attr_box = gui.hBox(self.mainArea)
        model = VariableListModel()
        model.wrap(self.attrs)
        self.attrXCombo = gui.comboBox(self.attr_box,
                                       self,
                                       value="attrX",
                                       contentsLength=12,
                                       callback=self.change_attr,
                                       sendSelectedValue=True,
                                       valueType=str)
        self.attrXCombo.setModel(model)
        gui.widgetLabel(self.attr_box, "\u2715").\
            setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.attrYCombo = gui.comboBox(self.attr_box,
                                       self,
                                       value="attrY",
                                       contentsLength=12,
                                       callback=self.change_attr,
                                       sendSelectedValue=True,
                                       valueType=str)
        self.attrYCombo.setModel(model)

        self.canvas = QGraphicsScene()
        self.canvasView = ViewWithPress(self.canvas,
                                        self.mainArea,
                                        handler=self.reset_selection)
        self.mainArea.layout().addWidget(self.canvasView)
        self.canvasView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.canvasView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        box = gui.hBox(self.mainArea)
        gui.button(box,
                   None,
                   "&Save Graph",
                   callback=self.save_graph,
                   autoDefault=False)
        gui.button(box,
                   None,
                   "&Report",
                   callback=self.show_report,
                   autoDefault=False)

    def sizeHint(self):
        return QSize(450, 550)

    def set_data(self, data):
        if type(data) == SqlTable and data.approx_len() > LARGE_TABLE:
            data = data.sample_time(DEFAULT_SAMPLE_TIME)

        self.closeContext()
        self.data = data
        self.areas = []
        if self.data is None:
            self.attrs[:] = []
        else:
            self.attrs[:] = [
                var for var in chain(self.data.domain, self.data.domain.metas)
                if var.is_discrete
            ]
        if self.attrs:
            self.attrX = self.attrs[0].name
            self.attrY = self.attrs[len(self.attrs) > 1].name
        else:
            self.attrX = self.attrY = None
        self.openContext(self.data)

        self.information(0, "")
        if data and any(attr.is_continuous for attr in data.domain):
            self.information(
                0, "Data contains continuous variables. "
                "Discretize the data to use them.")
        self.resolve_shown_attributes()
        self.update_selection()

    def change_attr(self):
        self.selection = set()
        self.updateGraph()
        self.update_selection()

    def set_input_features(self, attrList):
        self.input_features = attrList
        self.resolve_shown_attributes()
        self.update_selection()

    def resolve_shown_attributes(self):
        self.warning(1)
        self.attr_box.setEnabled(True)
        if self.input_features:  # non-None and non-empty!
            features = [f for f in self.input_features if f in self.attrs]
            if not features:
                self.warning(
                    1, "Features from the input signal "
                    "are not present in the data")
            else:
                old_attrs = self.attrX, self.attrY
                self.attrX, self.attrY = [f.name for f in (features * 2)[:2]]
                self.attr_box.setEnabled(False)
                if (self.attrX, self.attrY) != old_attrs:
                    self.selection = set()
        # else: do nothing; keep current features, even if input with the
        # features just changed to None
        self.updateGraph()

    def resizeEvent(self, e):
        OWWidget.resizeEvent(self, e)
        self.updateGraph()

    def showEvent(self, ev):
        OWWidget.showEvent(self, ev)
        self.updateGraph()

    def reset_selection(self):
        self.selection = set()
        self.update_selection()

    def select_area(self, area, ev):
        if ev.button() != Qt.LeftButton:
            return
        index = self.areas.index(area)
        if ev.modifiers() & Qt.ControlModifier:
            self.selection ^= {index}
        else:
            self.selection = {index}
        self.update_selection()

    def update_selection(self):
        if self.areas is None or not self.selection:
            self.send("Selection", None)
            return

        filters = []
        for i, area in enumerate(self.areas):
            if i in self.selection:
                width = 4
                val_x, val_y = area.value_pair
                filters.append(
                    filter.Values([
                        filter.FilterDiscrete(self.attrX, [val_x]),
                        filter.FilterDiscrete(self.attrY, [val_y])
                    ]))
            else:
                width = 1
            pen = area.pen()
            pen.setWidth(width)
            area.setPen(pen)
        if len(filters) == 1:
            filters = filters[0]
        else:
            filters = filter.Values(filters, conjunction=False)
        self.send("Selection", filters(self.data))

    # -----------------------------------------------------------------------
    # Everything from here on is ancient and has been changed only according
    # to what has been changed above. Some clean-up may be in order some day
    #
    def updateGraph(self, *args):
        for item in self.canvas.items():
            self.canvas.removeItem(item)
        if self.data is None or len(self.data) == 0 or \
                self.attrX is None or self.attrY is None:
            return
        data = self.data[:, [self.attrX, self.attrY]]
        valsX = []
        valsY = []
        contX = get_contingency(data, self.attrX, self.attrX)
        contY = get_contingency(data, self.attrY, self.attrY)
        # compute contingency of x and y attributes
        for entry in contX:
            sum_ = 0
            try:
                for val in entry:
                    sum_ += val
            except:
                pass
            valsX.append(sum_)

        for entry in contY:
            sum_ = 0
            try:
                for val in entry:
                    sum_ += val
            except:
                pass
            valsY.append(sum_)

        contXY, _ = get_conditional_distribution(
            data, [data.domain[self.attrX], data.domain[self.attrY]])
        # compute probabilities
        probs = {}
        for i in range(len(valsX)):
            valx = valsX[i]
            for j in range(len(valsY)):
                valy = valsY[j]
                try:
                    actualProb = contXY['%s-%s' %
                                        (data.domain[self.attrX].values[i],
                                         data.domain[self.attrY].values[j])]
                    # for val in contXY['%s-%s' %(i, j)]: actualProb += val
                except:
                    actualProb = 0
                probs['%s-%s' % (data.domain[self.attrX].values[i],
                                 data.domain[self.attrY].values[j])] = ((
                                     data.domain[self.attrX].values[i],
                                     valx), (data.domain[self.attrY].values[j],
                                             valy), actualProb, len(data))

        #get text width of Y labels
        max_ylabel_w = 0
        for j in range(len(valsY)):
            xl = CanvasText(self.canvas,
                            "",
                            0,
                            0,
                            html_text=getHtmlCompatibleString(
                                data.domain[self.attrY].values[j]),
                            show=False)
            max_ylabel_w = max(int(xl.boundingRect().width()), max_ylabel_w)
        max_ylabel_w = min(max_ylabel_w, 200)  #upper limit for label widths
        # get text width of Y attribute name
        text = CanvasText(self.canvas,
                          data.domain[self.attrY].name,
                          x=0,
                          y=0,
                          bold=1,
                          show=0,
                          vertical=True)
        xOff = int(text.boundingRect().height() + max_ylabel_w)
        yOff = 55
        sqareSize = min(self.canvasView.width() - xOff - 35,
                        self.canvasView.height() - yOff - 50)
        sqareSize = max(sqareSize, 10)
        self.canvasView.setSceneRect(0, 0, self.canvasView.width(),
                                     self.canvasView.height())

        # print graph name
        name = "<b>P(%s, %s) &#8800; P(%s)&times;P(%s)</b>" % (
            self.attrX, self.attrY, self.attrX, self.attrY)
        CanvasText(self.canvas,
                   "",
                   xOff + sqareSize / 2,
                   20,
                   Qt.AlignCenter,
                   html_text=name)
        CanvasText(self.canvas,
                   "N = " + str(len(data)),
                   xOff + sqareSize / 2,
                   38,
                   Qt.AlignCenter,
                   bold=0)

        ######################
        # compute chi-square
        chisquare = 0.0
        for i in range(len(valsX)):
            for j in range(len(valsY)):
                ((xAttr, xVal), (yAttr, yVal), actual,
                 sum_) = probs['%s-%s' % (data.domain[self.attrX].values[i],
                                          data.domain[self.attrY].values[j])]
                expected = float(xVal * yVal) / float(sum_)
                if expected == 0: continue
                pearson2 = (actual - expected) * (actual - expected) / expected
                chisquare += pearson2

        ######################
        # draw rectangles
        currX = xOff
        max_xlabel_h = 0
        normX, normY = sum(valsX), sum(valsY)
        self.areas = []
        for i in range(len(valsX)):
            if valsX[i] == 0: continue
            currY = yOff
            width = int(float(sqareSize * valsX[i]) / float(normX))

            for j in range(len(valsY) - 1, -1,
                           -1):  # this way we sort y values correctly
                ((xAttr, xVal), (yAttr, yVal), actual,
                 sum_) = probs['%s-%s' % (data.domain[self.attrX].values[i],
                                          data.domain[self.attrY].values[j])]
                if valsY[j] == 0: continue
                height = int(float(sqareSize * valsY[j]) / float(normY))

                # create rectangle
                selected = len(self.areas) in self.selection
                rect = CanvasRectangle(self.canvas,
                                       currX + 2,
                                       currY + 2,
                                       width - 4,
                                       height - 4,
                                       z=-10,
                                       onclick=self.select_area)
                rect.value_pair = i, j
                self.areas.append(rect)
                self.addRectIndependencePearson(
                    rect,
                    currX + 2,
                    currY + 2,
                    width - 4,
                    height - 4,
                    (xAttr, xVal),
                    (yAttr, yVal),
                    actual,
                    sum_,
                    width=1 + 3 * selected,  # Ugly! This is needed since
                    # resize redraws the graph! When this is handled by resizing
                    # just the viewer, update_selection will take care of this
                )

                expected = float(xVal * yVal) / float(sum_)
                pearson = (actual - expected) / sqrt(expected)
                tooltipText = """<b>X Attribute: %s</b><br>Value: <b>%s</b><br>Number of instances (p(x)): <b>%d (%.2f%%)</b><hr>
                                <b>Y Attribute: %s</b><br>Value: <b>%s</b><br>Number of instances (p(y)): <b>%d (%.2f%%)</b><hr>
                                <b>Number Of Instances (Probabilities):</b><br>Expected (p(x)p(y)): <b>%.1f (%.2f%%)</b><br>Actual (p(x,y)): <b>%d (%.2f%%)</b>
                                <hr><b>Statistics:</b><br>Chi-square: <b>%.2f</b><br>Standardized Pearson residual: <b>%.2f</b>""" % (
                    self.attrX, getHtmlCompatibleString(xAttr), xVal,
                    100.0 * float(xVal) / float(sum_), self.attrY,
                    getHtmlCompatibleString(yAttr), yVal,
                    100.0 * float(yVal) / float(sum_), expected,
                    100.0 * float(xVal * yVal) / float(sum_ * sum_), actual,
                    100.0 * float(actual) / float(sum_), chisquare, pearson)
                rect.setToolTip(tooltipText)

                currY += height
                if currX == xOff:
                    CanvasText(self.canvas,
                               "",
                               xOff,
                               currY - height / 2,
                               Qt.AlignRight | Qt.AlignVCenter,
                               html_text=getHtmlCompatibleString(
                                   data.domain[self.attrY].values[j]))

            xl = CanvasText(self.canvas,
                            "",
                            currX + width / 2,
                            yOff + sqareSize,
                            Qt.AlignHCenter | Qt.AlignTop,
                            html_text=getHtmlCompatibleString(
                                data.domain[self.attrX].values[i]))
            max_xlabel_h = max(int(xl.boundingRect().height()), max_xlabel_h)

            currX += width

        # show attribute names
        CanvasText(self.canvas,
                   self.attrY,
                   0,
                   yOff + sqareSize / 2,
                   Qt.AlignLeft | Qt.AlignVCenter,
                   bold=1,
                   vertical=True)
        CanvasText(self.canvas,
                   self.attrX,
                   xOff + sqareSize / 2,
                   yOff + sqareSize + max_xlabel_h,
                   Qt.AlignHCenter | Qt.AlignTop,
                   bold=1)

    ######################################################################
    ## show deviations from attribute independence with standardized pearson residuals
    def addRectIndependencePearson(self, rect, x, y, w, h, xAttr_xVal,
                                   yAttr_yVal, actual, sum, width):
        xAttr, xVal = xAttr_xVal
        yAttr, yVal = yAttr_yVal
        expected = float(xVal * yVal) / float(sum)
        pearson = (actual - expected) / sqrt(expected)

        if pearson > 0:  # if there are more examples that we would expect under the null hypothesis
            intPearson = floor(pearson)
            pen = QPen(QColor(0, 0, 255), width)
            rect.setPen(pen)
            b = 255
            r = g = 255 - intPearson * 20
            r = g = max(r, 55)  #
        elif pearson < 0:
            intPearson = ceil(pearson)
            pen = QPen(QColor(255, 0, 0), width)
            rect.setPen(pen)
            r = 255
            b = g = 255 + intPearson * 20
            b = g = max(b, 55)
        else:
            pen = QPen(QColor(255, 255, 255), width)
            r = g = b = 255  # white
        color = QColor(r, g, b)
        brush = QBrush(color)
        rect.setBrush(brush)

        if pearson > 0:
            pearson = min(pearson, 10)
            kvoc = 1 - 0.08 * pearson  #  if pearson in [0..10] --> kvoc in [1..0.2]
        else:
            pearson = max(pearson, -10)
            kvoc = 1 - 0.4 * pearson

        pen.setWidth(1)
        self.addLines(x, y, w, h, kvoc, pen)

    ##################################################
    # add lines
    def addLines(self, x, y, w, h, diff, pen):
        if w == 0 or h == 0:
            return

        dist = 20 * diff  # original distance between two lines in pixels
        temp = dist
        canvas = self.canvas
        while temp < w:
            r = QGraphicsLineItem(temp + x, y, temp + x, y + h, None)
            canvas.addItem(r)
            r.setPen(pen)
            temp += dist

        temp = dist
        while temp < h:
            r = QGraphicsLineItem(x, y + temp, x + w, y + temp, None)
            canvas.addItem(r)
            r.setPen(pen)
            temp += dist

    def closeEvent(self, ce):
        QDialog.closeEvent(self, ce)

    def get_widget_name_extension(self):
        if self.data is not None:
            return "{} vs {}".format(self.attrX, self.attrY)

    def send_report(self):
        self.report_plot()
Example #22
0
class PaintArea(QGraphicsView):
    def __init__(self, parent):
        QGraphicsView.__init__(self)
        self.init(parent)
        self.initShape()
        self.initCall = 2
        self.minimumWidth = 550
        self.setEnabled(False)

    def init(self, parent):
        self.timeline = parent
        self.pixmap = None
        self.image = None

    def initShape(self):
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setAlignment(Qt.AlignLeft)
        self.clicStart = None
        self.selectionRect = None

    def resizeEvent(self, sizEvent):
        if self.selectionRect and self.scene:
            self.scene.removeItem(self.selectionRect)
            self.selectionRect = None
            self.clicStart = None

        self.width = sizEvent.size().width()
        self.height = sizEvent.size().height()

        if self.initCall > 0:
            self.timeline.options.setMaximumWidth(
                self.timeline.options.minimumSizeHint().width())
            self.initCall -= 1

        if self.width < self.minimumWidth:
            self.timeline.setStateInfo('Unable to draw - Not enought width')
        else:
            self.timeline.workerThread.render()

    def mousePressEvent(self, mouseEvent):
        self.clicStart = mouseEvent.pos()
        if self.clicStart.x() < self.timeline.draw.yLeftMargin - 1:
            self.clicStart.setX(self.timeline.draw.yLeftMargin - 1)
        if self.clicStart.x(
        ) > self.timeline.ploter.width - self.timeline.m + 1:
            self.clicStart.setX(self.timeline.ploter.width - self.timeline.m +
                                1)

        if self.selectionRect:
            for item in self.scene.items():
                if str(type(
                        item)) == "<class 'PyQt4.QtGui.QGraphicsRectItem'>":
                    self.scene.removeItem(item)
            self.selectionRect = None
            self.timeline.options.zoomButton.setEnabled(False)
            self.timeline.options.exportButton.setEnabled(False)
            self.timeline.options.selectedNodes.setText('Nothing selected')
            if self.timeline.selDateMin:
                self.timeline.options.selStartTime.setText('From ' + str(
                    self.timeline.fromUSec(self.timeline.baseDateMin).strftime(
                        '%d.%m.%Y %H:%M:%S')))
                self.timeline.options.selEndTime.setText('To ' + str(
                    self.timeline.fromUSec(self.timeline.baseDateMax).strftime(
                        '%d.%m.%Y %H:%M:%S')))
            else:
                self.timeline.options.selStartTime.setText(
                    'No selection start time')
                self.timeline.options.selEndTime.setText(
                    'No selection end time')
            self.timeline.options.selectedNodes.setText('Nothing selected')

    def mouseMoveEvent(self, dragMoveEvent):
        if self.clicStart:
            if self.clicStart.x() < dragMoveEvent.x():
                x = self.clicStart.x()
                w = dragMoveEvent.x() - self.clicStart.x()
            else:
                x = dragMoveEvent.x()
                w = self.clicStart.x() - dragMoveEvent.x()

# Limit rectangle to selectable area
            if x < self.timeline.draw.yLeftMargin - 1:
                x = self.timeline.draw.yLeftMargin - 1
                w = self.clicStart.x() - x
            if x > self.timeline.ploter.width - self.timeline.m + 1:
                x = self.timeline.ploter.width - self.timeline.m + 1
                w = 0
            if x + w > self.timeline.ploter.width - self.timeline.m + 1:
                w = ((self.timeline.ploter.width - self.timeline.m + 1) - x)

            y = (self.timeline.m / 3) - 1
            h = self.height - self.timeline.m - self.timeline.m / 3 + 2
            if self.selectionRect and self.scene:
                self.scene.removeItem(self.selectionRect)
            self.selectionRect = self.scene.addRect(QRectF(x, y, w, h),
                                                    QPen(Qt.DashDotDotLine))

    def mouseReleaseEvent(self, mouseEvent):
        if self.selectionRect:
            if self.clicStart.x() > mouseEvent.x():
                x1 = mouseEvent.x()
                x2 = self.clicStart.x()
            else:
                x1 = self.clicStart.x()
                x2 = mouseEvent.x()
            self.timeline.nodesInRange(x1, x2)
            start = self.timeline.draw.findXTime(x1)
            if start:
                self.timeline.options.selStartTime.setText(
                    'From ' + str(start.strftime('%d.%m.%Y %H:%M:%S')))
            end = self.timeline.draw.findXTime(x2)
            if end:
                self.timeline.options.selEndTime.setText(
                    'To ' + str(end.strftime('%d.%m.%Y %H:%M:%S')))