Ejemplo n.º 1
0
 def generateData(self, canvas, lines, params):
     scene = QGraphicsScene()
     scene.setSceneRect(canvas)
     group = scene.createItemGroup([])
     for line in lines:
         clone = QGraphicsLineItem(line)
         clone.setLine(line.line())
         clone.setPen(line.pen())
         scene.addItem(clone)
         group.addToGroup(clone)
     pixmaps = []
     for i in xrange(params.count):
         pixmaps.append(self.generateRandom(scene, group, canvas, params))
     return pixmaps
Ejemplo n.º 2
0
class MarkedHistogram(QWidget):
    """Histogram with color-indication markers

       MarkedHistogram shows a histogram of a data set and an optional
       label for the numeric range of the data set.  Color markers can
       be placed on the histogram by the user and moved interactively,
       either with the mouse or by typing in a particular data value.
       A color button is used to control the color of the "current" marker
       (the one most recently selected with the mouse).  Markers can
       either be vertical bars or squares.  Vertical bars can only be
       moved left/right whereas squares can also be moved up/down.
       Squares are also connected from left to right by line segments.

       A row of associated widgets (such as the marker color button) is
       placed below the histogram.  User-specified widgets can also be
       placed in this row with the add_custom_widget() method.

       Individual markers are grouped into HistogramMarkers instances,
       and several HistogramMarkers instances can be associated with
       a single histogram, though only one instance is active/shown at
       a time.

       MarkedHistogram has the following constructor options:
           [Options noted as init options can only be specified at
        widget creation.  Others can be changed later via the corresponding
        property name.]

        color_button --  controls whether a color button is offered in
            the user interface for changing marker colors.
            default: True

        data_source -- either a string or a 3-tuple.  If a string, then
            no histogram is displayed and instead the string is
            displayed in the histogram area as a text message.
            The first 2 components of a 3-tuple should be the
            minimum and maximum values of the histogram,  The
            third component should either be an array of numbers
            (i.e. the histogram) or a callback function that
            takes as its single argument the number of bins to
            histogram into and that returns a histogram array.
            default: 'no data'

        layout -- [init option] how to organize the megawidget layout.
            Choices are 'single', 'top', and 'below'.  'single'
            should be used when you are using a single histogram
            in your GUI, or histograms that aren't arrayed
            vertically.  'top' and 'below' should be used for
            histograms that are laid out in a vertical array
            ('top' for the top-most one, and 'below' for all
            others).  Certain repeated elements will be omitted
            in 'below' histograms (e.g. some widget labels).
            default: single

        max_label/min_label [init options] show the max/min histogram
            values as labels below the histogram on the right/left.
            If neither is True, then the range will be shown in a
            single label widget below the histogram.
            default: False

        redraw_delay -- amount of time (in seconds) to delay between
            needing to redraw and actually starting to redraw.
            Used to avoid multiple (possibly slow) redraws during
            a resize.
            default: 0.25

        scaling -- how to scale the vertical height of the histogram.
            Either 'logarithmic' or 'linear'.
            default: logarithmic

        select_callback -- [init option] function to call when the
            "current" marker changes.  The function receives 4
            argments:  previous active marker set/marker,
            new active marker set/marker.  The marker set can
            be None to indicate no active marker set and the
            marker can be None if no marker was/is current.

        show_marker_help -- [init option] whether to show the help
            text over the histogram describing how to add/delete
            markers.
            default: True

        status_line -- function to use to output messages (such as
            warning when trying to add more markers than allowed).
            The function should take a single string argument.
            default: None

        value_label -- [init option] label to use next to the
            entry widget describing the current marker value.
            default: 'Value'

        value_width -- width of the current marker value entry widget.
            default: 7

       Constructor options that begin with 'Markers_' specify default
       constructor options for HistogramMarkers objects created in the
       add_markers method (e.g. Markers_connect_color='red' will supply
       connect_color='red' to the HistogramMarkers constructor).  Options
       for specific instances can still be provided to the add_ markers()
       method as keyword arguments (without the 'Markers_' prefix).
    """
    def __init__(self,
                 *args,
                 color_button=True,
                 data_source='no data',
                 layout='single',
                 max_label=False,
                 min_label=False,
                 redraw_delay=0.25,
                 scaling='logarithmic',
                 select_callback=None,
                 show_marker_help=True,
                 status_line=None,
                 value_label='Value',
                 value_width=7,
                 **kw):

        # Get HistogramMarkers options and initialise base class
        self._histogram_markers_kw = markers_kw = {}
        for opt_name in list(kw.keys()):
            if opt_name.startswith('Markers_'):
                markers_kw[opt_name[8:]] = kw.pop(opt_name)
        super().__init__(*args, **kw)

        # initialize variables
        self._layout = layout
        self.status_line = status_line
        self._show_marker_help = show_marker_help
        self._active_markers = None
        self._markers = []
        self._markable = False
        self._drag_marker = None
        self._scaling = scaling
        self._select_callback = select_callback
        if select_callback:
            self._prev_markers = None
            self._prev_marker = None

        overall_layout = QVBoxLayout()
        self.setLayout(overall_layout)

        # Create the add/delete marker help
        if show_marker_help and layout != 'below':
            self._marker_help = QLabel(
                "Ctrl-click on histogram to add or delete thresholds")
            self._marker_help.setAlignment(Qt.AlignCenter)
            overall_layout.addWidget(self._marker_help)
        else:
            self._marker_help = None

        # Create the data area
        class HistFrame(QFrame):
            def sizeHint(self):
                return QSize(300, 64)

        data_frame = QFrame()
        data_frame.setLineWidth(1)
        data_frame.setMidLineWidth(2)
        data_frame.setFrameStyle(data_frame.Panel | data_frame.Sunken)
        data_frame.setContentsMargins(0, 0, 0, 0)
        data_frame_layout = QHBoxLayout()
        data_frame.setLayout(data_frame_layout)
        self._data_widgets = QStackedWidget()
        data_frame_layout.addWidget(self._data_widgets)

        # Crate the histogram widget
        self._hist_scene = QGraphicsScene()
        self._hist_bars = self._hist_scene.createItemGroup([])
        self._hist_view = QGraphicsView(self._hist_scene)
        self._hist_view.resizeEvent = self._redraw
        self._hist_scene.mousePressEvent = lambda event: self._add_or_delete_marker_cb(event) \
            if event.modifiers() & mod_key_info("control")[0] else self._select_marker_cb(event)
        self._hist_scene.mouseMoveEvent = lambda event: self._move_marker_cb(event) \
            if self._drag_marker else super().mouseMoveEvent(event)
        self._hist_scene.mouseReleaseEvent = self._button_up_cb
        self._redraw_timer = QTimer()
        self._redraw_timer.timeout.connect(self._redraw_cb)
        self._redraw_timer.start(1000 * redraw_delay)
        self._redraw_timer.stop()
        self._data_widgets.addWidget(self._hist_view)

        # Create the histogram replacement label
        self._no_histogram_label = QLabel()
        self._no_histogram_label.setAlignment(Qt.AlignCenter)
        self._data_widgets.addWidget(self._no_histogram_label)
        overall_layout.addWidget(self._data_widgets, stretch=1)

        # Create range label(s)
        self._widget_area = QWidget()
        self._widget_layout = QHBoxLayout()
        self._min_label = self._max_label = None
        if min_label or max_label:
            min_max_layout = QHBoxLayout()
            if min_label:
                self._min_label = QLabel()
                min_max_layout.addWidget(self._min_label,
                                         alignment=Qt.AlignLeft & Qt.AlignTop)

            if max_label:
                self._max_label = QLabel()
                min_max_layout.addWidget(self._max_label,
                                         alignment=Qt.AlignRight & Qt.AlignTop)
            overall_layout.addLayout(min_max_layout)
        else:
            self._range_label = QLabel()
            if layout == 'below':
                self._widget_layout.addWidget(self._range_label)
            else:
                lab = QLabel("Range")
                if layout == 'single':
                    self._widget_layout.addWidget(lab, alignment=Qt.AlignRight)
                    self._widget_layout.addWidget(self._range_label,
                                                  alignment=Qt.AlignLeft)
                else:  # layout == 'top'
                    range_layout = QVBoxLayout()
                    range_layout.addWidget(lab, alignment=Qt.AlignBottom)
                    range_layout.addWidget(self._range_label,
                                           alignment=Qt.AlignTop)
                    self._widget_layout.addLayout(range_layout)
        self._widget_area.setLayout(self._widget_layout)
        overall_layout.addWidget(self._widget_area)

        # Create value widget
        self._value_entry = QLineEdit()
        self._value_entry.setEnabled(False)
        self._value_entry.returnPressed.connect(self._set_value_cb)
        self.value_width = value_width
        if layout == 'below':
            self._widget_layout.addWidget(self._value_entry)
        else:
            lab = QLabel(value_label)
            if layout == 'single':
                self._widget_layout.addWidget(lab, alignment=Qt.AlignRight)
                self._widget_layout.addWidget(self._value_entry,
                                              alignment=Qt.AlignLeft)
            else:
                value_layout = QVBoxLayout()
                value_layout.addWidget(lab, alignment=Qt.AlignBottom)
                value_layout.addWidget(self._value_entry,
                                       alignment=Qt.AlignTop)
                self._widget_layout.addLayout(value_layout)

        # Create color button widget
        from .color_button import ColorButton
        self._color_button = cb = ColorButton()
        cb.color_changed.connect(
            lambda rgba8: self._color_button_cb([c / 255.0 for c in rgba8]))
        cb.setEnabled(False)
        self._color_button_label = cbl = QLabel("Color")
        if layout == 'below':
            self._widget_layout.addWidget(self._color_button)
        else:
            if layout == 'single':
                self._widget_layout.addWidget(cbl, alignment=Qt.AlignRight)
                self._widget_layout.addWidget(cb, alignment=Qt.AlignLeft)
            else:
                color_layout = QVBoxLayout()
                color_layout.addWidget(cbl, alignment=Qt.AlignBottom)
                color_layout.addWidget(cb, alignment=Qt.AlignTop)
                self._widget_layout.addLayout(color_layout)
        self._color_button_shown = True

        # Show the histogram or the no-data label
        self.data_source = data_source

    def activate(self, markers):
        """Make the given set of markers the currently active set

           Any previously-active set will be hidden.
        """

        if markers is not None and markers not in self._markers:
            raise ValueError("activate() called with bad value")
        if markers == self._active_markers:
            return
        if self._active_markers is not None:
            self._active_markers._hide()
        elif self.layout != 'below' and self._show_marker_help:
            self._marker_help.setHidden(False)
        self._active_markers = markers
        if self._active_markers is not None:
            self._active_markers.shown = True
            self._set_sel_marker(self._active_markers._sel_marker)
        else:
            if self.layout != 'below' and self._show_marker_help:
                self._marker_help.setHidden(True)
            if self._select_callback:
                if self._prev_marker is not None:
                    self._select_callback(self._prev_markers,
                                          self._prev_marker, None, None)
                self._prev_markers = None
                self._prev_marker = None

    def add_custom_widget(self, widget, left_side=True):
        self._widget_layout.addWidget(0 if left_side else -1, widget)

    def add_markers(self, activate=True, **kw):
        """Create and return a new set of markers.

           If 'activate' is true, the new set will also become
           the active set.  Other keyword arguments will be passed
           to the HistogramMarkers constructor.
        """
        final_kw = {k: v for k, v in self._histogram_markers_kw.items()}
        final_kw.update(kw)
        final_kw['histogram'] = self
        markers = HistogramMarkers(**final_kw)
        self._markers.append(markers)
        if activate:
            self.activate(markers)
        return markers

    @property
    def color_button(self):
        return self._color_button_shown

    @color_button.setter
    def color_button(self, show):
        if show == self._color_button_shown:
            return
        if self.layout != 'below':
            self._color_button_label.setHidden(not show)
        self._color_button.setHidden(not show)
        self._color_button_shown = show

    def current_marker_info(self):
        """Identify the marker currently selected by the user.
           
           Returns a HistogramMarkers instance and a marker.
           The instance will be None if no marker set has been
           activated.  The marker will be None if no marker has
           been selected by the user.
        """
        if self._active_markers is None:
            return None, None
        return self._active_markers, self._active_markers._sel_marker

    @property
    def data_source(self):
        return self._data_source

    @data_source.setter
    def data_source(self, data_source):
        self._data_source = data_source
        self._new_data()

    def delete_markers(self, markers):
        """Delete the given set of markers.

           If the markers were active, there will be no active set
           of markers afterward.
        """
        if markers not in self._markers:
            raise ValueError("Bad value for delete()")
        if markers == self._active_markers:
            self.activate(None)
        self._markers.remove(markers)

    @property
    def layout(self):
        return self._layout

    @property
    def redraw_delay(self):
        return self._redraw_timer.interval() / 1000.0

    @redraw_delay.setter
    def redraw_delay(self, secs):
        self._redraw_timer.setInterval(secs * 1000)

    @property
    def scaling(self):
        return self._scaling

    @scaling.setter
    def scaling(self, scaling):
        if self._scaling != scaling:
            self._scaling = scaling
            self._redraw_cb()

    def snapshot_data(self):
        info = {
            'version': 1,
            'draw_min': self._draw_min,
            'draw_max': self._draw_max,
            'markers': [markers.snapshot_data() for markers in self._markers],
        }
        if self._active_markers is None:
            info['active markers'] = None
        else:
            info['active markers'] = self._markers.index(self._active_markers)
        if self['color_button']:
            info['color well'] = self._color_button.color
        else:
            info['color well'] = None
        return info

    def snapshot_restore(self, data):
        self._draw_min = data['draw_min']
        self._draw_max = data['draw_max']
        if data['color well'] is not None:
            self._color_button.color = data['color well']
        if len(data['markers']) != len(self._markers):
            # don't know how to deal with this situation
            return
        for markers, markers_data in zip(self._markers, data['markers']):
            markers.snapshot_restore(markers_data)
        if data['active markers'] is not None:
            self.activate(self._markers[data['active markers']])
            self._set_sel_marker(self._active_markers._sel_marker)

    @property
    def value_width(self):
        return self._value_width

    @value_width.setter
    def value_width(self, vw):
        self._value_width = vw
        ve = self._value_entry
        fm = ve.fontMetrics()
        tm = ve.textMargins()
        cm = ve.contentsMargins()
        w = vw * fm.width('w') + tm.left() + tm.right() + cm.left() + cm.right(
        ) + 8
        ve.setMaximumWidth(w)

    def _abs2rel(self, abs_xy):
        x, y = abs_xy
        rel_x = (x - self._min_val) / float(self._max_val - self._min_val)
        rel_y = y / float(self._ymax)
        return rel_x, rel_y

    def _abs_xy(self, scene_xy):
        scene_x, scene_y = scene_xy
        dy = min(max(self._bottom - scene_y, 0), self._hist_height - 1)
        if self.scaling == 'logarithmic':
            exp = dy / float(self._hist_height - 1)
            abs_y = (self._max_height + 1)**exp - 1
        else:
            abs_y = self._max_height * dy / float(self._hist_height - 1)

        cx = scene_x - self._border
        num_bins = len(self._bins)
        if num_bins == self._hist_width:
            fract = cx / (num_bins - 1)
            abs_x = self._min_val + fract * (self._max_val - self._min_val)
        elif num_bins == 2:
            abs_x = self._min_val + (self._max_val - self._min_val) * (
                2 * cx / self._hist_width - 0.5)
        else:
            extra = self._hist_width / (2.0 * (num_bins - 1))
            abs_x = self._min_val + (self._max_val - self._min_val) * (
                cx - extra) / (self._hist_width - 2.0 * extra)
        abs_x = max(self._min_val, abs_x)
        abs_x = min(self._max_val, abs_x)
        return abs_x, abs_y

    def _add_or_delete_marker_cb(self, event=None):
        if self._active_markers is None:
            return

        marker = self._active_markers._pick_marker(event.scenePos())

        if marker is None:
            max_marks = self._active_markers.max_marks
            if max_marks is not None and len(
                    self._active_markers) >= max_marks:
                if self.status_line:
                    self.status_line("Maximum of %d markers\n" % max_marks)
                return
            xy = self._abs_xy((event.scenePos().x(), event.scenePos().y()))
            if self._active_markers.coord_type == 'relative':
                xy = self._abs2rel(xy)
            sel_marker = self._active_markers._sel_marker
            if sel_marker:
                color = sel_marker.rgba
            else:
                color = self._active_markers.new_color
            marker = self._active_markers.append((xy, color))
            self._set_sel_marker(marker, drag_start=event)
        else:
            min_marks = self._active_markers.min_marks
            if min_marks is not None and len(
                    self._active_markers) <= min_marks:
                if self.status_line:
                    self.status_line("Minimum of %d markers\n" % min_marks)
                return
            self._active_markers.remove(marker)
            self._set_sel_marker(None)

    def _button_up_cb(self, event=None):
        if self._drag_marker:
            self._drag_marker = None
            if self._active_markers.move_callback:
                self._active_markers.move_callback('end')

    def _scene_xy(self, abs_xy):
        # minimum is in the _center_ of the first bin,
        # likewise, maximum is in the center of the last bin

        abs_x, abs_y = abs_xy

        abs_y = max(0, abs_y)
        abs_y = min(self._max_height, abs_y)
        if self.scaling == 'logarithmic':
            import math
            abs_y = math.log(abs_y + 1)
        scene_y = self._bottom - (self._hist_height - 1) * (abs_y /
                                                            self._max_height)

        abs_x = max(self._min_val, abs_x)
        abs_x = min(self._max_val, abs_x)
        num_bins = len(self._bins)
        if num_bins == self._hist_width:
            bin_width = (self._max_val - self._min_val) / float(num_bins - 1)
            left_edge = self._min_val - 0.5 * bin_width
            scene_x = int((abs_x - left_edge) / bin_width)
        else:
            # histogram is effectively one bin wider (two half-empty bins on each end)
            if num_bins == 1:
                scene_x = 0.5 * (self._hist_width - 1)
            else:
                extra = (self._max_val - self._min_val) / (2.0 *
                                                           (num_bins - 1))
                eff_min_val = self._min_val - extra
                eff_max_val = self._max_val + extra
                eff_range = float(eff_max_val - eff_min_val)
                scene_x = (self._hist_width - 1) * (abs_x -
                                                    eff_min_val) / eff_range
        return self._border + scene_x, scene_y

    def _color_button_cb(self, rgba):
        m = self._active_markers._sel_marker
        if not m:
            if self.status_line:
                self.status_line("No marker selected")
            return
        m.rgba = rgba

    def _marker2abs(self, marker):
        if self._active_markers.coord_type == 'absolute':
            return marker.xy
        else:
            return self._rel2abs(marker.xy)

    def _move_cur_marker(self, x, yy=None):
        #
        # Don't allow dragging out of the scene box.
        #
        m = self._active_markers._sel_marker
        if x < self._min_val:
            x = self._min_val
        elif x > self._max_val:
            x = self._max_val
        if yy is None:
            y = m.xy[1]
        else:
            y = yy
            if y < 0:
                y = 0
            elif y > self._ymax:
                y = self._ymax

        if self._active_markers.coord_type == 'absolute':
            m.xy = (x, y)
        else:
            m.xy = self._abs2rel((x, y))
        if yy is None:
            m.xy = (m.xy[0], y)

        self._set_value_entry(x)

        self._active_markers._update_plot()

        if self._active_markers.move_callback:
            self._active_markers.move_callback(m)

    def _move_marker_cb(self, event):
        mouse_xy = self._abs_xy((event.scenePos().x(), event.scenePos().y()))
        dx = mouse_xy[0] - self._last_mouse_xy[0]
        dy = mouse_xy[1] - self._last_mouse_xy[1]
        self._last_mouse_xy = mouse_xy

        if event.modifiers() & mod_key_info("shift")[0]:
            dx *= .1
            dy *= .1

        m = self._drag_marker
        mxy = self._marker2abs(m)
        x, y = mxy[0] + dx, mxy[1] + dy

        self._move_cur_marker(x, y)

    def _new_data(self):
        ds = self.data_source
        if isinstance(ds, str):
            self._no_histogram_label.setText(ds)
            self._data_widgets.setCurrentWidget(self._no_histogram_label)
            if self._min_label:
                self._min_label.setText("")
            if self._max_label:
                self._max_label.setText("")
            if self.layout != 'below' and self._show_marker_help:
                self._marker_help.setHidden(True)
            self._widget_area.setHidden(True)
        else:
            self._data_widgets.setCurrentWidget(self._hist_view)
            if self.layout != 'below' and self._show_marker_help:
                self._marker_help.setHidden(False)
            self._widget_area.setHidden(False)
        self._draw_min = self._draw_max = None
        self._redraw_cb()

    def _redraw(self, event=None):
        self._markable = False
        self._redraw_timer.start()

    def _redraw_cb(self):
        self._redraw_timer.stop()
        ds = self.data_source
        if isinstance(ds, str):
            # displaying a text label right now
            return
        view = self._hist_view
        scene = self._hist_scene
        hist_size = view.viewport().size()
        self._hist_width, self._hist_height = hist_width, hist_height = hist_size.width(
        ), hist_size.height()
        self._min_val, self._max_val, self._bins = ds
        filled_range = self._max_val - self._min_val
        empty_ranges = [0, 0]
        if self._draw_min != None:
            empty_ranges[0] = self._min_val - self._draw_min
            self._min_val = self._draw_min
        if self._draw_max != None:
            empty_ranges[1] = self._draw_max - self._max_val
            self._max_val = self._draw_max
        if callable(self._bins):
            if empty_ranges[0] or empty_ranges[1]:
                full_range = filled_range + empty_ranges[0] + empty_ranges[1]
                filled_bins = self._bins(
                    int(hist_width * filled_range / full_range))
                left = [0] * int(hist_width * empty_ranges[0] / full_range)
                right = [0] * (hist_width - len(filled_bins) - len(left))
                self._bins = left + filled_bins + right
            else:
                self._bins = self._bins(hist_width)
        elif empty_ranges[0] or empty_ranges[1]:
            full_range = filled_range + empty_ranges[0] + empty_ranges[1]
            left = [0] * int(len(self._bins) * empty_ranges[0] / full_range)
            right = [0] * int(len(self._bins) * empty_ranges[1] / full_range)
            self._bins = left + self._bins + right
        if self._min_label:
            self._min_label.setText(self._str_val(self._min_val))
        if self._max_label:
            self._max_label.setText(self._str_val(self._max_val))
        if not self._min_label and not self._max_label:
            self._range_label.setText(
                "%s - %s" %
                (self._str_val(self._min_val), self._str_val(self._max_val)))

        bars = self._hist_bars.childItems()
        for bar in bars:
            self._hist_bars.removeFromGroup(bar)
            self._hist_scene.removeItem(bar)

        self._ymax = max(self._bins)
        if self.scaling == 'logarithmic':
            from numpy import array, log, float32
            self._bins = array(self._bins, float32)
            self._bins += 1.0
            log(self._bins, self._bins)

        max_height = max(self._bins)
        self._max_height = max_height
        h_scale = float(hist_height - 1) / max_height
        self._border = border = 0
        bottom = hist_height + border - 1
        self._bottom = bottom

        num_bins = len(self._bins)
        if num_bins == hist_width:
            for b, n in enumerate(self._bins):
                x = border + b
                h = int(h_scale * n)
                line = self._hist_scene.addLine(x, bottom, x, bottom - h)
                self._hist_bars.addToGroup(line)
                line.setZValue(-1)  # keep bars below markers
        else:
            x_scale = (hist_width - 1) / float(num_bins)
            for b, n in enumerate(self._bins):
                x1 = border + b * x_scale
                x2 = border + (b + 1) * x_scale
                h = int(h_scale * n)
                rect = self._hist_scene.addRect(x1, bottom - h, x2 - x1, h)
                self._hist_bars.addToGroup(rect)
                rect.setZValue(-1)  # keep bars below markers
        self._markable = True
        if self._active_markers is not None:
            self._active_markers._update_plot()
            marker = self._active_markers._sel_marker
            if marker:
                self._set_value_entry(self._marker2abs(marker)[0])
        self._hist_scene.setSceneRect(self._hist_scene.itemsBoundingRect())

    def _rel2abs(self, rel_xy):
        x, y = rel_xy
        abs_x = self._min_val * (1 - x) + x * self._max_val
        abs_y = y * self._ymax
        return abs_x, abs_y

    def _select_marker_cb(self, event=None):
        if self._active_markers is not None:
            marker = self._active_markers._pick_marker(event.scenePos())
            self._set_sel_marker(marker, drag_start=event)
            if marker is not None:
                return
        # show value where histogram clicked
        self._set_value_entry(self._abs_xy((event.scenePos().x(), 0))[0])

    def _set_sel_marker(self, marker, drag_start=None):
        self._active_markers._sel_marker = marker
        if not marker:
            self._color_button.color = "gray"
            self._color_button.setEnabled(False)
            self._set_value_entry("")
            self._value_entry.setEnabled(False)
        else:
            self._color_button.setEnabled(True)
            self._color_button.color = marker.rgba
            self._value_entry.setEnabled(True)
            self._set_value_entry(self._marker2abs(marker)[0])
        if self._select_callback:
            if marker is not None or self._prev_marker is not None:
                self._select_callback(self._prev_markers, self._prev_marker,
                                      self._active_markers, marker)
            self._prev_markers = self._active_markers
            self._prev_marker = marker
        if not drag_start:
            return
        self._drag_marker = marker
        if not marker:
            return

        self._last_mouse_xy = self._abs_xy(
            (drag_start.scenePos().x(), drag_start.scenePos().y()))
        if self._active_markers.move_callback:
            self._active_markers.move_callback('start')

    def _set_value_cb(self):
        try:
            v = eval(self._value_entry.text())
        except Exception:
            raise ValueError("Invalid histogram value")
        if type(self._min_val) != type(v):
            v = type(self._min_val)(v)
        if v < self._min_val:
            self._draw_min = v
            self._redraw_cb()
        elif v > self._max_val:
            self._draw_max = v
            self._redraw_cb()
        self._move_cur_marker(v)

    def _set_value_entry(self, val):
        if isinstance(val, str):
            self._value_entry.setText(val)
            return
        if isinstance(self._min_val, int):
            val = int(val + 0.5)
        self._value_entry.setText("%g" % val)

    def _str_val(self, val):
        if isinstance(val, (int, bool)):
            return str(val)
        return "%g" % val
Ejemplo n.º 3
0
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, qApp):
        super(MainWindow, self).__init__()
        self.ui = wnd.Ui_MainWindow()  #создаём простой объект ui
        self.ui.setupUi(self)
        self.qApp = qApp
        self.scn = QGraphicsScene(self)
        self.scn.setSceneRect(0, 0, self.width() - 2, self.height() - 2)
        self.gv = QtWidgets.QGraphicsView(self)
        self.gv.setObjectName("gv")
        self.gv.setStyleSheet(
            "border-width: 0px; border-style: none; outline:0px;")
        self.gv.setRenderHint(QPainter.Antialiasing)
        self.gv.setScene(self.scn)
        self.gv.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.gv.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setCentralWidget(self.gv)
        self.gv.show()
        self.gv.mousePressEvent = self.mousePress
        self.gv.mouseMoveEvent = self.mouseMove

        set = QSettings("Tree", "config")
        x = int(set.value("Main/Left", "-1"))
        y = int(set.value("Main/Top", "-1"))
        if x > 0 and y > 0:
            self.move(x, y)
        else:
            self.move(QApplication.desktop().screen().rect().center() -
                      self.rect().center())

        self.elimg = self.scn.addPixmap(QPixmap(":/images/Tree.png"))
        self.elimg.setTransformationMode(QtCore.Qt.SmoothTransformation)
        self.elimg.setZValue(2)

        self.gv.setContextMenuPolicy(Qt.CustomContextMenu)
        self.gv.customContextMenuRequested.connect(self.initContextMenu)

        self.mx = 0
        self.my = 0
        self.ps = None
        self.lamps = []
        self.setLamps()
        self.curlight = -1
        self.snowalphach_len = self.height() / 3
        self.snows = []
        self.snowgroups = []
        self.snowing = set.value("Main/Snowing", "true") == "true"
        self.setSnows()

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.onTimer)
        self.timerinterval = 50
        self.timer.start(self.timerinterval)
        self.speed = int(set.value("Main/speed", "1"))
        self.lampturn = int(set.value("Main/turncnt", "1"))
        self.sparr = [0.1, 0.4, 0.6]
        self.cols = 6
        self.snowfallspeed = [2, 1.25, 0.8]

    def initContextMenu(self, pos):
        menu = QMenu(self)
        menu.setStyleSheet("background: #333; color: white;")

        smenu = menu.addMenu("Скорость")
        act = QAction("Медленно", self)
        act.triggered.connect(self.slowspeed)
        smenu.addAction(act)
        act = QAction("Средне", self)
        act.triggered.connect(self.midspeed)
        smenu.addAction(act)
        act = QAction("Быстро", self)
        act.triggered.connect(self.fastspeed)
        smenu.addAction(act)

        kmenu = menu.addMenu("Переключения")
        act = QAction("По 1 лампочке", self)
        act.triggered.connect(self.onelamp)
        kmenu.addAction(act)
        act = QAction("По 2 лампочки", self)
        act.triggered.connect(self.twolamp)
        kmenu.addAction(act)
        act = QAction("По 3 лампочки", self)
        act.triggered.connect(self.treelamp)
        kmenu.addAction(act)
        act = QAction("По 4 лампочки", self)
        act.triggered.connect(self.forelamp)
        kmenu.addAction(act)
        act = QAction("Снег", self)
        act.triggered.connect(self.snowOffOn)
        menu.addAction(act)

        menu.addSeparator()
        act = QAction("Закрыть", self)
        act.triggered.connect(self.close)
        menu.addAction(act)

        p = self.mapToGlobal(pos)
        p.setX(p.x() + 1)
        p.setY(p.y() + 1)
        menu.exec_(p)

    def slowspeed(self):
        self.speed = 0

    def midspeed(self):
        self.speed = 1

    def fastspeed(self):
        self.speed = 2

    def snowOffOn(self):
        #i = QtWidgets.QGraphicsItem
        self.snowing = not self.snowing
        for snows in self.snows:
            for snow in snows:
                if self.snowing:
                    snow.item.show()
                else:
                    snow.item.hide()

    def setlampsTurn(self, n):
        self.timer.stop()
        self.lampturn = n
        for lamps in self.lamps:
            for lamp in lamps:
                lamp.alpha = 0
                lamp.item.setOpacity(0)
        self.curlight = -1
        self.timer.start(self.timerinterval)

    def onelamp(self):
        self.setlampsTurn(1)

    def twolamp(self):
        self.setlampsTurn(2)

    def treelamp(self):
        self.setlampsTurn(3)

    def forelamp(self):
        self.setlampsTurn(4)

    def setLamps(self):
        '''
        pts = [[vect(22,239),vect(86,281),vect(169,256),vect(46,224),vect(122,240),vect(50,178),
            vect(122,200),vect(74,157),vect(138,149),vect(119,133),vect(90,95),vect(85,47)],
            [vect(32,255),vect(110,281),vect(181,240),vect(59,237),vect(137,236),vect(62,192),
            vect(139,192),vect(87,165),vect(65,108),vect(136,123),vect(106,98),vect(102,54)],
            [vect(49,271),vect(133,274),vect(22,191),vect(79,242),vect(153,224),vect(82,200),
            vect(156,182),vect(106,165),vect(78,125),vect(126,92),vect(150,109),vect(119,45)],
            [vect(65,278),vect(152,268),vect(32,208),vect(100,243),vect(168,217),vect(103,204),
            vect(57,149),vect(124,160),vect(97,134),vect(73,83),vect(137,77),vect(99,25)]]
        '''
        pts = [[
            vect(25, 238),
            vect(120, 277),
            vect(44, 218),
            vect(147, 236),
            vect(82, 201),
            vect(59, 133),
            vect(149, 153),
            vect(128, 125),
            vect(113, 94),
            vect(111, 63)
        ],
               [
                   vect(36, 250),
                   vect(137, 271),
                   vect(54, 232),
                   vect(161, 225),
                   vect(101, 204),
                   vect(72, 147),
                   vect(160, 137),
                   vect(141, 116),
                   vect(126, 92),
                   vect(126, 58)
               ],
               [
                   vect(51, 263),
                   vect(156, 267),
                   vect(71, 241),
                   vect(175, 215),
                   vect(121, 202),
                   vect(87, 158),
                   vect(55, 95),
                   vect(158, 105),
                   vect(134, 80),
                   vect(93, 32)
               ],
               [
                   vect(64, 275),
                   vect(172, 256),
                   vect(88, 246),
                   vect(43, 157),
                   vect(140, 199),
                   vect(100, 169),
                   vect(74, 114),
                   vect(73, 66),
                   vect(149, 71),
                   vect(108, 40)
               ],
               [
                   vect(80, 277),
                   vect(187, 245),
                   vect(108, 245),
                   vect(51, 178),
                   vect(154, 192),
                   vect(116, 167),
                   vect(89, 123),
                   vect(86, 79),
                   vect(89, 47),
                   vect(121, 34)
               ],
               [
                   vect(101, 279),
                   vect(28, 198),
                   vect(129, 244),
                   vect(66, 191),
                   vect(171, 179),
                   vect(135, 161),
                   vect(108, 127),
                   vect(99, 88),
                   vect(100, 59),
                   vect(108, 17)
               ]]
        clrs = [["red1", "red2", "red3", "red4", "red5"],
                ["aqua1", "aqua2", "aqua2", "aqua4", "aqua5"],
                ["fuji1", "fuji2", "fuji3", "fuji4", "fuji5"],
                ["green1", "green2", "green3", "green4", "green5"],
                ["blue1", "blue2", "blue3", "blue4", "blue5"],
                ["yellow1", "yellow2", "yellow3", "yellow4", "yellow5"]]
        cv = 0
        for c in range(len(pts)):
            lamps = []
            for v in pts[c]:
                lamp = Lamp(self.scn, ":/images/" + clrs[c][cv] + ".png", v.y,
                            v.x)
                lamp.item.setZValue(4)
                lamps.append(lamp)
                cv += 1
                if cv > 4: cv = 0
            self.lamps.append(lamps)

    def setSnows(self):
        maxminsnows = 50
        sc = [
            random.randint(0, 14) + maxminsnows,
            random.randint(0, 14) + maxminsnows,
            random.randint(0, 14) + maxminsnows
        ]

        for i in range(len(sc)):
            snows = []
            items = []
            for j in range(sc[i]):
                imgfn = ":/images/snow" + str(i + 1) + ".png"
                x = random.randint(8, self.width() - 8)
                y = random.randint(10, self.height())
                a = 1
                hl = self.height() - self.snowalphach_len
                if y > hl:
                    a = 1 - (y - hl) / self.snowalphach_len
                lamp = Lamp(self.scn, imgfn, x, y, a)
                if not self.snowing:
                    lamp.item.hide()
                snows.append(lamp)
                items.append(lamp.item)
            group = self.scn.createItemGroup(items)
            if i < 2:
                group.setZValue(4)
            else:
                group.setZValue(0)

            self.snows.append(snows)
            self.snowgroups.append(items)

    def onTimer(self):
        finished = False
        nl = 0
        if self.curlight == self.cols - 1: nl = 0
        else: nl = self.curlight + 1

        for lamp in self.lamps[nl]:
            lamp.alpha += self.sparr[self.speed]
            if lamp.alpha >= 1.0:
                lamp.alpha = 1.0
                finished = True
            lamp.item.setOpacity(lamp.alpha)
        #for t in range(self.lampturn):
        if self.curlight >= 0:
            n = self.curlight - self.lampturn + 1
            if n < 0: n = self.cols + n
            for lamp in self.lamps[n]:
                if lamp.alpha > 0:
                    lamp.alpha -= self.sparr[self.speed]
                    if lamp.alpha < 0: lamp.alpha = 0
                    lamp.item.setOpacity(lamp.alpha)
        if finished:
            self.curlight += 1
            if self.curlight >= self.cols: self.curlight = 0

        if self.snowing:
            for i in range(len(self.snows)):
                for snow in self.snows[i]:
                    a = snow.alpha
                    snow.y += self.snowfallspeed[i]
                    if snow.y > self.height():
                        snow.y = -10 - random.randint(0, 15)
                        snow.x = random.randint(8, self.width() - 8)
                        snow.alpha = 1
                    else:
                        hl = self.height() - self.snowalphach_len
                        if snow.y > hl:
                            snow.alpha = 1 - (snow.y -
                                              hl) / self.snowalphach_len
                    snow.item.setPos(
                        QPointF(snow.x - snow.hw, snow.y - snow.hh))
                    if snow.alpha != a:
                        snow.item.setOpacity(snow.alpha)

    def mousePress(self, event):
        """Событие нажатия мыши на QGraphicsView,
           в данной функции  переменным присваивается глобальная
           позиция мыши и позиция окна, для того, чтобы при
           перемещении мыши вызывать перемещение главного окна."""
        self.mx = event.globalPos().x()
        self.my = event.globalPos().y()
        self.ps = self.pos()

    def mouseMove(self, event):
        """Событие перемещения мыши на QGraphicsView,
           в данной функции перемещаем главное окно по экрану."""
        x = event.globalPos().x() - self.mx
        y = event.globalPos().y() - self.my
        self.move(self.ps.x() + x, self.ps.y() + y)

    def closeEvent(self, event):
        set = QSettings("Tree", "config")
        p = self.pos()
        set.setValue("Main/Left", p.x())
        set.setValue("Main/Top", p.y())
        set.setValue("Main/Snowing", self.snowing)
        set.setValue("Main/speed", self.speed)
        set.setValue("Main/turncnt", self.lampturn)
        self.qApp.quit()
Ejemplo n.º 4
0
class View(QGraphicsView):

    projections = OrderedDict([
        ('Spherical', Proj('+proj=ortho +lat_0=48 +lon_0=17')),
        ('Mercator', Proj(init='epsg:3395')),
        ('WGS84', Proj(init='epsg:3857')),
        ('ETRS89 - LAEA Europe', Proj("+init=EPSG:3035"))
    ])

    def __init__(self, controller):
        super().__init__()
        self.controller = controller
        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setRenderHint(QPainter.Antialiasing)
        self.proj = 'Spherical'
        self.ratio, self.offset = 1 / 400, (0, 0)
        self.display = True
        self.shapefile = join(controller.path_shapefiles,
                              'World countries_1.shp')

        # brush for water and lands
        self.water_brush = QBrush(QColor(64, 164, 223))
        self.land_brush = QBrush(QColor(52, 165, 111))
        self.land_pen = QPen(QColor(0, 0, 0))

        # draw the map
        self.polygons = self.scene.createItemGroup(self.draw_polygons())
        self.draw_water()

        # set of graphical nodes
        self.nodes = set()

    ## Zoom system

    def zoom_in(self):
        self.scale(1.25, 1.25)

    def zoom_out(self):
        self.scale(1 / 1.25, 1 / 1.25)

    def wheelEvent(self, event):
        self.zoom_in() if event.angleDelta().y() > 0 else self.zoom_out()

    ## Mouse bindings

    def mouseMoveEvent(self, event):
        # sliding the scrollbar with the right-click button
        if event.buttons() == Qt.RightButton:
            self.trigger_menu = False
            offset = self.cursor_pos - event.pos()
            self.cursor_pos = event.pos()
            x_value = self.horizontalScrollBar().value() + offset.x()
            y_value = self.verticalScrollBar().value() + offset.y()
            self.horizontalScrollBar().setValue(x_value)
            self.verticalScrollBar().setValue(y_value)
        super().mouseMoveEvent(event)

    def mousePressEvent(self, event):
        # activate rubberband for selection
        # by default, the rubberband is active for both clicks, we have to
        # deactivate it explicitly for the right-click
        if event.buttons() == Qt.LeftButton:
            self.setDragMode(QGraphicsView.RubberBandDrag)
        if event.button() == Qt.RightButton:
            self.cursor_pos = event.pos()
        super().mousePressEvent(event)

    ## Drag & Drop system

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            event.acceptProposedAction()

    dragMoveEvent = dragEnterEvent

    def dropEvent(self, event):
        pos = self.mapToScene(event.pos())
        geo_pos = self.to_geographical_coordinates(pos.x(), pos.y())
        if event.mimeData().hasFormat('application/x-dnditemdata'):
            new_node = Node(self.controller, pos)

    ## Map functions

    def to_geographical_coordinates(self, x, y):
        px, py = (x - self.offset[0]) / self.ratio, (self.offset[1] -
                                                     y) / self.ratio
        return self.projections[self.proj](px, py, inverse=True)

    def to_canvas_coordinates(self, longitude, latitude):
        px, py = self.projections[self.proj](longitude, latitude)
        return px * self.ratio + self.offset[
            0], -py * self.ratio + self.offset[1]

    def move_to_geographical_coordinates(self):
        for node in self.nodes:
            node.setPos(
                QPointF(*self.to_canvas_coordinates(node.longitude,
                                                    node.latitude)))

    def draw_polygons(self):
        sf = shapefile.Reader(self.shapefile)
        polygons = sf.shapes()
        for polygon in polygons:
            # convert shapefile geometries into shapely geometries
            # to extract the polygons of a multipolygon
            polygon = shapely.geometry.shape(polygon)
            # if it is a polygon, we use a list to make it iterable
            if polygon.geom_type == 'Polygon':
                polygon = [polygon]
            for land in polygon:
                qt_polygon = QPolygonF()
                for lon, lat in land.exterior.coords:
                    px, py = self.to_canvas_coordinates(lon, lat)
                    if px > 1e+10:
                        continue
                    qt_polygon.append(QPointF(px, py))
                polygon_item = QGraphicsPolygonItem(qt_polygon)
                polygon_item.setBrush(self.land_brush)
                polygon_item.setPen(self.land_pen)
                polygon_item.setZValue(1)
                yield polygon_item

    def draw_water(self):
        if self.proj in ('Spherical', 'ETRS89 - LAEA Europe'):
            cx, cy = self.to_canvas_coordinates(17, 48)
            # if the projection is ETRS89, we need the diameter and not the radius
            R = 6371000 * self.ratio * (1 if self.proj == 'Spherical' else 2)
            earth_water = QGraphicsEllipseItem(cx - R, cy - R, 2 * R, 2 * R)
            earth_water.setZValue(0)
            earth_water.setBrush(self.water_brush)
            self.polygons.addToGroup(earth_water)
        else:
            # we compute the projected bounds of the Mercator (3395) projection
            # upper-left corner x and y coordinates:
            ulc_x, ulc_y = self.to_canvas_coordinates(-180, 84)
            # lower-right corner x and y coordinates
            lrc_x, lrc_y = self.to_canvas_coordinates(180, -84.72)
            # width and height of the map (required for the QRectItem)
            width, height = lrc_x - ulc_x, lrc_y - ulc_y
            earth_water = QGraphicsRectItem(ulc_x, ulc_y, width, height)
            earth_water.setZValue(0)
            earth_water.setBrush(self.water_brush)
            self.polygons.addToGroup(earth_water)

    def show_hide_map(self):
        self.display = not self.display
        self.polygons.show() if self.display else self.polygons.hide()

    def delete_map(self):
        self.scene.removeItem(self.polygons)

    def redraw_map(self):
        self.delete_map()
        self.polygons = self.scene.createItemGroup(self.draw_polygons())
        self.draw_water()
        # replace the nodes at their geographical location
        self.move_to_geographical_coordinates()
Ejemplo n.º 5
0
class Editor(QWidget):
    def __init__(self, song):
        super().__init__()

        self.initUI(song)

    def initUI(self, song):
        self.songLenInBeats = 100
        self.songBeatsPBar = 4
        self.reverse = 1
        self.pixPSec = 1000.0
        self.disp8 = True
        self.disp12 = False
        self.disp16 = True
        self.disp24 = False
        self.disp32 = False
        self.disp48 = False
        self.disp64 = False
        self.spectrogramDisplay = True
        self.cursorExists = False
        self.framecount = 0
        self.timeOutLength = 17

        self.timer = QTimer()
        self.editorTheme = self.getTheme()
        self.boxw = self.editorTheme['BoxWidth']
        self.topLayout = QVBoxLayout()
        self.topLayout.setContentsMargins(0, 0, 0, 0)

        self.song = song
        self.song.pos = 0
        self.setLayout(self.topLayout)
        self.gs = QGraphicsScene()
        self.gv = QGraphicsView(self.gs)
        self.drawBG()
        self.song = song
        #        self.drawArrowDemo()
        self.boxes = []
        self.topLayout.addWidget(self.gv)

    def update(self, song, level):
        self.gs.clear()
        print(self.song.pos)
        self.song = song
        self.cursorExists = False
        self.song.pos = 0
        self.play(0)
        self.pause()
        self.updatescreen()

        if self.spectrogramDisplay:
            self.spectrogramPixMap = QPixmap(spectrogram_dir + song.audioFile +
                                             '.png')
            width = self.spectrogramPixMap.width()
            height = self.spectrogramPixMap.height()

            self.spectrogramPixMap = self.gs.addPixmap(self.spectrogramPixMap)
            self.spectrogramPixMap.setRotation(90)
            self.spectrogramPixMap.setTransform(QTransform().scale(
                -1, (self.song.lengthInSeconds * self.pixPSec) / width))
        self.drawGrid(self.song.levels[level])
        self.drawArrowDemo(self.song.levels[level])
        #self.play(0)

    def levelSelected(self, level):
        print("Selected ", level)
        if level in self.song.levels:
            print('switching')
            self.update(self.song, level)
        else:
            print('creating new level')

    def play(self, pos):
        self.timer.timeout.connect(self.updatescreen)
        self.timer.start(self.timeOutLength)
        self.song.playSong(pos)

    def pause(self):
        self.timer.stop()
        self.song.pauseSong()

    def keyPressEvent(self, event):
        key = event.key()
        shiftPressed = event.modifiers() == Qt.ShiftModifier
        ctrlPressed = event.modifiers() == Qt.ControlModifier
        altPressed = event.modifiers() == Qt.AltModifier
        restart = self.song.isPlaying

        if key == Qt.Key_Space:
            if self.song.isPlaying:
                self.pause()
            else:
                self.play(self.song.pos / 1000)
        elif key == Qt.Key_BracketRight:
            restart = self.song.isPlaying
            self.pause()
            if ctrlPressed:
                self.song.pos += 10000
            elif shiftPressed:
                self.song.pos += 1000
            elif altPressed:
                self.song.pos += 10
            else:
                self.song.pos += 100

            if not (self.song.pos > self.song.lengthInSeconds * 1000):
                if restart:
                    self.play(self.song.pos / 1000)
                else:
                    self.play(self.song.pos / 1000)
                    self.pause()
            else:
                self.song.pos = self.song.lengthInSeconds * 1000 - 1
                self.play(self.song.pos / 1000)
                self.pause()
            self.updatescreen()
        elif key == Qt.Key_BracketLeft:
            self.pause()
            if ctrlPressed:
                self.song.pos -= 10000
            elif shiftPressed:
                self.song.pos -= 1000
            elif altPressed:
                self.song.pos -= 10
            else:
                self.song.pos -= 100
            if self.song.pos < 0:
                self.song.pos = 0
            if restart:
                self.play(self.song.pos / 1000)
            else:
                self.play(self.song.pos / 1000)
                self.pause()
            self.updatescreen()
        elif key == Qt.Key_BraceRight:
            restart = self.song.isPlaying
            self.pause()
            if ctrlPressed:
                self.song.pos += 10000
            elif shiftPressed:
                self.song.pos += 1000
            elif altPressed:
                self.song.pos += 10
            else:
                self.song.pos += 200

            if not (self.song.pos > self.song.lengthInSeconds * 1000):
                if restart:
                    self.play(self.song.pos / 1000)
                else:
                    self.play(self.song.pos / 1000)
                    self.pause()
            else:
                self.song.pos = self.song.lengthInSeconds * 1000 - 1
                self.play(self.song.pos / 1000)
                self.pause()

            self.updatescreen()
        elif key == Qt.Key_BraceLeft:
            self.pause()
            if ctrlPressed:
                self.song.pos -= 10000
            elif shiftPressed:
                self.song.pos -= 1000
            elif altPressed:
                self.song.pos += 10
            else:
                self.song.pos -= 200
            if self.song.pos < 0:
                self.song.pos = 0
            if restart:
                self.play(self.song.pos / 1000)
            else:
                self.play(self.song.pos / 1000)
                self.pause()

            self.updatescreen()

    def updatescreen(self):
        self.song.updatePos()

        if self.song.isPlaying:
            self.framecount += 1
            curTime = time.time()
            if (curTime - self.song.time > 1):
                print('FPS: ', self.framecount)
                self.framecount = 0
                self.song.time = curTime

        if self.cursorExists:
            self.gs.removeItem(self.cursorLine)
        ypos = (self.song.pos / 1000.0 * self.pixPSec)
        self.gv.centerOn(0, ypos)
        self.cursorLine = self.gs.addLine(0, ypos, 1000, ypos,
                                          self.editorTheme['GridMeasure'])
        self.cursorExists = True
        self.cursor = True
        if self.song.pos < 0:
            self.song.pos = 0
            self.gv.centerOn(0, 0)
            self.pause()

    def drawArrowDemo(self, level):

        boxRotation = [180, 0, 90, 270, 225, 135, 315, 45, 0]

        for beatBox in level.notes:
            if beatBox.type == 0:
                if beatBox.cutDirection == 8:
                    notePixmap = QPixmap(graphics_dir + 'redcircle.png')
                else:
                    notePixmap = QPixmap(graphics_dir + 'redarrow.png')
            elif beatBox.type == 1:
                if beatBox.cutDirection == 8:
                    notePixmap = QPixmap(graphics_dir + 'bluecircle.png')
                else:
                    notePixmap = QPixmap(graphics_dir + 'bluearrow.png')
            else:
                notePixmap = QPixmap(graphics_dir + 'mine.png')

            notePixmap = notePixmap.scaled(40, 40)
            noteBox = NoteBox()
            noteBox.setPixmap(notePixmap)
            noteBox.setBox(beatBox)
            box = self.gs.addItem(noteBox)
            print(type(noteBox))
            noteBox.setTransformOriginPoint(20, 20)
            noteBox.setRotation(boxRotation[beatBox.cutDirection])
            boxy = (level.beatToSec(beatBox.time) * self.pixPSec -
                    (self.reverse * 20)) * self.reverse

            boxx = 40 * beatBox.lineIndex + 170 * beatBox.lineLayer
            noteBox.setPos(boxx, boxy)
            self.boxes.append(noteBox)

    def drawBG(self):
        self.gs.setBackgroundBrush(self.editorTheme['BG'])

    def drawGrid(self, level):
        #DEBUG need to through a clear grid in here

        self.drawGridConstantTime(level)

    def drawGridConstantBeat(self):
        pass

    def drawGridConstantTime(self, level):

        # DONT FORGET TO ADD REVERSE SCROLL

        #        self.disp192 = True
        self.noteLayer1 = self.gs.createItemGroup([])
        self.noteLayer2 = self.gs.createItemGroup([])
        self.noteLayer3 = self.gs.createItemGroup([])
        self.obstacleLayer = self.gs.createItemGroup([])
        self.eventLayer = self.gs.createItemGroup([])
        width = self.editorTheme['BoxWidth'] * 4
        spacing = self.editorTheme['LaneSpacing']
        self.noteLayer1Values = LayerValue(0, 0, width)
        self.noteLayer2Values = LayerValue((width + spacing), 0, width)
        self.noteLayer3Values = LayerValue((width + spacing) * 2, 0, width)
        self.obstacleLayerValues = LayerValue((width + spacing) * 3, 0, width)
        self.eventLayerValues = LayerValue((width + spacing) * 4, 0, width)

        self.drawGridLaneConstantTime(self.noteLayer1, self.noteLayer1Values,
                                      level)
        self.drawGridLaneConstantTime(self.noteLayer2, self.noteLayer2Values,
                                      level)
        self.drawGridLaneConstantTime(self.noteLayer3, self.noteLayer3Values,
                                      level)
        self.drawGridLaneConstantTime(self.obstacleLayer,
                                      self.obstacleLayerValues, level)
        self.drawGridLaneConstantTime(self.eventLayer, self.eventLayerValues,
                                      level)

    def drawGridLaneConstantTime(self, lane, values, level):
        #debug songlen not a int need to address leftover time
        for beat in range(int(self.song.lengthInBeats)):
            if beat % self.song.beatsPerBar == 0:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat)) *
                        self.pixPSec, self.editorTheme['GridMeasure']))
            else:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat)) *
                        self.pixPSec, self.editorTheme['Grid4']))
            if self.disp8:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5)) *
                        self.pixPSec, self.editorTheme['Grid8']))
            if self.disp16:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .25)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .25)) *
                        self.pixPSec, self.editorTheme['Grid16']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .75)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .75)) *
                        self.pixPSec, self.editorTheme['Grid16']))
            if self.disp32:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .125)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .125)) *
                        self.pixPSec, self.editorTheme['Grid32']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .375)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .375)) *
                        self.pixPSec, self.editorTheme['Grid32']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .625)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .625)) *
                        self.pixPSec, self.editorTheme['Grid32']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .875)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .875)) *
                        self.pixPSec, self.editorTheme['Grid32']))
            if self.disp64:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .0625)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .0625)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .1875)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .1875)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .3125)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .3125)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .4375)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .4375)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5625)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5625)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .6875)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .6875)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .8125)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .8125)) *
                        self.pixPSec, self.editorTheme['Grid64']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .9375)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .9375)) *
                        self.pixPSec, self.editorTheme['Grid64']))
            if self.disp12:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 3)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 3)) *
                        self.pixPSec, self.editorTheme['Grid12']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 2 / 3)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 2 / 3)) *
                        self.pixPSec, self.editorTheme['Grid12']))
            if self.disp24:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 6)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 6)) *
                        self.pixPSec, self.editorTheme['Grid24']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .5)) *
                        self.pixPSec, self.editorTheme['Grid24']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 5 / 6)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 5 / 6)) *
                        self.pixPSec, self.editorTheme['Grid24']))
            if self.disp48:
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 12)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 1 / 12)) *
                        self.pixPSec, self.editorTheme['Grid48']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .25)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .25)) *
                        self.pixPSec, self.editorTheme['Grid48']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 5 / 12)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 5 / 12)) *
                        self.pixPSec, self.editorTheme['Grid48']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 7 / 12)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 7 / 12)) *
                        self.pixPSec, self.editorTheme['Grid48']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .75)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + .75)) *
                        self.pixPSec, self.editorTheme['Grid48']))
                lane.addToGroup(
                    self.gs.addLine(
                        values.x + self.editorTheme['GridLineWidth'],
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 11 / 12)) *
                        self.pixPSec, values.x + values.w -
                        self.editorTheme['GridLineWidth'] * 2,
                        self.reverse *
                        (self.song.offset + level.beatToSec(beat + 11 / 12)) *
                        self.pixPSec, self.editorTheme['Grid48']))
        lane.addToGroup(
            self.gs.addLine(
                values.x, values.y, values.x,
                self.reverse *
                (self.song.offset + level.beatToSec(self.song.lengthInBeats)) *
                self.pixPSec, self.editorTheme['GridLayer1Vert']))
        lane.addToGroup(
            self.gs.addLine(
                values.x + values.w * .25, values.y, values.x + values.w * .25,
                self.reverse *
                (self.song.offset + level.beatToSec(self.song.lengthInBeats)) *
                self.pixPSec, self.editorTheme['GridLayer1Vert']))
        lane.addToGroup(
            self.gs.addLine(
                values.x + values.w * .5, values.y, values.x + values.w * .5,
                self.reverse *
                (self.song.offset + level.beatToSec(self.song.lengthInBeats)) *
                self.pixPSec, self.editorTheme['GridLayer1Vert']))
        lane.addToGroup(
            self.gs.addLine(
                values.x + values.w * .75, values.y, values.x + values.w * .75,
                self.reverse *
                (self.song.offset + level.beatToSec(self.song.lengthInBeats)) *
                self.pixPSec, self.editorTheme['GridLayer1Vert']))
        lane.addToGroup(
            self.gs.addLine(
                values.x + values.w, values.y, values.x + values.w,
                self.reverse *
                (self.song.offset + level.beatToSec(self.song.lengthInBeats)) *
                self.pixPSec, self.editorTheme['GridLayer1Vert']))

    def getTheme(self):
        return {
            'BoxWidth': 60,
            'LaneSpacing': 20,
            'BG': QBrush(QColor(0, 0, 0), Qt.SolidPattern),
            'GridLayer1Vert': QPen(QBrush(QColor(255, 255, 255)), 1,
                                   Qt.SolidLine),
            'GridLayer1BG': QBrush(Qt.black, Qt.SolidPattern),
            'GridLayer2Vert': QPen(Qt.white, Qt.SolidLine),
            'GridLayer2BG': QBrush(Qt.black, Qt.SolidPattern),
            'GridLayer3Vert': QPen(Qt.white, Qt.SolidLine),
            'GridLayer3BG': QBrush(Qt.black, Qt.SolidPattern),
            'GridObs': QPen(Qt.blue, Qt.SolidLine),
            'GridObsBG': QBrush(Qt.black, Qt.SolidPattern),
            'GridEventVert': QPen(Qt.red, Qt.SolidLine),
            'GridEventBG': QBrush(Qt.black, Qt.SolidPattern),
            'GridMeasure': QPen(QBrush(QColor(255, 0, 0)), 2, Qt.SolidLine),
            'Grid4': QPen(QBrush(QColor(255, 255, 255)), 2, Qt.DashLine),
            'Grid8': QPen(QBrush(QColor(0, 150, 255)), 2, Qt.DotLine),
            'Grid12': QPen(QBrush(QColor(100, 255, 50)), 2, Qt.DotLine),
            'Grid16': QPen(QBrush(QColor(255, 255, 50)), 2, Qt.DotLine),
            'Grid24': QPen(QBrush(QColor(150, 100, 255)), 2, Qt.DotLine),
            'Grid32': QPen(QBrush(QColor(0, 255, 150)), 2, Qt.DotLine),
            'Grid48': QPen(QBrush(QColor(255, 100, 150)), 2, Qt.DotLine),
            'Grid64': QPen(QBrush(QColor(150, 200, 100)), 2, Qt.DotLine),
            #                    'Grid192': QPen(Qt.red,Qt.SolidLine),
            'GridLineWidth': 1
        }