text: 
  try:
      from PyQt5.QtGui import *
      from PyQt5.QtCore import *
      from PyQt5.QtWidgets import *
  except ImportError:
      from PyQt4.QtGui import *
      from PyQt4.QtCore import *
  
  # from PyQt4.QtOpenGL import *
  import math
  from libs.shape import Shape
  from libs.utils import distance, calc_shib, calc_distance, calc_extra_points
  
  CURSOR_DEFAULT = Qt.ArrowCursor
  CURSOR_POINT = Qt.PointingHandCursor
  CURSOR_DRAW = Qt.CrossCursor
  CURSOR_MOVE = Qt.ClosedHandCursor
  CURSOR_GRAB = Qt.OpenHandCursor
  
  # class Canvas(QGLWidget):
  
  
  class Canvas(QWidget):
      zoomRequest = pyqtSignal(int)
      scrollRequest = pyqtSignal(int, int)
      newShape = pyqtSignal()
      selectionChanged = pyqtSignal(bool)
      shapeMoved = pyqtSignal()
      drawingPolygon = pyqtSignal(bool)
  
      CREATE, EDIT = list(range(2))
  
      epsilon = 11.0
  
      def __init__(self, *args, **kwargs):
          super(Canvas, self).__init__(*args, **kwargs)
          # Initialise local state.
          self.mode = self.EDIT
          self.shapes = []
          self.current = None
          self.selected_shape = None  # save the selected shape here
          self.selected_shape_copy = None
          self.drawing_line_color = QColor(0, 0, 255)
          self.drawing_rect_color = QColor(0, 0, 255)
          self.line = Shape(line_color=self.drawing_line_color)
          self.prev_point = QPointF()
          self.offsets = QPointF(), QPointF()
          self.scale = 1.0
          self.label_font_size = 8
          self.pixmap = QPixmap()
          self.visible = {}
          self._hide_background = False
          self.hide_background = False
          self.h_shape = None
          self.h_vertex = None
          self._painter = QPainter()
          self._cursor = CURSOR_DEFAULT
          # Menus:
          self.menus = (QMenu(), QMenu())
          # Set widget options.
          self.setMouseTracking(True)
          self.setFocusPolicy(Qt.WheelFocus)
          self.verified = False
          self.draw_square = False
  
          # initialisation for panning
          self.pan_initial_pos = QPoint()
  
      def set_drawing_color(self, qcolor):
          self.drawing_line_color = qcolor
          self.drawing_rect_color = qcolor
  
      def enterEvent(self, ev):
          self.override_cursor(self._cursor)
  
      def leaveEvent(self, ev):
          self.restore_cursor()
  
      def focusOutEvent(self, ev):
          self.restore_cursor()
  
      def isVisible(self, shape):
          return self.visible.get(shape, True)
  
      def drawing(self):
          return self.mode == self.CREATE
  
      def editing(self):
          return self.mode == self.EDIT
  
      def set_editing(self, value=True):
          self.mode = self.EDIT if value else self.CREATE
          if not value:  # Create
              self.un_highlight()
              self.de_select_shape()
          self.prev_point = QPointF()
          self.repaint()
  
      def un_highlight(self):
          if self.h_shape:
              self.h_shape.highlight_clear()
          self.h_vertex = self.h_shape = None
  
      def selected_vertex(self):
          return self.h_vertex is not None
  
      def mouseMoveEvent(self, ev):
          """Update line with last point and current coordinates."""
          pos = self.transform_pos(ev.pos())
  
          # Update coordinates in status bar if image is opened
          window = self.parent().window()
          if window.file_path is not None:
              self.parent().window().label_coordinates.setText(
                  'X: %d; Y: %d' % (pos.x(), pos.y()))
  
          # Polygon drawing.
          if self.drawing():
              self.override_cursor(CURSOR_DRAW)
              if self.current:
                  # Display annotation width and height while drawing
                  current_width = abs(self.current[0].x() - pos.x())
                  current_height = abs(self.current[0].y() - pos.y())
                  self.parent().window().label_coordinates.setText(
                      'Width: %d, Height: %d / X: %d; Y: %d' % (current_width, current_height, pos.x(), pos.y()))
  
                  color = self.drawing_line_color
                  if self.out_of_pixmap(pos):
                      # Don't allow the user to draw outside the pixmap.
                      # Clip the coordinates to 0 or max,
                      # if they are outside the range [0, max]
                      size = self.pixmap.size()
                      clipped_x = min(max(0, pos.x()), size.width())
                      clipped_y = min(max(0, pos.y()), size.height())
                      pos = QPointF(clipped_x, clipped_y)
                  elif len(self.current) > 1 and self.close_enough(pos, self.current[0]):
                      # Attract line to starting point and colorise to alert the
                      # user:
                      pos = self.current[0]
                      color = self.current.line_color
                      self.override_cursor(CURSOR_POINT)
                      self.current.highlight_vertex(0, Shape.NEAR_VERTEX)
  
                  if self.draw_square:
                      init_pos = self.current[0]
                      min_x = init_pos.x()
                      min_y = init_pos.y()
                      min_size = min(abs(pos.x() - min_x), abs(pos.y() - min_y))
                      direction_x = -1 if pos.x() - min_x < 0 else 1
                      direction_y = -1 if pos.y() - min_y < 0 else 1
                      self.line[1] = QPointF(
                          min_x + direction_x * min_size, min_y + direction_y * min_size)
                  else:
                      self.line[1] = pos
  
                  self.line.line_color = color
                  self.prev_point = QPointF()
                  self.current.highlight_clear()
              else:
                  self.prev_point = pos
              self.repaint()
              return
  
          # Polygon copy moving.
          if Qt.RightButton & ev.buttons():
              if self.selected_shape_copy and self.prev_point:
                  self.override_cursor(CURSOR_MOVE)
                  self.bounded_move_shape(self.selected_shape_copy, pos)
                  self.repaint()
              elif self.selected_shape:
                  self.selected_shape_copy = self.selected_shape.copy()
                  self.repaint()
              return
  
          # Polygon/Vertex moving.
          if Qt.LeftButton & ev.buttons():
              if self.selected_vertex():
                  self.bounded_move_vertex(pos)
                  self.shapeMoved.emit()
                  self.repaint()
              elif self.selected_shape and self.prev_point:
                  self.override_cursor(CURSOR_MOVE)
                  self.bounded_move_shape(self.selected_shape, pos)
                  self.shapeMoved.emit()
                  self.repaint()
              else:
                  # pan
                  delta_x = pos.x() - self.pan_initial_pos.x()
                  delta_y = pos.y() - self.pan_initial_pos.y()
                  self.scrollRequest.emit(delta_x, Qt.Horizontal)
                  self.scrollRequest.emit(delta_y, Qt.Vertical)
                  self.update()
              return
  
          # Just hovering over the canvas, 2 possibilities:
          # - Highlight shapes
          # - Highlight vertex
          # Update shape/vertex fill and tooltip value accordingly.
          self.setToolTip("Image")
          for shape in reversed([s for s in self.shapes if self.isVisible(s)]):
              # Look for a nearby vertex to highlight. If that fails,
              # check if we happen to be inside a shape.
              index = shape.nearest_vertex(pos, self.epsilon)
              if index is not None:
                  if self.selected_vertex():
                      self.h_shape.highlight_clear()
                  self.h_vertex, self.h_shape = index, shape
                  shape.highlight_vertex(index, shape.MOVE_VERTEX)
                  self.override_cursor(CURSOR_POINT)
                  self.setToolTip("Click & drag to move point")
                  self.setStatusTip(self.toolTip())
                  self.update()
                  break
              elif shape.contains_point(pos):
                  if self.selected_vertex():
                      self.h_shape.highlight_clear()
                  self.h_vertex, self.h_shape = None, shape
                  self.setToolTip(
                      "Click & drag to move shape '%s'" % shape.label)
                  self.setStatusTip(self.toolTip())
                  self.override_cursor(CURSOR_GRAB)
                  self.update()
                  break
          else:  # Nothing found, clear highlights, reset state.
              if self.h_shape:
                  self.h_shape.highlight_clear()
                  self.update()
              self.h_vertex, self.h_shape = None, None
              self.override_cursor(CURSOR_DEFAULT)
  
      def mousePressEvent(self, ev):
          pos = self.transform_pos(ev.pos())
  
          if ev.button() == Qt.LeftButton:
              if self.drawing():
                  self.handle_drawing(pos)
              else:
                  selection = self.select_shape_point(pos)
                  self.prev_point = pos
  
                  if selection is None:
                      # pan
                      QApplication.setOverrideCursor(QCursor(Qt.OpenHandCursor))
                      self.pan_initial_pos = pos
  
          elif ev.button() == Qt.RightButton and self.editing():
              self.select_shape_point(pos)
              self.prev_point = pos
          self.update()
  
      def mouseReleaseEvent(self, ev):
          if ev.button() == Qt.RightButton:
              menu = self.menus[bool(self.selected_shape_copy)]
              self.restore_cursor()
              if not menu.exec_(self.mapToGlobal(ev.pos()))\
                 and self.selected_shape_copy:
                  # Cancel the move by deleting the shadow copy.
                  self.selected_shape_copy = None
                  self.repaint()
          elif ev.button() == Qt.LeftButton and self.selected_shape:
              if self.selected_vertex():
                  self.override_cursor(CURSOR_POINT)
              else:
                  self.override_cursor(CURSOR_GRAB)
          elif ev.button() == Qt.LeftButton:
              pos = self.transform_pos(ev.pos())
              if self.drawing():
                  self.handle_drawing(pos)
              else:
                  # pan
                  QApplication.restoreOverrideCursor()
  
      def end_move(self, copy=False):
          assert self.selected_shape and self.selected_shape_copy
          shape = self.selected_shape_copy
          # del shape.fill_color
          # del shape.line_color
          if copy:
              self.shapes.append(shape)
              self.selected_shape.selected = False
              self.selected_shape = shape
              self.repaint()
          else:
              self.selected_shape.points = [p for p in shape.points]
          self.selected_shape_copy = None
  
      def hide_background_shapes(self, value):
          self.hide_background = value
          if self.selected_shape:
              # Only hide other shapes if there is a current selection.
              # Otherwise the user will not be able to select a shape.
              self.set_hiding(True)
              self.repaint()
  
      def handle_drawing(self, pos):
          if self.current and self.current.reach_max_points() is False:
              init_pos = self.current[0]
              target_pos = self.line[1]
              p1, p2 = calc_extra_points(-1/(calc_shib(init_pos, target_pos)),
                                         init_pos, max_d=calc_distance(init_pos, target_pos) / 2)
              self.current.add_point(p1)
              self.current.add_point(target_pos)
              self.current.add_point(p2)
              self.finalise()
          elif not self.out_of_pixmap(pos):
              self.current = Shape()
              self.current.add_point(pos)
              self.line.points = [pos, pos]
              self.set_hiding()
              self.drawingPolygon.emit(True)
              self.update()
  
      def set_hiding(self, enable=True):
          self._hide_background = self.hide_background if enable else False
  
      def can_close_shape(self):
          return self.drawing() and self.current and len(self.current) > 2
  
      def mouseDoubleClickEvent(self, ev):
          # We need at least 4 points here, since the mousePress handler
          # adds an extra one before this handler is called.
          if self.can_close_shape() and len(self.current) > 3:
              self.current.pop_point()
              self.finalise()
  
      def select_shape(self, shape):
          self.de_select_shape()
          shape.selected = True
          self.selected_shape = shape
          self.set_hiding()
          self.selectionChanged.emit(True)
          self.update()
  
      def select_shape_point(self, point):
          """Select the first shape created which contains this point."""
          self.de_select_shape()
          if self.selected_vertex():  # A vertex is marked for selection.
              index, shape = self.h_vertex, self.h_shape
              shape.highlight_vertex(index, shape.MOVE_VERTEX)
              self.select_shape(shape)
              return self.h_vertex
          for shape in reversed(self.shapes):
              if self.isVisible(shape) and shape.contains_point(point):
                  self.select_shape(shape)
                  self.calculate_offsets(shape, point)
                  return self.selected_shape
          return None
  
      def calculate_offsets(self, shape, point):
          rect = shape.bounding_rect()
          x1 = rect.x() - point.x()
          y1 = rect.y() - point.y()
          x2 = (rect.x() + rect.width()) - point.x()
          y2 = (rect.y() + rect.height()) - point.y()
          self.offsets = QPointF(x1, y1), QPointF(x2, y2)
  
      def snap_point_to_canvas(self, x, y):
          """
          Moves a point x,y to within the boundaries of the canvas.
          :return: (x,y,snapped) where snapped is True if x or y were changed, False if not.
          """
          # if x < 0 or x > self.pixmap.width() or y < 0 or y > self.pixmap.height():
          #     x = max(x, 0)
          #     y = max(y, 0)
          #     x = min(x, self.pixmap.width())
          #     y = min(y, self.pixmap.height())
          #     return x, y, True
  
          return x, y, True
  
      def bounded_move_vertex(self, pos):
          index, shape = self.h_vertex, self.h_shape
          point = shape[index]
          # if self.out_of_pixmap(pos):
          #     size = self.pixmap.size()
          #     clipped_x = min(max(0, pos.x()), size.width())
          #     clipped_y = min(max(0, pos.y()), size.height())
          #     pos = QPointF(clipped_x, clipped_y)
  
          if self.draw_square:
              opposite_point_index = (index + 2) % 4
              opposite_point = shape[opposite_point_index]
  
              min_size = min(abs(pos.x() - opposite_point.x()),
                             abs(pos.y() - opposite_point.y()))
              direction_x = -1 if pos.x() - opposite_point.x() < 0 else 1
              direction_y = -1 if pos.y() - opposite_point.y() < 0 else 1
              shift_pos = QPointF(opposite_point.x() + direction_x * min_size - point.x(),
                                  opposite_point.y() + direction_y * min_size - point.y())
          else:
              shift_pos = pos - point
  
          shape.move_vertex_by(index, shift_pos)
  
          left_index = (index + 1) % 4
          right_index = (index + 3) % 4
          left_shift = None
          right_shift = None
          if index % 2 == 0:
              right_shift = QPointF(shift_pos.x(), 0)
              left_shift = QPointF(0, shift_pos.y())
          else:
              left_shift = QPointF(shift_pos.x(), 0)
              right_shift = QPointF(0, shift_pos.y())
          shape.move_vertex_by(right_index, right_shift)
          shape.move_vertex_by(left_index, left_shift)
  
      def bounded_move_shape(self, shape, pos):
          # if self.out_of_pixmap(pos):
          #     return False  # No need to move
          # o1 = pos + self.offsets[0]
          # if self.out_of_pixmap(o1):
          #     pos -= QPointF(min(0, o1.x()), min(0, o1.y()))
          # o2 = pos + self.offsets[1]
          # if self.out_of_pixmap(o2):
          #     pos += QPointF(min(0, self.pixmap.width() - o2.x()),
          #                    min(0, self.pixmap.height() - o2.y()))
          # The next line tracks the new position of the cursor
          # relative to the shape, but also results in making it
          # a bit "shaky" when nearing the border and allows it to
          # go outside of the shape's area for some reason. XXX
          # self.calculateOffsets(self.selectedShape, pos)
          dp = pos - self.prev_point
          if dp:
              shape.move_by(dp)
              self.prev_point = pos
              return True
          return False
  
      def de_select_shape(self):
          if self.selected_shape:
              self.selected_shape.selected = False
              self.selected_shape = None
              self.set_hiding(False)
              self.selectionChanged.emit(False)
              self.update()
  
      def delete_selected(self):
          if self.selected_shape:
              shape = self.selected_shape
              self.shapes.remove(self.selected_shape)
              self.selected_shape = None
              self.update()
              return shape
  
      def copy_selected_shape(self):
          if self.selected_shape:
              shape = self.selected_shape.copy()
              self.de_select_shape()
              self.shapes.append(shape)
              shape.selected = True
              self.selected_shape = shape
              self.bounded_shift_shape(shape)
              return shape
  
      def bounded_shift_shape(self, shape):
          # this is OK
          # Try to move in one direction, and if it fails in another.
          # Give up if both fail.
          point = shape[0]
          offset = QPointF(2.0, 2.0)
          self.calculate_offsets(shape, point)
          self.prev_point = point
          if not self.bounded_move_shape(shape, point - offset):
              self.bounded_move_shape(shape, point + offset)
  
      def paint_rect(self, left_top, right_bottom):
          p = self._painter
          rect_width = right_bottom.x() - left_top.x()
          rect_height = right_bottom.y() - left_top.y()
          p.setPen(self.drawing_rect_color)
          brush = QBrush(Qt.BDiagPattern)
          p.setBrush(brush)
          p.drawRect(left_top.x(), left_top.y(), rect_width, rect_height)
  
      def paint_traingle(self, left_top, right_bottom):
          p = self._painter
          brush = QBrush(Qt.BDiagPattern)
          p.setBrush(brush)
          p.setPen(self.drawing_rect_color)
  
          p1, p2 = calc_extra_points(-1/(calc_shib(left_top, right_bottom)),
                                     left_top, max_d=calc_distance(left_top, right_bottom) / 2)
          points = QPolygonF([
              p1,
              left_top,
              p2,
              right_bottom,
          ])
          p.drawPolygon(points)
  
      def paintEvent(self, event):
          if not self.pixmap:
              return super(Canvas, self).paintEvent(event)
  
          p = self._painter
          p.begin(self)
          p.setRenderHint(QPainter.Antialiasing)
          p.setRenderHint(QPainter.HighQualityAntialiasing)
          p.setRenderHint(QPainter.SmoothPixmapTransform)
  
          p.scale(self.scale, self.scale)
          p.translate(self.offset_to_center())
  
          p.drawPixmap(0, 0, self.pixmap)
          Shape.scale = self.scale
          Shape.label_font_size = self.label_font_size
          for shape in self.shapes:
              if (shape.selected or not self._hide_background) and self.isVisible(shape):
                  shape.fill = shape.selected or shape == self.h_shape
                  shape.paint(p)
          if self.current:
              self.current.paint(p)
              self.line.paint(p)
          if self.selected_shape_copy:
              self.selected_shape_copy.paint(p)
  
          # Paint rect
          if self.current is not None and len(self.line) == 2:
              left_top = self.line[0]
              right_bottom = self.line[1]
              self.paint_traingle(left_top, right_bottom)
  
          if self.drawing() and not self.prev_point.isNull() and not self.out_of_pixmap(self.prev_point):
              p.setPen(QColor(0, 0, 0))
              p.drawLine(self.prev_point.x(), 0,
                         self.prev_point.x(), self.pixmap.height())
              p.drawLine(0, self.prev_point.y(),
                         self.pixmap.width(), self.prev_point.y())
  
          self.setAutoFillBackground(True)
          if self.verified:
              pal = self.palette()
              pal.setColor(self.backgroundRole(), QColor(184, 239, 38, 128))
              self.setPalette(pal)
          else:
              pal = self.palette()
              pal.setColor(self.backgroundRole(), QColor(232, 232, 232, 255))
              self.setPalette(pal)
  
          p.end()
  
      def transform_pos(self, point):
          """Convert from widget-logical coordinates to painter-logical coordinates."""
          return point / self.scale - self.offset_to_center()
  
      def offset_to_center(self):
          s = self.scale
          area = super(Canvas, self).size()
          w, h = self.pixmap.width() * s, self.pixmap.height() * s
          aw, ah = area.width(), area.height()
          x = (aw - w) / (2 * s) if aw > w else 0
          y = (ah - h) / (2 * s) if ah > h else 0
          return QPointF(x, y)
  
      def out_of_pixmap(self, p):
          # w, h = self.pixmap.width(), self.pixmap.height()
          # return not (0 <= p.x() <= w and 0 <= p.y() <= h)
          return False
  
      def finalise(self):
          assert self.current
          if self.current.points[0] == self.current.points[-1]:
              self.current = None
              self.drawingPolygon.emit(False)
              self.update()
              return
          self.current.close()
          self.shapes.append(self.current)
          self.current = None
          self.set_hiding(False)
          self.newShape.emit()
          self.update()
  
      def close_enough(self, p1, p2):
          # d = distance(p1 - p2)
          # m = (p1-p2).manhattanLength()
          # print "d %.2f, m %d, %.2f" % (d, m, d - m)
          return distance(p1 - p2) < self.epsilon
  
      # These two, along with a call to adjustSize are required for the
      # scroll area.
      def sizeHint(self):
          return self.minimumSizeHint()
  
      def minimumSizeHint(self):
          if self.pixmap:
              return self.scale * self.pixmap.size()
          return super(Canvas, self).minimumSizeHint()
  
      def wheelEvent(self, ev):
          qt_version = 4 if hasattr(ev, "delta") else 5
          if qt_version == 4:
              if ev.orientation() == Qt.Vertical:
                  v_delta = ev.delta()
                  h_delta = 0
              else:
                  h_delta = ev.delta()
                  v_delta = 0
          else:
              delta = ev.angleDelta()
              h_delta = delta.x()
              v_delta = delta.y()
  
          mods = ev.modifiers()
          if Qt.ControlModifier == int(mods) and v_delta:
              self.zoomRequest.emit(v_delta)
          else:
              v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical)
              h_delta and self.scrollRequest.emit(h_delta, Qt.Horizontal)
          ev.accept()
  
      def keyPressEvent(self, ev):
          key = ev.key()
          if key == Qt.Key_Escape and self.current:
              print('ESC press')
              self.current = None
              self.drawingPolygon.emit(False)
              self.update()
          elif key == Qt.Key_Return and self.can_close_shape():
              self.finalise()
          elif key == Qt.Key_Left and self.selected_shape:
              self.move_one_pixel('Left')
          elif key == Qt.Key_Right and self.selected_shape:
              self.move_one_pixel('Right')
          elif key == Qt.Key_Up and self.selected_shape:
              self.move_one_pixel('Up')
          elif key == Qt.Key_Down and self.selected_shape:
              self.move_one_pixel('Down')
  
      def move_one_pixel(self, direction):
          # print(self.selectedShape.points)
          if direction == 'Left' and not self.move_out_of_bound(QPointF(-1.0, 0)):
              # print("move Left one pixel")
              self.selected_shape.points[0] += QPointF(-1.0, 0)
              self.selected_shape.points[1] += QPointF(-1.0, 0)
              self.selected_shape.points[2] += QPointF(-1.0, 0)
              self.selected_shape.points[3] += QPointF(-1.0, 0)
          elif direction == 'Right' and not self.move_out_of_bound(QPointF(1.0, 0)):
              # print("move Right one pixel")
              self.selected_shape.points[0] += QPointF(1.0, 0)
              self.selected_shape.points[1] += QPointF(1.0, 0)
              self.selected_shape.points[2] += QPointF(1.0, 0)
              self.selected_shape.points[3] += QPointF(1.0, 0)
              self.selected_shape.points[3] += QPointF(1.0, 0)
          elif direction == 'Up' and not self.move_out_of_bound(QPointF(0, -1.0)):
              # print("move Up one pixel")
              self.selected_shape.points[0] += QPointF(0, -1.0)
              self.selected_shape.points[1] += QPointF(0, -1.0)
              self.selected_shape.points[2] += QPointF(0, -1.0)
              self.selected_shape.points[3] += QPointF(0, -1.0)
          elif direction == 'Down' and not self.move_out_of_bound(QPointF(0, 1.0)):
              # print("move Down one pixel")
              self.selected_shape.points[0] += QPointF(0, 1.0)
              self.selected_shape.points[1] += QPointF(0, 1.0)
              self.selected_shape.points[2] += QPointF(0, 1.0)
              self.selected_shape.points[3] += QPointF(0, 1.0)
          self.shapeMoved.emit()
          self.repaint()
  
      def move_out_of_bound(self, step):
          print('self.selected_shape.points: ', self.selected_shape.points)
          points = [p1 + p2 for p1,
                    p2 in zip(self.selected_shape.points, [step] * 4)]
          return True  # in map(self.out_of_pixmap, points)
  
      def set_last_label(self, text, line_color=None, fill_color=None):
          assert text
          self.shapes[-1].label = text
          if line_color:
              self.shapes[-1].line_color = line_color
  
          if fill_color:
              self.shapes[-1].fill_color = fill_color
  
          return self.shapes[-1]
  
      def undo_last_line(self):
          assert self.shapes
          self.current = self.shapes.pop()
          self.current.set_open()
          self.line.points = [self.current[-1], self.current[0]]
          self.drawingPolygon.emit(True)
  
      def reset_all_lines(self):
          assert self.shapes
          self.current = self.shapes.pop()
          self.current.set_open()
          self.line.points = [self.current[-1], self.current[0]]
          self.drawingPolygon.emit(True)
          self.current = None
          self.drawingPolygon.emit(False)
          self.update()
  
      def load_pixmap(self, pixmap):
          self.pixmap = pixmap
          self.shapes = []
          self.repaint()
  
      def load_shapes(self, shapes):
          self.shapes = list(shapes)
          self.current = None
          self.repaint()
  
      def set_shape_visible(self, shape, value):
          self.visible[shape] = value
          self.repaint()
  
      def current_cursor(self):
          cursor = QApplication.overrideCursor()
          if cursor is not None:
              cursor = cursor.shape()
          return cursor
  
      def override_cursor(self, cursor):
          self._cursor = cursor
          if self.current_cursor() is None:
              QApplication.setOverrideCursor(cursor)
          else:
              QApplication.changeOverrideCursor(cursor)
  
      def restore_cursor(self):
          QApplication.restoreOverrideCursor()
  
      def reset_state(self):
          self.restore_cursor()
          self.pixmap = None
          self.update()
  
      def set_drawing_shape_to_square(self, status):
          self.draw_square = status
  