Esempio n. 1
0
File: views.py Progetto: herstky/DES
class ReadoutView(View):
    class Orientation(Enum):
        horizontal = 1
        vertical = 2

    def __init__(self, model):
        super().__init__(model)
        self.graphics_item = QGraphicsRectItem(0, 0, 9, 9)
        self.graphics_item.setBrush(Qt.black)

        inner_rect_item = QGraphicsRectItem(1, 1, 7, 7, self.graphics_item)
        inner_rect_item.setBrush(Qt.white)

        self.text_item = None
        self.orientation = ReadoutView.Orientation.horizontal

    def line(self):
        for child in self.graphics_item.childItems():
            if isinstance(child, QGraphicsLineItem):
                return child.line()

    def line_item(self):
        for child in self.graphics_item.childItems():
            if isinstance(child, QGraphicsLineItem):
                return child

    def init_text(self):
        width = self.graphics_item.boundingRect().width()
        height = self.graphics_item.boundingRect().height()
        self.text_item = QGraphicsTextItem(self.graphics_item)
        self.text_item.setPos(width / 2 + 2, -height)
Esempio n. 2
0
class CplxItem(KineticsDisplayItem):
    defaultWidth = 10
    defaultHeight = 10
    name = constants.ITEM

    def __init__(self, *args, **kwargs):
        KineticsDisplayItem.__init__(self, *args, **kwargs)
        self.gobj = QGraphicsRectItem(0, 0, CplxItem.defaultWidth,
                                      CplxItem.defaultHeight, self)
        self.gobj.mobj = self.mobj
        self._conc = self.mobj.conc
        self._n = self.mobj.n
        doc = "Conc\t: " + str(self._conc) + "\nn\t: " + str(self._n)
        self.gobj.setToolTip(doc)

    def updateValue(self, gobj):
        self._gobj = gobj
        if (isinstance(self._gobj, moose.PoolBase)):
            self._conc = self.mobj.conc
            self._n = self.mobj.n
            doc = "Conc\t: " + str(self._conc) + "\nn\t: " + str(self._n)
            self.gobj.setToolTip(doc)

    def setDisplayProperties(self, x, y, textcolor, bgcolor):
        """Set the display properties of this item."""
        self.setGeometry(self.gobj.boundingRect().width() / 2,
                         self.gobj.boundingRect().height(),
                         self.gobj.boundingRect().width(),
                         self.gobj.boundingRect().height())
        self.setFlag(QGraphicsItem.ItemIsMovable, False)

    def refresh(self, scale):
        defaultWidth = CplxItem.defaultWidth * scale
        defaultHeight = CplxItem.defaultHeight * scale

        self.gobj.setRect(0, 0, defaultWidth, defaultHeight)
Esempio n. 3
0
def label_maker(node, label):
    """Form correctly formatted leaf label."""

    face = QGraphicsTextItem()
    face.setHtml(label)

    # Create parent RectItem with TextItem in center
    fbox = face.boundingRect()
    rect = QGraphicsRectItem(0, 0, fbox.width(), fbox.height())
    rbox = rect.boundingRect()
    face.setPos(rbox.x(), rbox.center().y() - fbox.height() / 2)

    # Remove border
    rect.setPen(QPen(QtCore.Qt.NoPen))

    # Set as parent item so DynamicItemFace can use .rect() method
    face.setParentItem(rect)

    return rect
Esempio n. 4
0
class SvgView(QGraphicsView):
    Native, OpenGL, Image = range(3)

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

        self.renderer = SvgView.Native
        self.svgItem = None
        self.backgroundItem = None
        self.outlineItem = None
        self.image = QImage()

        self.setScene(QGraphicsScene(self))
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)

        # Prepare background check-board pattern.
        tilePixmap = QPixmap(64, 64)
        tilePixmap.fill(Qt.white)
        tilePainter = QPainter(tilePixmap)
        color = QColor(220, 220, 220)
        tilePainter.fillRect(0, 0, 32, 32, color)
        tilePainter.fillRect(32, 32, 32, 32, color)
        tilePainter.end()

        self.setBackgroundBrush(QBrush(tilePixmap))

    def drawBackground(self, p, rect):
        p.save()
        p.resetTransform()
        p.drawTiledPixmap(self.viewport().rect(),
                          self.backgroundBrush().texture())
        p.restore()

    def openFile(self, svg_file):
        if not svg_file.exists():
            return

        s = self.scene()

        if self.backgroundItem:
            drawBackground = self.backgroundItem.isVisible()
        else:
            drawBackground = False

        if self.outlineItem:
            drawOutline = self.outlineItem.isVisible()
        else:
            drawOutline = True

        s.clear()
        self.resetTransform()

        self.svgItem = QGraphicsSvgItem(svg_file.fileName())
        self.svgItem.setFlags(QGraphicsItem.ItemClipsToShape)
        self.svgItem.setCacheMode(QGraphicsItem.NoCache)
        self.svgItem.setZValue(0)

        self.backgroundItem = QGraphicsRectItem(self.svgItem.boundingRect())
        self.backgroundItem.setBrush(Qt.white)
        self.backgroundItem.setPen(QPen(Qt.NoPen))
        self.backgroundItem.setVisible(drawBackground)
        self.backgroundItem.setZValue(-1)

        self.outlineItem = QGraphicsRectItem(self.svgItem.boundingRect())
        outline = QPen(Qt.black, 2, Qt.DashLine)
        outline.setCosmetic(True)
        self.outlineItem.setPen(outline)
        self.outlineItem.setBrush(QBrush(Qt.NoBrush))
        self.outlineItem.setVisible(drawOutline)
        self.outlineItem.setZValue(1)

        s.addItem(self.backgroundItem)
        s.addItem(self.svgItem)
        s.addItem(self.outlineItem)

        s.setSceneRect(self.outlineItem.boundingRect().adjusted(
            -10, -10, 10, 10))

    def setRenderer(self, renderer):
        self.renderer = renderer

        if self.renderer == SvgView.OpenGL:
            if QGLFormat.hasOpenGL():
                self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
        else:
            self.setViewport(QWidget())

    def setHighQualityAntialiasing(self, highQualityAntialiasing):
        if QGLFormat.hasOpenGL():
            self.setRenderHint(QPainter.HighQualityAntialiasing,
                               highQualityAntialiasing)

    def setViewBackground(self, enable):
        if self.backgroundItem:
            self.backgroundItem.setVisible(enable)

    def setViewOutline(self, enable):
        if self.outlineItem:
            self.outlineItem.setVisible(enable)

    def paintEvent(self, event):
        if self.renderer == SvgView.Image:
            if self.image.size() != self.viewport().size():
                self.image = QImage(self.viewport().size(),
                                    QImage.Format_ARGB32_Premultiplied)

            imagePainter = QPainter(self.image)
            QGraphicsView.render(self, imagePainter)
            imagePainter.end()

            p = QPainter(self.viewport())
            p.drawImage(0, 0, self.image)
        else:
            super(SvgView, self).paintEvent(event)

    def wheelEvent(self, event):
        factor = pow(1.2, event.delta() / 240.0)
        self.scale(factor, factor)
        event.accept()
Esempio n. 5
0
class SvgView(QGraphicsView):
    Native, OpenGL, Image = range(3)

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

        self.renderer = SvgView.Native
        self.svgItem = None
        self.backgroundItem = None
        self.outlineItem = None
        self.image = QImage()

        self.setScene(QGraphicsScene(self))
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)

        # Prepare background check-board pattern.
        tilePixmap = QPixmap(64, 64)
        tilePixmap.fill(Qt.white)
        tilePainter = QPainter(tilePixmap)
        color = QColor(220, 220, 220)
        tilePainter.fillRect(0, 0, 32, 32, color)
        tilePainter.fillRect(32, 32, 32, 32, color)
        tilePainter.end()

        self.setBackgroundBrush(QBrush(tilePixmap))

    def drawBackground(self, p, rect):
        p.save()
        p.resetTransform()
        p.drawTiledPixmap(self.viewport().rect(),
                self.backgroundBrush().texture())
        p.restore()

    def openFile(self, svg_file):
        if not svg_file.exists():
            return

        s = self.scene()

        if self.backgroundItem:
            drawBackground = self.backgroundItem.isVisible()
        else:
            drawBackground = False

        if self.outlineItem:
            drawOutline = self.outlineItem.isVisible()
        else:
            drawOutline = True

        s.clear()
        self.resetTransform()

        self.svgItem = QGraphicsSvgItem(svg_file.fileName())
        self.svgItem.setFlags(QGraphicsItem.ItemClipsToShape)
        self.svgItem.setCacheMode(QGraphicsItem.NoCache)
        self.svgItem.setZValue(0)

        self.backgroundItem = QGraphicsRectItem(self.svgItem.boundingRect())
        self.backgroundItem.setBrush(Qt.white)
        self.backgroundItem.setPen(QPen(Qt.NoPen))
        self.backgroundItem.setVisible(drawBackground)
        self.backgroundItem.setZValue(-1)

        self.outlineItem = QGraphicsRectItem(self.svgItem.boundingRect())
        outline = QPen(Qt.black, 2, Qt.DashLine)
        outline.setCosmetic(True)
        self.outlineItem.setPen(outline)
        self.outlineItem.setBrush(QBrush(Qt.NoBrush))
        self.outlineItem.setVisible(drawOutline)
        self.outlineItem.setZValue(1)

        s.addItem(self.backgroundItem)
        s.addItem(self.svgItem)
        s.addItem(self.outlineItem)

        s.setSceneRect(self.outlineItem.boundingRect().adjusted(-10, -10, 10, 10))

    def setRenderer(self, renderer):
        self.renderer = renderer

        if self.renderer == SvgView.OpenGL:
            if QGLFormat.hasOpenGL():
                self.setViewport(QGLWidget(QGLFormat(QGL.SampleBuffers)))
        else:
            self.setViewport(QWidget())

    def setHighQualityAntialiasing(self, highQualityAntialiasing):
        if QGLFormat.hasOpenGL():
            self.setRenderHint(QPainter.HighQualityAntialiasing,
                    highQualityAntialiasing)

    def setViewBackground(self, enable):
        if self.backgroundItem:
            self.backgroundItem.setVisible(enable)

    def setViewOutline(self, enable):
        if self.outlineItem:
            self.outlineItem.setVisible(enable)

    def paintEvent(self, event):
        if self.renderer == SvgView.Image:
            if self.image.size() != self.viewport().size():
                self.image = QImage(self.viewport().size(),
                        QImage.Format_ARGB32_Premultiplied)

            imagePainter = QPainter(self.image)
            QGraphicsView.render(self, imagePainter)
            imagePainter.end()

            p = QPainter(self.viewport())
            p.drawImage(0, 0, self.image)
        else:
            super(SvgView, self).paintEvent(event)

    def wheelEvent(self, event):
        print(event.angleDelta().y())
        factor = pow(1.2, event.angleDelta().y() / 240.0)
        self.scale(factor, factor)
        event.accept()
Esempio n. 6
0
class Scene_Base(QGraphicsScene):
	def __init__(self, window):
		super().__init__()
		self.setSceneRect(0, 0, window.game_width, window.game_height)
		self.window = window
		self.init_fields()
		self.create_fade()
		self.dialog_box = None
		self.setup()

	def init_fields(self):
		self.actions = []
		self.current_action = -1
		self.dialog_box = None
		self.target_text = None
		self.current_dialog_text = None
		self.current_dialog_text_offset = 0
		self.background = None
		self.fading_box = None
		self.fade_dir = 0
		self.next_scene = None
		self.buttons = []
		self.current_id = 1
		self.texts = {}
		self.images = {}
		self.moving_images = []
		self.updatable_images = []
		self.vaxx = None
		self.entry = None
		self.pathogen = None
		self.tempPic = None

	def create_fade(self, opc=1):
		self.fading_box = QGraphicsRectItem(0, 0, self.width(), self.height())
		self.fading_box.setBrush(QColor(0, 0, 0))
		self.fading_box.setOpacity(opc)
		self.fading_box.setZValue(100)
		self.addItem(self.fading_box)

	# ==============================================
	# * Setup
	#
	# Overwrite with child classes to add actions.
	# ==============================================

	def setup(self):
		pass

	# ==============================================
	# * Update
	#
	# Updates the scene. Handles input, waiting, etc.
	# ==============================================

	def update(self):
		self.update_fade()
		self.update_dialog()
		self.update_dialog_text()
		self.update_moving_images()
		self.update_animated_images()

	def update_fade(self):
		if self.fading_box is not None:
			if self.fade_dir == 0 and self.fading_box.opacity() > 0:
				self.fading_box.setOpacity(self.fading_box.opacity() - 0.02)
				if self.fading_box.opacity() <= 0:
					self.removeItem(self.fading_box)
					self.fading_box = None
			elif self.fade_dir == 1 and self.fading_box.opacity() < 1:
				self.fading_box.setOpacity(self.fading_box.opacity() + 0.02)
				if self.fading_box.opacity() >= 1:
					if self.next_scene is not None:
						self.window.goto_scene(self.next_scene)
					else:
						self.actually_close_game()

	def update_dialog(self):
		if self.dialog_box is not None:
			if self.dialog_box.opacity() < 0.5:
				self.dialog_box.setOpacity(self.dialog_box.opacity() + 0.04)
				if self.dialog_box.opacity() >= 0.5:
					self.dialog_box.setOpacity(0.5)
					if self.actions[0][0] == 0:
						self.actually_show_dialog(self.actions[0])

	def update_dialog_text(self):
		if self.current_dialog_text is not None:
			curr_text = self.current_dialog_text.toPlainText()
			if curr_text != self.target_text:
				if self.current_dialog_text_offset < 3:
					self.current_dialog_text_offset += 1
				else:
					self.current_dialog_text_offset = 0
					self.current_dialog_text.setPlainText(self.target_text[:len(curr_text) + 1])

	def update_moving_images(self):
		if len(self.moving_images) > 0:
			index = 0
			new_data = []
			for image in self.moving_images:
				pic_id = image[0]
				item = self.images[pic_id]
				item.setX(item.x() + image[1])
				item.setY(item.y() + image[2])
				image[3] -= 1
				if image[3] > 0:
					new_data.append(image)
				elif image[4] is not None:
					image[4]()
				index += 1

			self.moving_images = new_data

	def update_animated_images(self):
		if len(self.updatable_images) > 0:
			index = 0
			for image in self.updatable_images:
				image[2] += 1
				if image[2] > image[4]:
					image[2] = 0
					image[3] += 1
					if image[3] >= len(image[1]):
						image[3] = 0
					image[0].setPixmap(QPixmap(image[1][image[3]]))


	# ==============================================
	# * Mouse Events
	#
	# Handle mouse input.
	# ==============================================

	def mouseMoveEvent(self, mouseEvent):
		pass

	def mousePressEvent(self, mouseEvent):
		self.when_mouse_pressed(mouseEvent)

	def mouseDoubleClickEvent(self, mouseEvent):
		self.when_mouse_pressed(mouseEvent)

	def when_mouse_pressed(self, mouseEvent):
		if self.current_action == 0 and mouseEvent.button() == 1:
			if self.current_dialog_text is not None:
				if self.current_dialog_text.toPlainText() != self.target_text:
					self.current_dialog_text.setPlainText(self.target_text)
				else:
					self.removeItem(self.current_dialog_text)
					self.call_next_action()
		else:
			super(Scene_Base, self).mousePressEvent(mouseEvent)

	# ==============================================
	# * Action Management
	# ==============================================

	def finish_action(self):
		if len(self.actions) > 0:
			self.actions.pop(0)
		else:
			self.wait_for_button_press()

	def check_if_first(self):
		if len(self.actions) == 1:
			self.perform_next_action()

	def perform_next_action(self):
		if len(self.actions) > 0:
			action = self.actions[0]
			self.current_action = action_type = action[0]

			if action_type is -1:
				self.actually_hide_dialog_box()
			elif action_type is 0:
				self.actually_show_dialog(action)
			elif action_type is 1:
				self.actually_show_button(action)
			elif action_type is 2:
				self.actually_set_background(action)
			elif action_type is 3:
				self.actually_goto_scene(action)
			elif action_type is 4:
				self.actually_close_game()
			elif action_type is 5:
				self.actually_play_song(action)
			elif action_type is 6:
				self.actually_wait_for_button_press()
			elif action_type is 7:
				self.actually_remove_all_buttons()
			elif action_type is 8:
				self.actually_show_text(action)
			elif action_type is 9:
				self.actually_hide_text(action)
			elif action_type is 10:
				self.actually_show_image(action)
			elif action_type is 11:
				self.actually_hide_image(action)
			elif action_type is 12:
				self.actually_move_image(action)
			elif action_type is 50:
				self.actually_play_sound(action)

	def call_next_action(self):
		self.finish_action()
		self.perform_next_action()

	# ==============================================
	# * Actual Implementations
	# ==============================================

	def actually_show_dialog(self, data):
		if self.dialog_box is None:
			self.create_dialog_box()
		else:
			self.current_dialog_text = QGraphicsTextItem()
			self.current_dialog_text.setZValue(20)
			self.current_dialog_text.setDefaultTextColor(QColor(255, 255, 255))

			temp_font = self.current_dialog_text.font()
			temp_font.setPointSize(data[2])
			self.current_dialog_text.setFont(temp_font)

			self.addItem(self.current_dialog_text)
			self.current_dialog_text.setX(self.dialog_box.x() + 10)
			self.current_dialog_text.setY(self.dialog_box.y() + 10)
			self.current_dialog_text.setTextWidth(self.dialog_box.boundingRect().width() - 20)

			self.target_text = data[1]
			self.current_dialog_text_offset = 0

	def create_dialog_box(self):
		self.dialog_box = QGraphicsRectItem(0, 0, self.width() - 20, self.height() / 4)
		self.dialog_box.setBrush(QColor(0, 0, 0))
		self.dialog_box.setX(10)
		self.dialog_box.setY(self.height() - self.dialog_box.boundingRect().height() - 10)
		self.dialog_box.setZValue(15);
		self.dialog_box.setOpacity(0)
		self.addItem(self.dialog_box)

	def actually_hide_dialog_box(self):
		if self.dialog_box is not None:
			self.removeItem(self.dialog_box)
			self.dialog_box = None

	def actually_show_button(self, data):
		button = Button(self, data[3], data[4], data[5], data[6], data[7], data[8], data[9])
		button.setX(data[1])
		button.setY(data[2])
		self.buttons.append(button)
		self.addItem(button)
		self.call_next_action()

	def actually_set_background(self, data):
		if self.background is not None:
			self.removeItem(self.background)
			self.background = None

		self.background = QGraphicsPixmapItem(QPixmap(data[1]).scaled(self.window.game_width, self.window.game_height))
		self.background.setZValue(-10)
		self.addItem(self.background)
		self.call_next_action()

	def actually_goto_scene(self, data):
		self.next_scene = data[1]
		self.fade_dir = 1
		self.create_fade(0)

	def actually_close_game(self):
		self.window.close_game()

	def actually_play_song(self, data):
		AudioPlayer.play_song(data[1])
		self.call_next_action()

	def actually_wait_for_button_press(self):
		self.current_action = -1

	def actually_remove_all_buttons(self):
		for b in self.buttons:
			self.removeItem(b)
		self.buttons = []
		self.call_next_action()

	def actually_show_text(self, data):
		text_item = QGraphicsTextItem()
		text_item.setZValue(5)
		if len(data) >= 8 and data[7] != None:
			text_item.setDefaultTextColor(QColor(data[7]))
		else:
			text_item.setDefaultTextColor(QColor("#FFFFFF"))

		text_item.setX(data[2])
		text_item.setY(data[3])
		text_item.setTextWidth(data[4])
		text_item.setPlainText(data[5])

		temp_font = text_item.font()
		temp_font.setPointSize(data[6])
		text_item.setFont(temp_font)

		self.addItem(text_item)
		self.texts[data[1]] = text_item
		self.call_next_action()

	def actually_hide_text(self, data):
		self.removeItem(self.texts[data[1]])
		self.texts[data[1]] = None
		self.call_next_action()

	def actually_show_image(self, data):
		image = None
		if isinstance(data[2], list):
			image = QGraphicsPixmapItem(QPixmap(data[2][0]))
			self.updatable_images.append([image, data[2], 0, 0, data[5]])
		else:
			image = QGraphicsPixmapItem(QPixmap(data[2]))
		image.setX(data[3])
		image.setY(data[4])
		image.setZValue(0)
		self.addItem(image)
		self.images[data[1]] = image
		self.call_next_action()

	def actually_hide_image(self, data):
		self.removeItem(self.images[data[1]])
		self.images[data[1]] = None
		self.call_next_action()

	def actually_move_image(self, data):
		image_id = data[1]
		image = self.images[image_id]
		duration = data[4]
		x_speed = (data[2] - image.x()) / duration
		y_speed = (data[3] - image.y()) / duration
		callback = self.call_next_action if data[5] else None
		image_data = [image_id, x_speed, y_speed, duration, callback]
		self.moving_images.append(image_data)
		if not data[5]:
			self.call_next_action()

	def actually_play_sound(self, data):
		AudioPlayer.play_sound_effect(data[1])
		self.call_next_action()

	# ==============================================
	# * Setup Calls
	# ==============================================

	def add_call(self, data):
		self.actions.append(data)
		self.check_if_first()

	# ==============================================
	# * Extended Calls
	# ==============================================

	# ==============================================
	# Adds a dialog to the game.
	#
	# Ex:
	#     self.add_dialog("Hello World!")
	# ==============================================
	def add_dialog(self, msg, fontSize=20):
		self.add_call([0, msg, fontSize])

	# ==============================================
	# Hides the dialog box
	#
	# Ex:
	#     self.hide_dialog_box()
	# ==============================================
	def hide_dialog_box(self):
		self.add_call([-1])

	# ==============================================
	# Adds a button to the game.
	# After adding all buttons, be sure to call "self.wait_for_button_press".
	# Check scenes/scene_titlescreen.py for example of "font" and "buttonColors"
	#
	# Ex:
	#     self.add_button(30, 30, 200, 200, "My Button!", self.another_function)
	# ==============================================
	def add_button(self, x, y, w, h, name, action, font=None, buttonColors=None, textColors=None):
		self.add_call([1, x, y, w, h, name, font, buttonColors, textColors, action])

	# ==============================================
	# Sets the current background.
	#
	# Ex:
	#     self.set_background("images/Background1.png")
	# ==============================================
	def set_background(self, path):
		self.add_call([2, path])

	# ==============================================
	# Changes the game to the provided scene.
	#
	# Ex:
	#     self.goto_scene(scenes.my_other_scene.My_Other_Scene)
	# ==============================================
	def goto_scene(self, scene):
		self.add_call([3, scene])

	# ==============================================
	# Closes the game.
	#
	# Ex:
	#     self.close_game()
	# ==============================================
	def close_game(self):
		self.add_call([4])

	# ==============================================
	# Plays a song.
	#
	# Ex:
	#     self.play_song("audio/testmusic2.mp3")
	# ==============================================
	def play_song(self, path):
		self.add_call([5, path])

	# ==============================================
	# Plays a sound effect once.
	#
	# Ex:
	#     self.play_sound("audio/testmusic2.mp3")
	# ==============================================
	def play_sound(self, path):
		self.add_call([50, path])

	# ==============================================
	# Once all buttons are created, this will wait for the player to press one.
	#
	# Ex:
	#     self.wait_for_button_press()
	# ==============================================
	def wait_for_button_press(self):
		self.add_call([6])

	# ==============================================
	# Removes all buttons from the screen.
	#
	# Ex:
	#     self.remove_all_buttons()
	# ==============================================
	def remove_all_buttons(self):
		self.add_call([7])

	# ==============================================
	# Based on the value provided, there is a chance it will return True.
	#
	# Ex:
	#     if self.generate_random_chance(30):
	#		  # there is a 30% chance of this happening
	# 	  else:
	#		  # there is a 70% chance of this happening
	# ==============================================
	def generate_random_chance(self, val):
		return random.randint(0, 100) <= val

	# ==============================================
	# Shows text on the screen (not in dialog).
	# This function returns an ID that can be used in self.hide_text.
	#
	# Ex:
	#     text_id = self.show_text(10, 10, 200, "Hello Screen!", 40, "#FF44CC")
	# ==============================================
	def show_text(self, x, y, w, text, size=30, color=None):
		new_id = self.current_id
		self.current_id += 1
		self.add_call([8, new_id, x, y, w, text, size, color])
		return new_id

	# ==============================================
	# Hides the text connected to the ID.
	#
	# Ex:
	#     self.hide_text(text_id)
	# ==============================================
	def hide_text(self, text_id):
		self.add_call([9, text_id])

	# ==============================================
	# Shows a picture on the screen.
	# This function returns an ID that can be used in other functions.
	#
	# Ex:
	#     pic_id = self.show_picture("images/TestImage1.png", 30, 30)
	#
	# ----------------------------------------------
	#
	# Using an array of strings will create an animation.
	# In that case, the last argument is the frame-change frequency of the animation.
	#
	# Ex:
	#     pic_id = self.show_picture(["images/p1_frame_0.png", "images/p1_frame_1.png"], 100, 100, 30)
	# ==============================================
	def show_picture(self, path, x, y, animation_speed=20):
		new_id = self.current_id
		self.current_id += 1
		self.add_call([10, new_id, path, x, y, animation_speed])
		return new_id

	# ==============================================
	# Removes the picture connected to the provided ID.
	#
	# Ex:
	#     self.hide_picture(pic_id)
	# ==============================================
	def hide_picture(self, image_id):
		self.add_call([11, image_id])

	# ==============================================
	# Moves the picture to new coordinates over a duration of time.
	#
	# Ex:
	#     self.move_picture(pic_id, 90, 30, 120)
	# ==============================================
	def move_picture(self, image_id, x, y, duration, wait_until_finished=True):
		self.add_call([12, image_id, x, y, duration, wait_until_finished])

	#===============================================
	# Sets the vaccination status
	# True/False, 50% chance
	#===============================================
	def set_vaxx(self):
		if self.generate_random_chance(50):
			Scene_Base.vaxx = True
		else:
			Scene_Base.vaxx = False

	#===============================================
	# Sets the entry point
	# 0 is for blood (cut)
	# 1 is for stomach (mouth)
	#===============================================	
	def set_entry(self):
		if self.generate_random_chance(50):
			Scene_Base.entry = 0
		else:
			Scene_Base.entry = 1

	# ==============================================
	# Gets and sets global values
	#
	# Ex:
	#     self.set_value("Health", 100)
	#
	#     self.add_value("Health", -1)
	#
	#     if self.get_value("Health") <= 30:
	#         self.add_dialog("Player is less than 30 health!")
	# ==============================================
			
	def set_value(self, name, value):
		Scene_Base.GLOBAL_VARS[name] = value

	def add_value(self, name, value):
		Scene_Base.GLOBAL_VARS[name] += value

	def get_value(self, name):
		return Scene_Base.GLOBAL_VARS[name]
Esempio n. 7
0
    def refresh(self):
        if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter:
            return

        if not self.isVisible():
            return

        LINE_HEIGHT = 18
        SPACING = 3
        TEXT_WIDTH = self.sldTxtSize.value()
        CIRCLE_WIDTH = 10
        LEVEL_HEIGHT = 12

        s = self.scene
        s.clear()

        # Get Max Level (max depth)
        root = self._mdlOutline.rootItem

        def maxLevel(item, level=0, max=0):
            if level > max:
                max = level
            for c in item.children():
                m = maxLevel(c, level + 1)
                if m > max:
                    max = m
            return max

        MAX_LEVEL = maxLevel(root)

        # Get the list of tracked items (array of references)
        trackedItems = []

        if self.actPlots.isChecked():
            trackedItems += self.plotReferences()

        if self.actCharacters.isChecked():
            trackedItems += self.charactersReferences()

        ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING)

        fm = QFontMetrics(s.font())
        max_name = 0
        for ref in trackedItems:
            name = references.title(ref)
            max_name = max(fm.width(name), max_name)

        TITLE_WIDTH = max_name + 2 * SPACING

        # Add Folders and Texts
        outline = OutlineRect(0, 0, 0,
                              ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
        s.addItem(outline)
        outline.setPos(TITLE_WIDTH + SPACING, 0)

        refCircles = [
        ]  # a list of all references, to be added later on the lines

        # A Function to add a rect with centered elided text
        def addRectText(x, w, parent, text="", level=0, tooltip=""):
            deltaH = LEVEL_HEIGHT if level else 0
            r = OutlineRect(0,
                            0,
                            w,
                            parent.rect().height() - deltaH,
                            parent,
                            title=text)
            r.setPos(x, deltaH)

            txt = QGraphicsSimpleTextItem(text, r)
            f = txt.font()
            f.setPointSize(8)
            fm = QFontMetricsF(f)
            elidedText = fm.elidedText(text, Qt.ElideMiddle, w)
            txt.setFont(f)
            txt.setText(elidedText)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
            txt.setY(0)
            return r

        # A function to returns an item's width, by counting its children
        def itemWidth(item):
            if item.isFolder():
                r = 0
                for c in item.children():
                    r += itemWidth(c)
                return r or TEXT_WIDTH
            else:
                return TEXT_WIDTH

        def listItems(item, rect, level=0):
            delta = 0
            for child in item.children():
                w = itemWidth(child)

                if child.isFolder():
                    parent = addRectText(delta,
                                         w,
                                         rect,
                                         child.title(),
                                         level,
                                         tooltip=child.title())
                    parent.setToolTip(
                        references.tooltip(references.textReference(
                            child.ID())))
                    listItems(child, parent, level + 1)

                else:
                    rectChild = addRectText(delta,
                                            TEXT_WIDTH,
                                            rect,
                                            "",
                                            level,
                                            tooltip=child.title())
                    rectChild.setToolTip(
                        references.tooltip(references.textReference(
                            child.ID())))

                    # Find tracked references in that scene (or parent folders)
                    for ref in trackedItems:

                        result = []

                        # Tests if POV
                        scenePOV = False  # Will hold true of character is POV of the current text, not containing folder
                        if references.type(ref) == references.CharacterLetter:
                            ID = references.ID(ref)
                            c = child
                            while c:
                                if c.POV() == ID:
                                    result.append(c.ID())
                                    if c == child: scenePOV = True
                                c = c.parent()

                        # Search in notes/references
                        c = child
                        while c:
                            result += references.findReferencesTo(
                                ref, c, recursive=False)
                            c = c.parent()

                        if result:
                            ref2 = result[0]

                            # Create a RefCircle with the reference
                            c = RefCircle(TEXT_WIDTH / 2,
                                          -CIRCLE_WIDTH / 2,
                                          CIRCLE_WIDTH,
                                          ID=ref2,
                                          important=scenePOV)

                            # Store it, with the position of that item, to display it on the line later on
                            refCircles.append(
                                (ref, c,
                                 rect.mapToItem(outline, rectChild.pos())))

                delta += w

        listItems(root, outline)

        OUTLINE_WIDTH = itemWidth(root)

        # Add Tracked items
        i = 0
        itemsRect = s.addRect(0, 0, 0, 0)
        itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)

        # Set of colors for plots (as long as they don't have their own colors)
        colors = [
            "#D97777",
            "#AE5F8C",
            "#D9A377",
            "#FFC2C2",
            "#FFDEC2",
            "#D2A0BC",
            "#7B0F0F",
            "#7B400F",
            "#620C3D",
            "#AA3939",
            "#AA6C39",
            "#882D61",
            "#4C0000",
            "#4C2200",
            "#3D0022",
        ]

        for ref in trackedItems:
            if references.type(ref) == references.CharacterLetter:
                color = self._mdlCharacter.getCharacterByID(
                    references.ID(ref)).color()
            else:
                color = QColor(colors[i % len(colors)])

            # Rect
            r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
            r.setPen(QPen(Qt.NoPen))
            r.setBrush(QBrush(color))
            r.setPos(0, i * LINE_HEIGHT + i * SPACING)
            r.setToolTip(references.tooltip(ref))
            i += 1

            # Text
            name = references.title(ref)
            txt = QGraphicsSimpleTextItem(name, r)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())

            # Line
            line = PlotLine(0, 0, OUTLINE_WIDTH + SPACING, 0)
            line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
            s.addItem(line)
            line.setPen(QPen(color, 5))
            line.setToolTip(references.tooltip(ref))

            # We add the circles / references to text, on the line
            for ref2, circle, pos in refCircles:
                if ref2 == ref:
                    circle.setParentItem(line)
                    circle.setPos(pos.x(), 0)

        # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio
        self.view.setSceneRect(0, 0, 0, 0)
Esempio n. 8
0
class TextInputWidget(QGraphicsObject):

    input_entered = pyqtSignal(str)
    input_cancelled = pyqtSignal(str)
    font = QFont('monospace', 16)

    def __init__(self,
                 mode: str = 'number',
                 label: str = '',
                 getter=None,
                 setter=None,
                 parser=str,
                 validator=None,
                 parent: Optional[QGraphicsItem] = None):
        QGraphicsObject.__init__(self, parent)
        self.getter: Callable[[], str] = getter
        self.setter: Callable[[str], None] = setter
        self.parser: Callable[[str], Any] = parser
        self.validator: Callable[[Any], bool] = validator
        self.accept_button = Button("btn_accept", "Accept", parent=self)
        self.accept_button.set_base_color([ButtonColor.GREEN])
        self.accept_button.clicked.connect(self.handle_input_accepted)
        self.cancel_button = Button("btn_cancel", "Cancel", parent=self)
        self.cancel_button.set_base_color([ButtonColor.RED])
        self.cancel_button.clicked.connect(
            lambda: self.input_cancelled.emit(self.text_field.toPlainText()))
        self.accept_button.set_height(12)
        self.cancel_button.set_height(12)
        self.hor_padding = 2
        self.background_rect = QGraphicsRectItem(parent=self)
        self.background_rect.setBrush(QBrush(QColor(50, 50, 50, 200)))
        self.text_field = NumberInputItem(self)
        self.text_field.setFont(self.font)
        self.text_field.setTextInteractionFlags(Qt.TextEditable)
        self.text_field.setDefaultTextColor(Qt.white)
        self.text_field.text_changed.connect(self.adjust_layout)
        self.text_field.set_is_number_mode(mode == 'number')
        if self.getter is not None:
            self.text_field.setPlainText(str(self.getter()))
            self.adjust_layout()
        self.text_field.accepted.connect(
            lambda: self.accept_button.click_button(artificial_emit=True))
        self.text_field.cancelled.connect(
            lambda: self.cancel_button.click_button(artificial_emit=True))
        self.text_label = QGraphicsTextItem(self)
        self.text_label.setFont(self.font)
        self.text_label.setPlainText(label)
        self.text_label.setDefaultTextColor(Qt.white)
        self.setVisible(False)

    def paint(self,
              painter: QPainter,
              option: QStyleOptionGraphicsItem,
              widget: Optional[QWidget] = ...) -> None:
        pass

    def boundingRect(self) -> QRectF:
        return self.background_rect.boundingRect().united(self.accept_button.boundingRect())\
            .united(self.cancel_button.boundingRect())

    def adjust_layout(self):
        self.accept_button.fit_to_contents()
        self.cancel_button.fit_to_contents()
        total_width = 3 * self.hor_padding + self.text_field.boundingRect().width() \
                      + self.text_label.boundingRect().width()
        total_width = max(
            total_width,
            3 * self.hor_padding + self.accept_button.boundingRect().width() +
            self.cancel_button.boundingRect().width())
        total_height = self.text_field.boundingRect().height() + self.accept_button.boundingRect().height() \
                       + 3 * self.hor_padding
        self.background_rect.setRect(0, 0, total_width, total_height)
        self.setTransformOriginPoint(
            QPointF(0.5 * total_width, 0.5 * total_height))
        self.text_label.setPos(-0.5 * total_width + self.hor_padding,
                               -0.5 * total_height + self.hor_padding)
        self.text_field.setPos(
            self.hor_padding + self.text_label.pos().x() +
            self.text_label.boundingRect().width(),
            self.text_label.pos().y())
        self.background_rect.setPos(-0.5 * total_width, -0.5 * total_height)
        self.accept_button.set_width(
            (total_width - 3 * self.hor_padding) * 0.5)
        self.cancel_button.set_width(
            (total_width - 3 * self.hor_padding) * 0.5)
        self.accept_button.setPos(-0.5 * total_width + self.hor_padding,
                                  2 * self.hor_padding)
        self.cancel_button.setPos(self.accept_button.pos() + QPointF(
            self.hor_padding + self.accept_button.boundingRect().width(), 0.0))
        self.accept_button.set_disabled(
            len(self.text_field.toPlainText()) == 0)
        if self.validator is not None:
            try:
                value = self.parser(self.text_field.toPlainText())
                is_valid = self.validator(value)
            except ValueError:
                is_valid = False
            self.text_field.set_is_valid(is_valid)
            self.accept_button.set_disabled(not is_valid)
        self.update()

    def get_value(self) -> str:
        return self.text_field.toPlainText()

    def set_value(self, text: str):
        self.text_field.setPlainText(text)
        self.adjust_layout()
        self.set_focus()

    def set_focus(self):
        self.text_field.setFocus(Qt.PopupFocusReason)
        cursor = self.text_field.textCursor()
        cursor.movePosition(QTextCursor.EndOfLine)
        self.text_field.setTextCursor(cursor)

    def set_getter_setter_parser_validator(self, getter: Callable[[], Any],
                                           setter: Callable[[Any], None],
                                           parser: Callable[[str], Any],
                                           validator: Callable[[Any], bool]):
        self.getter = getter
        self.setter = setter
        self.parser = parser
        self.validator = validator
        if self.getter is not None:
            self.text_field.setPlainText(str(self.getter()))
            self.adjust_layout()

    def set_label(self, label: str):
        self.text_label.setPlainText(label)
        self.adjust_layout()

    def handle_input_accepted(self):
        self.setter(self.parser(self.text_field.toPlainText()))
        self.input_entered.emit(self.text_field.toPlainText())

    def handle_input_cancelled(self):
        self.input_cancelled.emit(str(self.getter()))
Esempio n. 9
0
class GameArea(QWidget):
    #constants of widget
    RATIO_WITH_SHIPLIST = 11 / 14
    RATIO_WITHOUT_SHIPLIST = 1
    EPS = 0.3

    #signals
    shipPlaced = pyqtSignal(Ship)

    def __init__(self, parent=None):
        super(GameArea, self).__init__(parent)
        self.__ui = Ui_GameArea()
        self.__ui.setupUi(self)

        self.__ratio = self.RATIO_WITH_SHIPLIST
        self.__scaleFactor = 1

        self.controller = Controller()
        self.controller._accept = self.__accept
        self.controller._decline = self.__decline
        self.__gameModel = None

        self.__shipList = {
            "boat": ShipListItem(length=1, name="boat", count=4),
            "destroyer": ShipListItem(length=2, name="destroyer", count=3),
            "cruiser": ShipListItem(length=3, name="cruiser", count=2),
            "battleship": ShipListItem(length=4, name="battleship", count=1),
        }

        # resources
        self.__cellImages = {"intact": None, "hit": None, "miss": None}

        self.__sprites = {"explosion": None, "splash": None}

        self.__originalTileSize = 0
        self.tileSize = 0

        self.__shipListImage = QImage()
        self.__counterImage = QImage()

        # drawing items
        self.__placedShips = []
        self.__field = [QGraphicsPixmapItem() for _ in range(100)]
        self.__letters = [QGraphicsTextItem() for _ in range(10)]
        self.__numbers = [QGraphicsTextItem() for _ in range(10)]
        for i in range(10):
            self.__letters[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0))
            self.__numbers[i].setDefaultTextColor(QColor.fromRgb(0, 0, 0))

        self.__spriteAnimations = []

        self.__shipListItem = QGraphicsPixmapItem()
        self.__ghostShip = QGraphicsPixmapItem(
        )  # data: 0 - rotation; 1 - ShipListItem
        self.__placer = QGraphicsRectItem()
        self.__dragShip = False

        self.__targetPixmap = None
        self.__targets = []
        self.__scanEffect = None

        # prepare Qt objects
        self.__scene = QGraphicsScene()
        self.__loadResources()
        self.__initGraphicsView()
        self.__adjustedToSize = 0

    def serviceModel(self, game_model):
        self.removeModel()
        self.__gameModel = game_model
        self.__gameModel.shipKilled.connect(self.__shootAround)

    def removeModel(self):
        if self.__gameModel:
            self.shipKilled.disconnect()
            self.__gameModel = None

    def __loadResources(self):
        if DEBUG_RESOURCE:
            resourcesPath = os.path.join(os.path.dirname(__file__),
                                         DEBUG_RESOURCE)
        else:
            resourcesPath = Environment.Resources.path()

        first = True
        for imageName in self.__cellImages:
            image = QImage(
                os.path.join(resourcesPath, "img", "cells",
                             f"{imageName}.png"))
            if first:
                first = False
                self.__originalTileSize = min(image.width(), image.height())
            else:
                self.__originalTileSize = min(self.__originalTileSize,
                                              image.width(), image.height())
            self.__cellImages[imageName] = image

        for spriteName in self.__sprites:
            image = QImage(
                os.path.join(resourcesPath, "img", "cells",
                             f"{spriteName}.png"))
            self.__sprites[spriteName] = image

        for shipName, ship in self.__shipList.items():
            ship.image = QImage(
                os.path.join(resourcesPath, "img", "ships", f"{shipName}.png"))

        self.__shipListImage = QImage(
            os.path.join(resourcesPath, "img", "backgrounds", "shiplist.png"))
        self.__counterImage = QImage(
            os.path.join(resourcesPath, "img", "miscellaneous",
                         "shipcounter.png"))

        self.__targetPixmap = QPixmap(
            os.path.join(resourcesPath, "img", "cells", "target.png"))

    def __initGraphicsView(self):
        self.__ui.graphicsView.setScene(self.__scene)
        self.__ui.graphicsView.viewport().installEventFilter(self)
        self.__ui.graphicsView.setRenderHints(
            QPainter.RenderHint.HighQualityAntialiasing
            | QPainter.RenderHint.TextAntialiasing
            | QPainter.RenderHint.SmoothPixmapTransform)
        self.__ui.graphicsView.horizontalScrollBar().blockSignals(True)
        self.__ui.graphicsView.verticalScrollBar().blockSignals(True)

        for cell in self.__field:
            cell.setData(0, "intact")
            pixmap = QPixmap.fromImage(self.__cellImages["intact"])
            cell.setPixmap(pixmap)
            cell.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            self.__scene.addItem(cell)

        ca_letters = ["А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "К"]
        # ca_letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
        ca_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

        font = QFont("Roboto")
        font.setPixelSize(int(self.__originalTileSize * 0.8))
        for i in range(10):
            self.__letters[i].setPlainText(ca_letters[i])
            self.__letters[i].setFont(font)
            self.__numbers[i].setPlainText(ca_numbers[i])
            self.__numbers[i].setFont(font)
            self.__scene.addItem(self.__letters[i])
            self.__scene.addItem(self.__numbers[i])

        self.__shipListItem.setPixmap(QPixmap.fromImage(self.__shipListImage))
        self.__shipListItem.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        self.__scene.addItem(self.__shipListItem)

        font.setPixelSize(int(self.__originalTileSize * 0.3))
        for _, ship in self.__shipList.items():
            t = QTransform().rotate(90)
            ship.shipItem.setPixmap(
                QPixmap.fromImage(ship.image).transformed(t))
            ship.shipItem.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            ship.counterItem.setPixmap(QPixmap.fromImage(self.__counterImage))
            ship.counterItem.setTransformationMode(
                Qt.TransformationMode.SmoothTransformation)
            ship.counterText.setPlainText(str(ship.count))
            ship.counterText.setFont(font)
            self.__scene.addItem(ship.shipItem)
            self.__scene.addItem(ship.counterItem)
            self.__scene.addItem(ship.counterText)

        self.__ghostShip.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        self.__ghostShip.setOpacity(0.7)

        pen = QPen()
        pen.setWidth(2)
        pen.setStyle(Qt.PenStyle.DashLine)
        pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
        self.__placer.setPen(pen)

    def __setCell(self, x, y, cell_type):
        if cell_type not in self.__cellImages.keys():
            raise ValueError(
                f"Type is {cell_type}. Allowed \"intact\", \"miss\", \"hit\"")

        cellItem = self.__field[y * 10 + x]
        cellItem.setData(0, cell_type)
        pixmap = QPixmap.fromImage(self.__cellImages[cell_type])
        cellItem.setPixmap(pixmap)

    def __runAnimation(self, x, y, animation, looped=False):
        sprite = SpriteItem()
        sprite.setData(0, QPoint(x, y))
        spritePixmap = QPixmap.fromImage(self.__sprites[animation])

        sprite.setSpriteMap(spritePixmap, 60, 60, 5)
        sprite.setScale(self.__scaleFactor)
        sprite.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
        sprite.setZValue(10)

        self.__spriteAnimations.append(sprite)
        self.__scene.addItem(sprite)

        def removeAnimation():
            self.__scene.removeItem(sprite)
            self.__spriteAnimations.remove(sprite)
            sprite.stopAnimation()

        sprite.startAnimation(100, looped, removeAnimation)

    def __shootAround(self, ship: Ship):
        shipCells = []
        for i in range(ship.length):
            shipCells.append(ship.pos +
                             (QPoint(0, i) if ship.vertical else QPoint(i, 0)))
        cells = []

        for cell in shipCells:
            arounds = [
                cell + QPoint(i, j) for i in range(-1, 2)
                for j in range(-1, 2)
            ]
            for candidate in arounds:
                if 0 <= candidate.x() < 10 and 0 <= candidate.y() < 10:
                    if candidate not in cells:
                        cells.append(candidate)

        for cell in cells:
            if cell not in shipCells:
                self.__setCell(cell.x(), cell.y(), "miss")
                self.__runAnimation(cell.x(), cell.y(), "splash")
        log.debug(
            f"name: {ship.name} | length: {ship.length} | pos: {ship.pos} | vertical: {ship.vertical}"
        )

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width):
        return width / self.__ratio

    def hideShipList(self):
        self.__ratio = self.RATIO_WITHOUT_SHIPLIST
        self.__scene.removeItem(self.__shipListItem)
        for _, ship in self.__shipList.items():
            self.__scene.removeItem(ship.shipItem)

        self.__adjustedToSize = None
        resize = QResizeEvent(self.size(), self.size())
        QApplication.postEvent(self, resize)

    def getPlacedShips(self):
        return self.__placedShips

    def placedShipsCount(self):
        return len(self.__placedShips)

    def hideShips(self):
        for ship in self.__placedShips:
            self.__scene.removeItem(ship)

    def removePlacedShips(self):
        if self.__shipListItem.scene() is None:
            return

        for ship in self.__placedShips:
            shipListItem = ship.data(1)
            shipListItem.count += 1
            shipListItem.counterText.setPlainText(str(shipListItem.count))
            self.__scene.removeItem(ship)

        self.__placedShips.clear()

    def shuffleShips(self):
        if self.__shipListItem.scene() is None:
            return

        self.removePlacedShips()

        def findPossibleCells(length, rotation):
            vertical = rotation.isVertical()
            if vertical == rotation.isHorizontal():
                raise Exception(
                    "Unknown state! Rotation is not horizontal and not vertical."
                )  # wtf

            width = 1 if vertical else length
            height = length if vertical else 1

            cells = []

            for x in range(10):
                for y in range(10):
                    if QRect(0, 0, 10, 10) != QRect(0, 0, 10, 10).united(
                            QRect(x, y, width, height)):
                        break
                    if self.__validatePosition(x, y, width, height):
                        cells.append(QPoint(x, y))

            return cells

        shipList = list(self.__shipList.values())
        shipList.sort(key=lambda ship: ship.length, reverse=True)
        for shipItem in shipList:
            for i in range(shipItem.count):
                rot = random.choice(list(Rotation))
                cells = findPossibleCells(shipItem.length, rot)

                if not cells:
                    rot = rot.next()
                    cells = findPossibleCells(shipItem.length, rot)

                if not cells:
                    return

                cell = random.choice(cells)
                self.__placeShip(shipItem, cell.x(), cell.y(), rot)

    def resizeEvent(self, event):
        size = event.size()
        if size == self.__adjustedToSize:
            return

        self.__adjustedToSize = size

        nowWidth = size.width()
        nowHeight = size.height()

        width = min(nowWidth, nowHeight * self.__ratio)
        height = min(nowHeight, nowWidth / self.__ratio)

        h_margin = round((nowWidth - width) / 2) - 2
        v_margin = round((nowHeight - height) / 2) - 2

        self.setContentsMargins(
            QMargins(h_margin, v_margin, h_margin, v_margin))
        self.__resizeScene()

    def scanEffect(self, x, y):
        """
        :return: True on success, False otherwise
        """
        if self.__scanEffect:
            return False

        def scanFinisedCallback():
            self.__scene.removeItem(self.__scanEffect)
            del self.__scanEffect
            self.__scanEffect = None

            target = FadingPixmapItem(self.__targetPixmap)
            target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
            target.setScale(self.__scaleFactor)
            target.setData(0, (x, y))
            self.__targets.append(target)
            self.__scene.addItem(target)

        self.__scanEffect = FloatingGradientItem(
            QRectF(self.tileSize, self.tileSize, self.__originalTileSize * 10,
                   self.__originalTileSize * 10), scanFinisedCallback)

        self.__scanEffect.setScale(self.__scaleFactor)

        self.__scanEffect.setBackwards(True)
        self.__scene.addItem(self.__scanEffect)
        return True

    def __resizeScene(self):
        width = self.__ui.graphicsView.width()
        height = self.__ui.graphicsView.height()
        self.__scene.setSceneRect(0, 0, width, height)

        self.tileSize = min(width / 11, height / (11 / self.__ratio))
        self.__scaleFactor = self.tileSize / self.__originalTileSize

        for i, cell in enumerate(self.__field):
            x = i % 10
            y = i // 10
            cell.setScale(self.__scaleFactor)
            cell.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)

        for sprite in self.__spriteAnimations:
            pos = sprite.data(0)
            sprite.setPos((pos.x() + 1) * self.tileSize,
                          (pos.y() + 1) * self.tileSize)
            sprite.setScale(self.__scaleFactor)

        for i in range(10):
            letter = self.__letters[i]
            letter.setScale(self.__scaleFactor)
            offsetX = (self.tileSize -
                       letter.boundingRect().width() * self.__scaleFactor) / 2
            offsetY = (self.tileSize -
                       letter.boundingRect().height() * self.__scaleFactor) / 2
            letter.setPos((i + 1) * self.tileSize + offsetX, offsetY)

            number = self.__numbers[i]
            number.setScale(self.__scaleFactor)
            offsetX = (self.tileSize -
                       number.boundingRect().width() * self.__scaleFactor) / 2
            offsetY = (self.tileSize -
                       number.boundingRect().height() * self.__scaleFactor) / 2
            number.setPos(offsetX, (i + 1) * self.tileSize + offsetY)

        xPos = 0
        for _, ship in self.__shipList.items():
            xPos += (ship.length - 1)
            xOffset = xPos * self.tileSize

            ship.shipItem.setScale(self.__scaleFactor)
            ship.shipItem.setPos(self.tileSize + xOffset,
                                 self.tileSize * (12 + self.EPS))

            ship.counterItem.setScale(self.__scaleFactor)
            ship.counterItem.setPos(self.tileSize + xOffset,
                                    self.tileSize * (12.65 + self.EPS))

            counterSize = ship.counterItem.boundingRect()
            textSize = ship.counterText.boundingRect()
            textXOffset = (counterSize.width() -
                           textSize.width()) * self.__scaleFactor / 2
            textYOffset = (counterSize.height() -
                           textSize.height()) * self.__scaleFactor / 2
            textX = ship.counterItem.pos().x() + textXOffset
            textY = ship.counterItem.pos().y() + textYOffset
            ship.counterText.setScale(self.__scaleFactor)
            ship.counterText.setPos(textX, textY)

        for ship in self.__placedShips:
            mapPos = ship.data(2)
            ship.setPos((mapPos.x() + 1) * self.tileSize,
                        (mapPos.y() + 1) * self.tileSize)
            ship.setScale(self.__scaleFactor)

        for target in self.__targets:
            x, y = target.data(0)
            target.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)
            target.setScale(self.__scaleFactor)

        if self.__scanEffect:
            self.__scanEffect.setPos(self.tileSize, self.tileSize)
            self.__scanEffect.setScale(self.__scaleFactor)

        shipListX = self.tileSize
        shipListY = self.tileSize * (11 + self.EPS)
        self.__shipListItem.setScale(self.__scaleFactor)
        self.__shipListItem.setPos(shipListX, shipListY)
        self.__ghostShip.setScale(self.__scaleFactor)

    def eventFilter(self, obj, event):
        if obj is self.__ui.graphicsView.viewport():
            if event.type() == QEvent.MouseButtonPress:
                self.__viewportMousePressEvent(event)
            elif event.type() == QEvent.MouseButtonRelease:
                self.__viewportMouseReleaseEvent(event)
            elif event.type() == QEvent.MouseMove:
                self.__viewportMouseMoveEvent(event)

        return super().eventFilter(obj, event)

    def sceneToMap(self, x, y):
        x -= self.tileSize
        y -= self.tileSize
        x //= self.tileSize
        y //= self.tileSize
        return int(x), int(y)

    def __initGhostShip(self, ship, pos):
        self.__ghostShip.setPixmap(QPixmap.fromImage(ship.image))
        self.__ghostShip.setPos(pos)
        self.__ghostShip.setRotation(0)

        width = self.__ghostShip.boundingRect().width()
        height = self.__ghostShip.boundingRect().height()
        self.__ghostShip.setOffset(-width / 2, -height / 2)
        self.__ghostShip.setData(0, Rotation.UP)
        self.__ghostShip.setData(1, ship)

        self.__placer.setRect(0, 0, self.tileSize, self.tileSize * ship.length)
        self.__placer.setZValue(50)

        self.__scene.addItem(self.__ghostShip)
        self.__ghostShip.setZValue(100)

    def __rotateGhostShip(self, rotation=None):
        rotation = rotation if rotation else self.__ghostShip.data(0).next()
        self.__ghostShip.setData(0, rotation)
        self.__ghostShip.setRotation(rotation.value)

        placerRect = self.__placer.rect()
        maxSide = max(placerRect.width(), placerRect.height())
        minSide = min(placerRect.width(), placerRect.height())

        if rotation.isHorizontal():
            self.__placer.setRect(0, 0, maxSide, minSide)
        elif rotation.isVertical():
            self.__placer.setRect(0, 0, minSide, maxSide)
        else:
            raise Exception(
                "Unknown state! Rotation is not horizontal and not vertical."
            )  # wtf
        self.__validatePlacer()

    def __ghostShipLongSurface(self):
        pos = self.__ghostShip.pos()
        x = pos.x()
        y = pos.y()
        rot: Rotation = self.__ghostShip.data(0)
        length = self.__ghostShip.data(1).length
        if rot.isHorizontal():
            x -= self.tileSize * length / 2
            return x, y

        if rot.isVertical():
            y -= self.tileSize * length / 2
            return x, y

    def __validatePosition(self, x, y, width=1, height=1):
        positionRect = QRectF((x + 1) * self.tileSize, (y + 1) * self.tileSize,
                              self.tileSize * width, self.tileSize * height)

        isPlacerValid = True
        for ship in self.__placedShips:
            shipRect = ship.mapRectToScene(ship.boundingRect()).adjusted(
                -self.tileSize / 2, -self.tileSize / 2, self.tileSize / 2,
                self.tileSize / 2)
            isPlacerValid = not positionRect.intersects(shipRect)
            if not isPlacerValid:
                break
        return isPlacerValid

    def __validatePlacer(self):
        sceneX, sceneY = self.__ghostShipLongSurface()
        x, y = self.sceneToMap(sceneX, sceneY)
        self.__placer.setPos((x + 1) * self.tileSize, (y + 1) * self.tileSize)

        permittedArea = QRectF(self.__ui.graphicsView.viewport().geometry())
        permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize))
        permittedArea.setBottomRight(
            QPointF(self.tileSize * 12, self.tileSize * 12))
        placerSize = self.__placer.boundingRect()
        placerRect = QRectF(sceneX, sceneY, placerSize.width(),
                            placerSize.height())

        isPlacerValid = False
        # first validation - ship can be placed inside game field
        if permittedArea.contains(self.__ghostShip.pos(
        )) and permittedArea == permittedArea.united(placerRect):
            if self.__placer.scene() == None:
                self.__scene.addItem(self.__placer)
            x, y = self.sceneToMap(sceneX, sceneY)
            self.__placer.setPos((x + 1) * self.tileSize,
                                 (y + 1) * self.tileSize)
            isPlacerValid = True

        else:
            if self.__placer.scene() == self.__scene:
                self.__scene.removeItem(self.__placer)

        # second validation - placer does not intersect with other ships
        if isPlacerValid:
            isPlacerValid = self.__validatePosition(
                x, y,
                placerSize.width() / self.tileSize,
                placerSize.height() / self.tileSize)

        # set color of placer
        pen = self.__placer.pen()
        if isPlacerValid:
            pen.setColor(Qt.GlobalColor.darkGreen)
        else:
            pen.setColor(Qt.GlobalColor.red)
        self.__placer.setPen(pen)
        self.__placer.setData(0, isPlacerValid)

    def __placeShip(self, shipListItem, x, y, rotation):
        sceneX, sceneY = (x + 1) * self.tileSize, (y + 1) * self.tileSize

        shipListItem.count -= 1
        shipListItem.counterText.setPlainText(str(shipListItem.count))

        pixmap = QPixmap(shipListItem.image).transformed(QTransform().rotate(
            (rotation.value)))
        placedShip = QGraphicsPixmapItem(pixmap)
        placedShip.setData(0, rotation)
        placedShip.setData(1, shipListItem)
        placedShip.setData(2, QPoint(x, y))  # position in map coordinates

        placedShip.setPos(sceneX, sceneY)
        placedShip.setTransformationMode(
            Qt.TransformationMode.SmoothTransformation)
        placedShip.setScale(self.__scaleFactor)

        self.__placedShips.append(placedShip)
        self.__scene.addItem(placedShip)

    def __placeGhostShip(self):
        isPlacingPermitted = self.__placer.data(0)
        if isPlacingPermitted:
            sceneX = self.__placer.pos().x() + self.tileSize / 2
            sceneY = self.__placer.pos().y() + self.tileSize / 2
            mapX, mapY = self.sceneToMap(sceneX, sceneY)
            rotation = self.__ghostShip.data(0)
            if rotation.isVertical():
                vertical = True
            elif rotation.isHorizontal():
                vertical = False
            else:
                raise Exception(
                    "Unknown state! Rotation is not horizontal and not vertical."
                )  # wtf

            shipListItem = self.__ghostShip.data(1)
            self.__placeShip(shipListItem, mapX, mapY, rotation)

            log.debug(
                f"ship \"{shipListItem.name}\"; "
                f"position ({mapX}, {mapY}); "
                f"oriented {'vertically' if vertical else 'horizontally'}")

            self.shipPlaced.emit(
                Ship(name=shipListItem.name,
                     length=shipListItem.length,
                     pos=QPoint(mapX, mapY),
                     vertical=vertical))

    def __viewportMousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            if self.__shipListItem.scene():
                # check press on shiplist
                shipUnderMouse = None
                for _, ship in self.__shipList.items():
                    if ship.shipItem.isUnderMouse():
                        shipUnderMouse = ship
                        break

                # check press on field
                rotation = Rotation.RIGHT
                if shipUnderMouse == None:
                    for ship in self.__placedShips:
                        if ship.isUnderMouse():
                            rotation = ship.data(0)
                            shipListItem = ship.data(1)

                            if shipListItem.count == 0:
                                shipListItem.shipItem.setGraphicsEffect(None)

                            shipListItem.count += 1
                            shipListItem.counterText.setPlainText(
                                str(shipListItem.count))
                            shipUnderMouse = shipListItem
                            self.__placedShips.remove(ship)
                            self.__scene.removeItem(ship)
                            break

                # if ship grabbed
                if shipUnderMouse and shipUnderMouse.count > 0:
                    self.__initGhostShip(shipUnderMouse, event.pos())
                    self.__rotateGhostShip(rotation)
                    self.__dragShip = True

            x, y = self.sceneToMap(event.pos().x(), event.pos().y())
            if x >= 0 and x < 10 and y >= 0 and y < 10:
                if self.controller.isBot:
                    return
                self.controller.emitHit(x, y)

        if event.button() == Qt.MouseButton.RightButton:
            if self.__dragShip:
                self.__rotateGhostShip()

    def __viewportMouseReleaseEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            if self.__dragShip:
                self.__scene.removeItem(self.__ghostShip)
                self.__dragShip = False
            if self.__placer.scene() != None:
                self.__scene.removeItem(self.__placer)

            # self.__placeShip()
            self.__placeGhostShip()
            self.__placer.setData(0, False)

    def __viewportMouseMoveEvent(self, event):
        if self.__dragShip:
            self.__ghostShip.setPos(event.pos())

            permittedArea = QRectF(
                self.__ui.graphicsView.viewport().geometry())
            permittedArea.setTopLeft(QPointF(self.tileSize, self.tileSize))

            # stop dragging ship if mouse out of field
            # if not permittedArea.contains(event.pos()):
            #     self.__scene.removeItem(self.__ghostShip)
            #     self.__dragShip = False

            self.__validatePlacer()

    def __accept(self, x, y, hit_type: CellState):
        log.debug(
            f" -- ACCEPTED -- hit on point ({x}, {y}) hit type: {hit_type}")
        cell = self.__field[y * 10 + x]

        if cell.data(0) != "intact":
            return
        if hit_type in [CellState.HIT, CellState.KILLED]:
            self.__setCell(x, y, 'hit')
            self.__runAnimation(x, y, "explosion", looped=True)
            for target in self.__targets:
                if (x, y) == target.data(0):
                    self.__scene.removeItem(target)
                    self.__targets.remove(target)
                    break
        elif hit_type in [CellState.MISS]:
            self.__setCell(x, y, 'miss')
            self.__runAnimation(x, y, "splash", looped=False)

        if hit_type == CellState.KILLED:
            for ship in self.__placedShips:
                rotation = ship.data(0)
                length = ship.data(1).length
                position = ship.data(2)
                width = 1 if rotation.isVertical() else length
                height = length if rotation.isVertical() else 1
                rect = QRect(position.x(), position.y(), width, height)
                if rect.contains(x, y):
                    self.__scene.addItem(ship)

    def __decline(self, x, y):
        log.debug(f"declined hit on point ({x}, {y})")
Esempio n. 10
0
    def refresh(self):
        if not self._mdlPlots or not self._mdlOutline or not self._mdlCharacter:
            return

        if not self.isVisible():
            return

        LINE_HEIGHT = 18
        SPACING = 3
        TEXT_WIDTH = self.sldTxtSize.value()
        CIRCLE_WIDTH = 10
        LEVEL_HEIGHT = 12

        s = self.scene
        s.clear()

        # Get Max Level (max depth)
        root = self._mdlOutline.rootItem
        def maxLevel(item, level=0, max=0):
            if level > max:
                max = level
            for c in item.children():
                m = maxLevel(c, level + 1)
                if m > max:
                    max = m
            return max

        MAX_LEVEL = maxLevel(root)

        # Get the list of tracked items (array of references)
        trackedItems = []

        if self.actPlots.isChecked():
            trackedItems += self.plotReferences()

        if self.actCharacters.isChecked():
            trackedItems += self.charactersReferences()

        ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )

        fm = QFontMetrics(s.font())
        max_name = 0
        for ref in trackedItems:
            name = references.title(ref)
            max_name = max(fm.width(name), max_name)

        TITLE_WIDTH = max_name + 2 * SPACING

        # Add Folders and Texts
        outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
        s.addItem(outline)
        outline.setPos(TITLE_WIDTH + SPACING, 0)

        refCircles = [] # a list of all references, to be added later on the lines

        # A Function to add a rect with centered elided text
        def addRectText(x, w, parent, text="", level=0, tooltip=""):
            deltaH = LEVEL_HEIGHT if level else 0
            r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text)
            r.setPos(x, deltaH)

            txt = QGraphicsSimpleTextItem(text, r)
            f = txt.font()
            f.setPointSize(8)
            fm = QFontMetricsF(f)
            elidedText = fm.elidedText(text, Qt.ElideMiddle, w)
            txt.setFont(f)
            txt.setText(elidedText)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
            txt.setY(0)
            return r

        # A function to returns an item's width, by counting its children
        def itemWidth(item):
            if item.isFolder():
                r = 0
                for c in item.children():
                    r += itemWidth(c)
                return r or TEXT_WIDTH
            else:
                return TEXT_WIDTH

        def listItems(item, rect, level=0):
            delta = 0
            for child in item.children():
                w = itemWidth(child)

                if child.isFolder():
                    parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title())
                    parent.setToolTip(references.tooltip(references.textReference(child.ID())))
                    listItems(child, parent, level + 1)

                else:
                    rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title())
                    rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
                    
                    # Find tracked references in that scene (or parent folders)
                    for ref in trackedItems:

                        result = []

                        # Tests if POV
                        scenePOV = False  # Will hold true of character is POV of the current text, not containing folder
                        if references.type(ref) == references.CharacterLetter:
                            ID = references.ID(ref)
                            c = child
                            while c:
                                if c.POV() == ID:
                                    result.append(c.ID())
                                    if c == child: scenePOV = True
                                c = c.parent()

                        # Search in notes/references
                        c = child
                        while c:
                            result += references.findReferencesTo(ref, c, recursive=False)
                            c = c.parent()

                        if result:
                            ref2 = result[0]
                            
                            # Create a RefCircle with the reference
                            c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2, important=scenePOV)
                            
                            # Store it, with the position of that item, to display it on the line later on
                            refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))

                delta += w

        listItems(root, outline)

        OUTLINE_WIDTH = itemWidth(root)

        # Add Tracked items
        i = 0
        itemsRect = s.addRect(0, 0, 0, 0)
        itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)

        # Set of colors for plots (as long as they don't have their own colors)
        colors = [
            "#D97777", "#AE5F8C", "#D9A377", "#FFC2C2", "#FFDEC2", "#D2A0BC",
            "#7B0F0F", "#7B400F", "#620C3D", "#AA3939", "#AA6C39", "#882D61",
            "#4C0000", "#4C2200", "#3D0022",
        ]

        for ref in trackedItems:
            if references.type(ref) == references.CharacterLetter:
                color = self._mdlCharacter.getCharacterByID(references.ID(ref)).color()
            else:
                color = QColor(colors[i % len(colors)])

            # Rect
            r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
            r.setPen(QPen(Qt.NoPen))
            r.setBrush(QBrush(color))
            r.setPos(0, i * LINE_HEIGHT + i * SPACING)
            r.setToolTip(references.tooltip(ref))
            i += 1

            # Text
            name = references.title(ref)
            txt = QGraphicsSimpleTextItem(name, r)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())

            # Line
            line = PlotLine(0, 0,
                            OUTLINE_WIDTH + SPACING, 0)
            line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
            s.addItem(line)
            line.setPen(QPen(color, 5))
            line.setToolTip(references.tooltip(ref))

            # We add the circles / references to text, on the line
            for ref2, circle, pos in refCircles:
                if ref2 == ref:
                    circle.setParentItem(line)
                    circle.setPos(pos.x(), 0)

        # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio
        self.view.setSceneRect(0, 0, 0, 0)
Esempio n. 11
0
    def refresh(self):
        if not self._mdlPlots or not self._mdlOutline or not self._mdlPersos:
            pass

        LINE_HEIGHT = 18
        SPACING = 3
        TEXT_WIDTH = self.sldTxtSize.value()
        CIRCLE_WIDTH = 10
        LEVEL_HEIGHT = 12

        s = self.scene
        s.clear()

        # Get Max Level (max depth)
        root = self._mdlOutline.rootItem
        def maxLevel(item, level=0, max=0):
            if level > max:
                max = level
            for c in item.children():
                m = maxLevel(c, level + 1)
                if m > max:
                    max = m
            return max

        MAX_LEVEL = maxLevel(root)

        # Generate left entries
        # (As of now, plot only)
        plotsID = self._mdlPlots.getPlotsByImportance()
        trackedItems = []
        fm = QFontMetrics(s.font())
        max_name = 0

        for importance in plotsID:
            for ID in importance:
                name = self._mdlPlots.getPlotNameByID(ID)
                ref = references.plotReference(ID, searchable=True)

                trackedItems.append((ID, ref, name))
                max_name = max(fm.width(name), max_name)

        ROWS_HEIGHT = len(trackedItems) * (LINE_HEIGHT + SPACING )
        TITLE_WIDTH = max_name + 2 * SPACING


        # Add Folders and Texts
        outline = OutlineRect(0, 0, 0, ROWS_HEIGHT + SPACING + MAX_LEVEL * LEVEL_HEIGHT)
        s.addItem(outline)
        outline.setPos(TITLE_WIDTH + SPACING, 0)

        refCircles = [] # a list of all references, to be added later on the lines

        # A Function to add a rect with centered elided text
        def addRectText(x, w, parent, text="", level=0, tooltip=""):
            deltaH = LEVEL_HEIGHT if level else 0
            r = OutlineRect(0, 0, w, parent.rect().height()-deltaH, parent, title=text)
            r.setPos(x, deltaH)

            txt = QGraphicsSimpleTextItem(text, r)
            f = txt.font()
            f.setPointSize(8)
            fm = QFontMetricsF(f)
            elidedText = fm.elidedText(text, Qt.ElideMiddle, w)
            txt.setFont(f)
            txt.setText(elidedText)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())
            txt.setY(0)
            return r

        # A function to returns an item's width, by counting its children
        def itemWidth(item):
            if item.isFolder():
                r = 0
                for c in item.children():
                    r += itemWidth(c)
                return r or TEXT_WIDTH
            else:
                return TEXT_WIDTH

        def listItems(item, rect, level=0):
            delta = 0
            for child in item.children():
                w = itemWidth(child)

                if child.isFolder():
                    parent = addRectText(delta, w, rect, child.title(), level, tooltip=child.title())
                    parent.setToolTip(references.tooltip(references.textReference(child.ID())))
                    listItems(child, parent, level + 1)

                else:
                    rectChild = addRectText(delta, TEXT_WIDTH, rect, "", level, tooltip=child.title())
                    rectChild.setToolTip(references.tooltip(references.textReference(child.ID())))
                    
                    # Find tracked references in that scene (or parent folders)
                    for ID, ref, name in trackedItems:

                        result = []
                        c = child
                        while c:
                            result += references.findReferencesTo(ref, c, recursive=False)
                            c = c.parent()

                        if result:
                            ref2 = result[0]
                            
                            # Create a RefCircle with the reference
                            c = RefCircle(TEXT_WIDTH / 2, - CIRCLE_WIDTH / 2, CIRCLE_WIDTH, ID=ref2)
                            
                            # Store it, with the position of that item, to display it on the line later on
                            refCircles.append((ref, c, rect.mapToItem(outline, rectChild.pos())))

                delta += w

        listItems(root, outline)

        OUTLINE_WIDTH = itemWidth(root)

        # Add Plots
        i = 0
        itemsRect = s.addRect(0, 0, 0, 0)
        itemsRect.setPos(0, MAX_LEVEL * LEVEL_HEIGHT + SPACING)

        for ID, ref, name in trackedItems:
            color = randomColor()

            # Rect
            r = QGraphicsRectItem(0, 0, TITLE_WIDTH, LINE_HEIGHT, itemsRect)
            r.setPen(QPen(Qt.NoPen))
            r.setBrush(QBrush(color))
            r.setPos(0, i * LINE_HEIGHT + i * SPACING)
            i += 1

            # Text
            txt = QGraphicsSimpleTextItem(name, r)
            txt.setPos(r.boundingRect().center() - txt.boundingRect().center())

            # Line
            line = PlotLine(0, 0,
                            OUTLINE_WIDTH + SPACING, 0)
            line.setPos(TITLE_WIDTH, r.mapToScene(r.rect().center()).y())
            s.addItem(line)
            line.setPen(QPen(color, 5))
            line.setToolTip(self.tr("Plot: ") + name)

            # We add the circles / references to text, on the line
            for ref2, circle, pos in refCircles:
                if ref2 == ref:
                    circle.setParentItem(line)
                    circle.setPos(pos.x(), 0)

        # self.view.fitInView(0, 0, TOTAL_WIDTH, i * LINE_HEIGHT, Qt.KeepAspectRatioByExpanding) # KeepAspectRatio
        self.view.setSceneRect(0, 0, 0, 0)
Esempio n. 12
0
class SelectAreaDialog(QDialog):

    finish_selecting_area = pyqtSignal(TrimmingData)

    def __init__(self):
        super().__init__()
        self.ui = Ui_SelectAreaDialog()
        self.ui.setupUi(self)

        self.width = 200
        self.height = 200
        self.h, self.w = None, None
        self.select_area = None
        self.original_image_scene = None
        self.size_flag = True
        self.select_area_label = None
        self.select_area_label_proxy = None
        self.start_position = None

        self.get_ng_sample_image_path()

        if self.h <= self.height and self.w <= self.width:
            self.size_flag = False

        if self.size_flag:
            self.show_select_area_at_default_position()
        else:
            self.ui.notation_label.setText('この画像サイズは十分小さいため, 画像全体でトレーニングを行います.'
                                           '\nこのままトレーニング開始ボタンを押してください.')
            pass

        self.ui.ok_button.clicked.connect(self.on_clicked_ok_button)
        self.ui.cancel_button.clicked.connect(self.on_clicked_cancel_button)

    def get_ng_sample_image_path(self):
        test_ng_path = str(Dataset.images_path(Dataset.Category.TEST_NG))
        test_ng_images = os.listdir(test_ng_path)
        test_ng_images = [
            img for img in test_ng_images
            if Path(img).suffix in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
        ]
        if not test_ng_images:
            return
        original_image_path = os.path.join(test_ng_path, test_ng_images[0])
        original_image = cv2.imread(original_image_path)
        h, w, c = original_image.shape
        self.h, self.w = h, w
        original_image_shape = QSize(w + 2, h + 10)
        original_image_item = QGraphicsPixmapItem(QPixmap(original_image_path))
        original_image_item.setZValue(0)
        self.original_image_scene = QGraphicsScene()
        self.original_image_scene.addItem(original_image_item)
        self.ui.original_image_view.setScene(self.original_image_scene)
        self.ui.original_image_view.setBaseSize(original_image_shape)
        self.ui.original_image_view.setMaximumSize(original_image_shape)
        self.resize(self.w + 32, self.h + 72)

    def show_select_area_at_default_position(self):
        trimming_data = Project.latest_trimming_data()
        if trimming_data.position:
            self.start_position = QPoint(trimming_data.position[0],
                                         trimming_data.position[1])
            rect = QRectF(trimming_data.position[0], trimming_data.position[1],
                          self.width, self.height)
        else:
            self.start_position = QPoint((self.w - self.width) // 2,
                                         (self.h - self.height) // 2)
            rect = QRectF((self.w - self.width) // 2,
                          (self.h - self.height) // 2, self.width, self.height)
        self.select_area = QGraphicsRectItem(rect)
        self.select_area.setZValue(1)
        pen = QPen(QColor('#ffa00e'))
        pen.setWidth(4)
        pen.setJoinStyle(Qt.RoundJoin)
        self.select_area.setPen(pen)
        self.select_area.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.original_image_scene.addItem(self.select_area)
        self.select_area_label_proxy = QGraphicsProxyWidget(self.select_area)
        self.select_area_label = SelectAreaLabel()
        self.select_area_label.set_label()
        self.select_area_label_proxy.setWidget(self.select_area_label)
        self.select_area_label_proxy.setPos(
            self.select_area.boundingRect().left() + 2,
            self.select_area.boundingRect().bottom() -
            self.select_area_label.height() - 2)

    def on_clicked_ok_button(self):
        if not self.size_flag:
            trimming_data = TrimmingData(position=(0, 0),
                                         size=(self.w, self.h),
                                         needs_trimming=False)
            self.finish_selecting_area.emit(trimming_data)
            self.close()
        else:
            rel_position = self.select_area.pos()
            position = (self.start_position.x() + rel_position.x(),
                        self.start_position.y() + rel_position.y())
            if position[0] < 0 or position[
                    0] > self.w - self.width - 1 or position[
                        1] < 0 or position[1] > self.h - self.height - 1:
                print('Error: Please set area contained in the image.')
                self.ui.notation_label.setText('エラー: 切り取る領域は画像内に収まるようにしてください.')
            else:
                trimming_data = TrimmingData(position=position,
                                             size=(self.width, self.height),
                                             needs_trimming=True)
                self.finish_selecting_area.emit(trimming_data)
                self.close()

    def on_clicked_cancel_button(self):
        self.close()

    def closeEvent(self, QCloseEvent):
        self.close()
Esempio n. 13
0
class SvgView(QGraphicsView):
    """
    SVG display taken from the PyQt5 examples (see license.txt)
    """
    Native, OpenGL, Image = range(3)

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

        self.renderer = SvgView.Native
        self.svgItem = None
        self.backgroundItem = None
        self.outlineItem = None
        self.image = QImage()

        self.setScene(QGraphicsScene(self))
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)

        self.TRANSLUCENT_WHITE = QColor(255, 255, 255, 180)
        # Prepare background check-board pattern.
        tilePixmap = QPixmap(64, 64)
        tilePixmap.fill(Qt.white)
        tilePainter = QPainter(tilePixmap)
        color = QColor(220, 220, 220)
        tilePainter.fillRect(0, 0, 32, 32, color)
        tilePainter.fillRect(32, 32, 32, 32, color)
        tilePainter.end()

        self.setBackgroundBrush(QBrush(tilePixmap))
        self.num_x = 1000
        self.num_y = 1500

        # zoom tracker
        self.zoom = Zoom()

        # position tracker
        self.x = self.width() / 2
        self.y = self.height() / 2
        self.r = 10
        # scales when zooming
        #self.r_mult = 1

    def openFile(self, svg_file):
        if not svg_file.exists():
            return

        s = self.scene()

        if self.backgroundItem:
            drawBackground = self.backgroundItem.isVisible()
        else:
            drawBackground = False

        if self.outlineItem:
            drawOutline = self.outlineItem.isVisible()
        else:
            drawOutline = True

        s.clear()
        self.resetTransform()

        self.svgItem = MapItem(svg_file.fileName())
        self.svgItem.setFlags(QGraphicsItem.ItemClipsToShape)
        self.svgItem.setCacheMode(QGraphicsItem.NoCache)
        self.svgItem.setZValue(0)

        self.backgroundItem = QGraphicsRectItem(self.svgItem.boundingRect())
        self.backgroundItem.setBrush(Qt.white)
        self.backgroundItem.setPen(QPen(Qt.NoPen))
        self.backgroundItem.setVisible(drawBackground)
        self.backgroundItem.setZValue(-1)

        self.outlineItem = QGraphicsRectItem(self.svgItem.boundingRect())
        outline = QPen(Qt.black, 2, Qt.DashLine)
        outline.setCosmetic(True)
        self.outlineItem.setPen(outline)
        self.outlineItem.setBrush(QBrush(Qt.NoBrush))
        self.outlineItem.setVisible(drawOutline)
        self.outlineItem.setZValue(1)

        s.addItem(self.backgroundItem)
        s.addItem(self.svgItem)
        s.addItem(self.outlineItem)

        self.svgItem.setRadius(self.r)
        self.svgItem.makeGrid(self.num_x, self.num_y,
                              self.svgItem.boundingRect().width(),
                              self.svgItem.boundingRect().height())
        self.svgItem.resetGrid()

        s.setSceneRect(self.outlineItem.boundingRect().adjusted(-10, -10, 10, 10))
        self.cursor_circle = fillCircle(self.outlineItem.scene(), self.x, self.y,
                                        self.r,
                                        self.TRANSLUCENT_WHITE)

    def setHighQualityAntialiasing(self, highQualityAntialiasing):
        if QGLFormat.hasOpenGL():
            self.setRenderHint(QPainter.HighQualityAntialiasing,
                    highQualityAntialiasing)

    def removeItem(self, item):
        self.scene().removeItem(item)

    def setRadius(self, r):
        self.r = r
        self.svgItem.setRadius(r)

    def setViewBackground(self, enable):
        if self.backgroundItem:
            self.backgroundItem.setVisible(enable)

    def setViewOutline(self, enable):
        if self.outlineItem:
            self.outlineItem.setVisible(enable)

    def paintEvent(self, event):
        super(SvgView, self).paintEvent(event)
        p = QPainter(self.viewport())

    def updateCursorCircle(self):
        self.removeItem(self.cursor_circle)
        transformedPos = self.mapToScene(self.pos)
        x = transformedPos.x()
        y = transformedPos.y()
        self.cursor_circle = fillCircle(self.outlineItem.scene(), x, y,
                                        self.r,
                                        self.TRANSLUCENT_WHITE)

    def mouseMoveEvent(self, event):
        super(SvgView, self).mouseMoveEvent(event)
        self.pos = event.pos() #QCursor.pos()
        self.updateCursorCircle()

    def wheelEvent(self, event):
        self.zoom.change_scroll(event.angleDelta().y())
        factor = pow(1.1, self.zoom.zoom_delta())
        self.scale(factor, factor)
        # TODO: test floating point problems with this implementation
        # (repeatedly zoom in and out and see if it becomes inaccurate)
        #self.r_mult = self.r_mult * factor
        self.updateCursorCircle()
        event.accept()

    def keyPressEvent(self, event):
        super(SvgView, self).keyPressEvent(event)
        if event.key() == Qt.Key_Plus:
            self.setRadius(self.r * 1.2)
            self.updateCursorCircle()
        elif event.key() == Qt.Key_Minus:
            self.setRadius(self.r / 1.2)
            self.updateCursorCircle()
Esempio n. 14
0
class Board(QGraphicsView):
    def __init__(self, parent, config, currentMap, currentStage, bridge,
                 gameTypeData, playerData):
        QGraphicsView.__init__(self, parent)
        self.config = config
        self.currentMap = currentMap
        self.currentStage = currentStage
        self.bridge = bridge
        self.gameTypeData = gameTypeData
        self.isOnline = gameTypeData.isOnline
        self.numOfPlayers = gameTypeData.numOfPlayers
        self.playerData = playerData
        self.isGameOver = False
        # set up player and enemy details
        # incremented when generating players (used on local)
        self.playersAlive = 0
        self.playerColors = ["yellow", "green"]
        self.playerLevels = self.config.playerLevels
        self.playerWrappers = {}
        self.enemyColor = "gray"
        self.enemiesEtds = {}
        self.enemies = {}
        self.enemySpawnRegionWidth = self.config.enemySpawnRegionWidth
        self.enemySpawnRegionHeight = self.config.enemySpawnRegionHeight
        self.enemySpawnInterval = 3000
        self.maxEnemyCnt = 20
        self.currentEnemyCnt = 0
        self.maxEnemiesCurrentlyAlive = 6
        self.enemiesCurrentlyAlive = 0

        # ENEMY MOVEMENT TIMERS
        self.enemyMovementTimers = {}
        self.slowEnemyMovementTimer = QTimer()
        self.slowEnemyMovementTimer.setTimerType(Qt.PreciseTimer)
        self.slowEnemyMovementTimer.setInterval(
            self.config.enemyMovementSpeedMap["slow"])
        self.slowEnemyMovementTimer.start()
        self.enemyMovementTimers["slow"] = self.slowEnemyMovementTimer

        self.normalEnemyMovementTimer = QTimer()
        self.normalEnemyMovementTimer.setTimerType(Qt.PreciseTimer)
        self.normalEnemyMovementTimer.setInterval(
            self.config.enemyMovementSpeedMap["normal"])
        self.normalEnemyMovementTimer.start()
        self.enemyMovementTimers["normal"] = self.normalEnemyMovementTimer

        self.fastEnemyMovementTimer = QTimer()
        self.fastEnemyMovementTimer.setTimerType(Qt.PreciseTimer)
        self.fastEnemyMovementTimer.setInterval(
            self.config.enemyMovementSpeedMap["fast"])
        self.fastEnemyMovementTimer.start()
        self.enemyMovementTimers["fast"] = self.fastEnemyMovementTimer

        # ENEMY SHOOTING TIMER
        self.enemyShootingTimer = QTimer()
        self.enemyShootingTimer.setTimerType(Qt.PreciseTimer)
        self.enemyShootingTimer.setInterval(self.config.enemyShootInterval)
        self.enemyShootingTimer.start()

        # SET UP RANDOM ENEMY SPAWNING TIMER
        self.enemySpawnTimer = QTimer()
        self.enemySpawnTimer.setTimerType(Qt.PreciseTimer)
        self.enemySpawnTimer.timeout.connect(self.generateEnemy)
        self.enemySpawnTimer.setInterval(self.enemySpawnInterval)
        self.enemySpawnTimer.start()

        # BULLET REFRESH RATE
        # i've chose not to make different timers for different speeds just to save
        # on resources and computational power
        # difference from tank movement is that tanks constantly move by 1px
        # and bullets don't need to so we change the number of pixel as the movement pace for bullets
        self.bulletTimer = QTimer()
        self.bulletTimer.setTimerType(Qt.PreciseTimer)
        self.bulletTimer.setInterval(10)
        self.bulletTimer.start()

        # movement animation timer
        self.animationTimer = QTimer()
        self.animationTimer.setTimerType(Qt.PreciseTimer)
        self.animationTimer.setInterval(100)
        self.animationTimer.start()

        # each player and enemy will have this emitter passed to them
        # and will give it to each bullet so the bullet can signal when an enemy has been killed
        self.killEmitter = KillEmitter()
        self.killEmitter.emitKillSignal.connect(self.killEmitterHandler)

        # player dead emitter
        # each player has one and when the player has no more lives, it emits this signal
        self.playerDeadEmitter = PlayerDeadEmitter()
        self.playerDeadEmitter.playerDeadSignal.connect(
            self.playerDeadEmitterHandler)

        # explosion sound whenever an enemy is destroyed
        self.enemyExplosionSound = oalOpen(self.config.sounds["explosion"])
        # explosion sound whenever a player is destroyed
        self.playerExplosionSound = oalOpen(
            self.config.sounds["playerExplosion"])
        # # # moving sound
        # self.movementSound = oalOpen(self.config.sounds["tankMoving"])
        # self.movementSound.set_looping(True)
        # self.movementSound.set_gain(30.0)
        # # not moving sound
        # self.nonMovementSound = oalOpen(self.config.sounds["tankNotMoving"])
        # self.nonMovementSound.set_looping(True)
        # self.nonMovementSound.set_gain(30.0)

        # initialize board ui
        self.__init_ui__()

        # 34 is the max width of the enemy tank
        self.randomEnemyPositions = [
            self.field.x(),
            self.field.x() + (self.field.boundingRect().width() - 1) / 2,
            # '-34' doesn't let enemies spawn outside the field
            self.field.x() + self.field.boundingRect().width() - 34
        ]

        # GAME OVER setup
        self.gameOverEmitter = GameOverEmitter()
        self.gameOverEmitter.gameOverSignal.connect(self.gameOverHandler)
        # game over animation
        # we need to keep the gameOverAnimation alive so it can animate
        self.gameOver = GameOver(self.config.gameOverTexture)
        self.gameOver.setZValue(3)
        self.gameOver.setPos(QPointF(150, self.fieldBottom + 50))
        self.gameOverTimer = QTimer()
        self.gameOverTimer.setTimerType(Qt.PreciseTimer)
        self.gameOverTimer.setInterval(15)
        self.gameOverTimer.timeout.connect(self.animateGameOver)
        self.gameOverSound = oalOpen(self.config.sounds["gameOver"])

        # deus ex spawner and its possible locations
        self.deusExLocations = [
            QPointF(self.field.x(), self.field.y()),
            QPointF(self.field.x() + 160, self.field.y()),
            QPointF(self.field.x() + 240, self.field.y()),
            QPointF(self.field.x() + 320, self.field.y()),
            QPointF(self.field.x(),
                    self.field.y() + 160),
            QPointF(self.field.x() + 160,
                    self.field.y() + 160),
            QPointF(self.field.x() + 240,
                    self.field.y() + 160),
            QPointF(self.field.x() + 320,
                    self.field.y() + 160),
            QPointF(self.field.x(),
                    self.field.y() + 240),
            QPointF(self.field.x() + 160,
                    self.field.y() + 240),
            QPointF(self.field.x() + 240,
                    self.field.y() + 240),
            QPointF(self.field.x() + 320,
                    self.field.y() + 240),
            QPointF(self.field.x(),
                    self.field.y() + 320),
            QPointF(self.field.x() + 160,
                    self.field.y() + 320),
            QPointF(self.field.x() + 240,
                    self.field.y() + 320),
            QPointF(self.field.x() + 320,
                    self.field.y() + 320)
        ]
        # deusExActivities
        self.positiveDeusExActivities = [
            self.destroyCurrentlyAliveEnemies, self.playerShield,
            self.playerLevelUp, self.playerLifeUp, self.stopTheTime,
            self.upgradeBase
        ]
        self.negativeDeusExActivities = [
            self.playerLevelDown, self.playerLifeDown, self.playerLosePoints,
            self.playerCantMove, self.removeBaseShield
        ]
        self.deusExSpawner = DeusExSpawner(self.scene, self.config, 15000,
                                           self.deusExActivate,
                                           self.deusExLocations)

        self.generateEtd()
        self.movementSoundHandler = MovementSoundHandler(self.config)
        self.movementSoundHandler.activate()
        self.generatePlayers()

    # UI INITIALIZATION
    def __init_ui__(self):
        # set up the scene
        self.scene = QGraphicsScene()
        # these 10 subtracted pixels are for the margin
        self.scene.setSceneRect(0, 0, self.config.mainWindowSize["width"] - 10,
                                self.config.mainWindowSize["height"] - 10)
        self.scene.setBackgroundBrush(Qt.darkGray)
        # set up the view
        self.setScene(self.scene)
        self.setFixedSize(self.config.mainWindowSize["width"],
                          self.config.mainWindowSize["height"])
        # optimization
        self.setOptimizationFlag(QGraphicsView.DontAdjustForAntialiasing)
        self.setOptimizationFlag(QGraphicsView.DontSavePainterState)
        self.setCacheMode(QGraphicsView.CacheBackground)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setInteractive(False)
        self.setViewport(QGLWidget(QGLFormat()))
        self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
        # initialize the map
        self.generateMap()
        # HUD
        # stage hud
        self.hudCurrentStage = HudCurrentStage(self.config, self.currentStage)
        self.hudCurrentStage.setX(self.field.x() +
                                  self.field.boundingRect().width() + 20)
        self.hudCurrentStage.setY(self.field.y() +
                                  self.field.boundingRect().height() - 100)
        self.scene.addItem(self.hudCurrentStage)
        # player lives hud
        self.hudPlayersLives = {}
        if self.isOnline:
            pass
        else:
            for i in range(self.numOfPlayers):
                if i == 0:
                    if self.playerData.firstPlayerDetails.isAlive:
                        playerLives = HudPlayerLives(
                            i, self.config,
                            self.playerData.firstPlayerDetails.lives)
                        playerLives.setX(self.field.x() +
                                         self.field.boundingRect().width() +
                                         20)
                        playerLives.setY(self.field.y() +
                                         self.field.boundingRect().height() -
                                         220 + i * 60)
                        self.scene.addItem(playerLives)
                        self.hudPlayersLives[self.playerData.firstPlayerDetails
                                             .id] = playerLives
                elif i == 1:
                    if self.playerData.secondPlayerDetails.isAlive:
                        playerLives = HudPlayerLives(
                            i, self.config,
                            self.playerData.secondPlayerDetails.lives)
                        playerLives.setX(self.field.x() +
                                         self.field.boundingRect().width() +
                                         20)
                        playerLives.setY(self.field.y() +
                                         self.field.boundingRect().height() -
                                         220 + i * 60)
                        self.scene.addItem(playerLives)
                        self.hudPlayersLives[
                            self.playerData.secondPlayerDetails.
                            id] = playerLives
        # enemies left hud
        self.enemyHud = HudEnemyContainer(self.config)
        self.enemyHud.setX(self.field.x() + self.field.boundingRect().width() +
                           20)
        self.enemyHud.setY(self.field.y() + 40)
        self.scene.addItem(self.enemyHud)

    def generateMap(self):
        # save block textures
        self.blockTextures = self.config.blockTextures
        # set up the field
        self.field = QGraphicsRectItem(0, 0, 520, 520)
        self.field.setZValue(-1)
        self.field.setBrush(Qt.black)
        self.field.setX(40)
        self.field.setY(20)
        self.scene.addItem(self.field)
        # save these for later use
        self.fieldCenterX = self.field.x() + (
            self.field.boundingRect().width() - 1) / 2
        self.fieldBottom = self.field.y() + self.field.boundingRect().height(
        ) - 1
        self.baseBlocks = []
        # set up the map
        for b in self.config.maps[f"map{self.currentMap}"]["blueprint"]:
            blockX = b["xCoord"]
            blockY = b["yCoord"]
            blockType = b["type"]
            blockIsBase = b["isBaseBlock"]
            block = Block(blockX, blockY, blockType, blockIsBase,
                          self.blockTextures)
            # hold reference to base blocks
            if block.isBaseBlock:
                self.baseBlocks.append(block)
            # setting z value to be higher than others so the tanks would appear under the bush
            if blockType == BlockType.bush:
                block.setZValue(2)
            # setting z value lower than others so the tanks would appear above the ice
            elif blockType == BlockType.ice:
                block.setZValue(-1)
            self.scene.addItem(block)
        # add the base
        self.base = Base(self.config.baseTextures["aliveBase"],
                         self.config.baseTextures["deadBase"])
        self.base.setX(self.fieldCenterX - self.base.aliveImage.width() / 2)
        self.base.setY(self.fieldBottom - self.base.aliveImage.height())
        self.scene.addItem(self.base)

    def generatePlayers(self):
        if self.isOnline:
            pass
        else:
            for i in range(self.numOfPlayers):
                if i == 0:
                    firingKey = Qt.Key_Space
                    movementKeys = {
                        "Up": Qt.Key_Up,
                        "Down": Qt.Key_Down,
                        "Left": Qt.Key_Left,
                        "Right": Qt.Key_Right
                    }
                    movementNotifier = MovementNotifier(
                        self.config.playerMovementSpeed)
                    if self.playerData.firstPlayerDetails.isAlive:
                        movementNotifier.movementSignal.connect(
                            self.updatePosition)
                    firingNotifier = FiringNotifier(50)
                    if self.playerData.firstPlayerDetails.isAlive:
                        firingNotifier.firingSignal.connect(self.fireCanon)
                    playerDetails = self.playerData.firstPlayerDetails
                    playerWrapper = PlayerWrapper(
                        playerDetails, self.config, self.playerColors[i],
                        firingKey, movementKeys, firingNotifier,
                        movementNotifier, self.playerLevels, self.field,
                        self.killEmitter, self.bulletTimer, Enemy,
                        self.animationTimer, self.playerDeadEmitter,
                        self.gameOverEmitter)
                    if self.playerData.firstPlayerDetails.isAlive:
                        playerWrapper.player.canShootSignal.connect(
                            self.allowFiring)
                        startingPos = QPointF(
                            self.fieldCenterX -
                            self.base.boundingRect().width() / 2 -
                            self.base.boundingRect().width() * 2,
                            self.fieldBottom -
                            playerWrapper.player.boundingRect().height() - 5)
                        playerWrapper.player.startingPos = startingPos
                        self.scene.addItem(playerWrapper.player)
                        self.playersAlive += 1
                        playerWrapper.player.setPos(startingPos)
                        playerWrapper.movementNotifier.movementSignal.connect(
                            self.movementSoundHandler.playMovementSound)
                    self.playerWrappers[playerDetails.id] = playerWrapper
                elif i == 1:
                    firingKey = Qt.Key_J
                    movementKeys = {
                        "Up": Qt.Key_W,
                        "Down": Qt.Key_S,
                        "Left": Qt.Key_A,
                        "Right": Qt.Key_D
                    }
                    movementNotifier = MovementNotifier(
                        self.config.playerMovementSpeed)
                    if self.playerData.secondPlayerDetails.isAlive:
                        movementNotifier.movementSignal.connect(
                            self.updatePosition)
                    firingNotifier = FiringNotifier(50)
                    if self.playerData.secondPlayerDetails.isAlive:
                        firingNotifier.firingSignal.connect(self.fireCanon)
                    playerDetails = self.playerData.secondPlayerDetails
                    playerWrapper = PlayerWrapper(
                        playerDetails, self.config, self.playerColors[i],
                        firingKey, movementKeys, firingNotifier,
                        movementNotifier, self.playerLevels, self.field,
                        self.killEmitter, self.bulletTimer, Enemy,
                        self.animationTimer, self.playerDeadEmitter,
                        self.gameOverEmitter)
                    if self.playerData.secondPlayerDetails.isAlive:
                        playerWrapper.player.canShootSignal.connect(
                            self.allowFiring)
                        startingPos = QPointF(
                            self.fieldCenterX +
                            self.base.boundingRect().width() / 2 +
                            self.base.boundingRect().width(),
                            self.fieldBottom -
                            playerWrapper.player.boundingRect().height() - 5)
                        playerWrapper.player.startingPos = startingPos
                        self.scene.addItem(playerWrapper.player)
                        self.playersAlive += 1
                        playerWrapper.player.setPos(startingPos)
                        playerWrapper.movementNotifier.movementSignal.connect(
                            self.movementSoundHandler.playMovementSound)
                    self.playerWrappers[playerDetails.id] = playerWrapper

    def generateEtd(self):
        # generate enemy details
        etdFactory = EnemyTankDetailsFactory(
            self.config.enemyTypes, self.config.enemyTypeIds,
            self.config.maps[f"map{self.currentMap}"]["enemies"],
            self.config.bulletSpeedMap)
        if self.enemiesEtds:
            self.enemiesEtds.clear()
        self.enemiesEtds = etdFactory.generateEnemiesDetails()

    def generateEnemy(self):
        if self.currentEnemyCnt < self.maxEnemyCnt:
            if self.enemiesCurrentlyAlive < self.maxEnemiesCurrentlyAlive:
                # set enemy pos and check if it can be spawned
                posX1 = random.choice(self.randomEnemyPositions)
                posY1 = self.field.y()
                posX2 = posX1 + self.enemySpawnRegionWidth
                posY2 = posY1 + self.enemySpawnRegionHeight
                middleX = posX1 + self.enemySpawnRegionWidth / 2
                middleY = posY1 + self.enemySpawnRegionHeight / 2
                item = self.scene.itemAt(posX1, posY1, self.transform())
                if type(item) != QGraphicsRectItem and item is not None:
                    return
                else:
                    item = self.scene.itemAt(posX1, posY2, self.transform())
                    if type(item) != QGraphicsRectItem and item is not None:
                        return
                    else:
                        item = self.scene.itemAt(posX2, posY1,
                                                 self.transform())
                        if type(item
                                ) != QGraphicsRectItem and item is not None:
                            return
                        else:
                            item = self.scene.itemAt(posX2, posY2,
                                                     self.transform())
                            if type(
                                    item
                            ) != QGraphicsRectItem and item is not None:
                                return
                            else:
                                item = self.scene.itemAt(
                                    middleX, middleY, self.transform())
                                if type(
                                        item
                                ) != QGraphicsRectItem and item is not None:
                                    return
                                else:
                                    pass
                enemyEtd = self.enemiesEtds[self.currentEnemyCnt]
                # set if tank is flashing or not
                isFlashing = False
                if self.currentEnemyCnt == 3 or self.currentEnemyCnt == 11 or self.currentEnemyCnt == 17:
                    isFlashing = True
                enemy = Enemy(self.currentEnemyCnt, enemyEtd, isFlashing,
                              self.enemyColor, self.field,
                              self.enemyMovementTimers[enemyEtd.movementSpeed],
                              self.enemyShootingTimer, self.animationTimer,
                              self.bulletTimer, Player, self.killEmitter,
                              self.gameOverEmitter)
                self.scene.removeItem(self.enemyHud.removeEnemy())
                self.scene.addItem(enemy)
                enemy.setPos(posX1, posY1)
                self.enemies[enemy.id] = enemy
                self.currentEnemyCnt += 1
                self.enemiesCurrentlyAlive += 1

    def keyPressEvent(self, event):
        playedSoundCnt = 0
        if not self.isGameOver:
            key = event.key()
            playerWrapper: PlayerWrapper
            for playerWrapper in self.playerWrappers.values():
                if key == playerWrapper.firingKey:
                    playerWrapper.firingNotifier.add_key(key)
                elif key in playerWrapper.movementKeys.values():
                    playerWrapper.movementNotifier.add_key(key)

    def keyReleaseEvent(self, event):
        playedSoundCnt = 0
        if not self.isGameOver:
            key = event.key()
            playerWrapper: PlayerWrapper
            for playerWrapper in self.playerWrappers.values():
                if key == playerWrapper.firingKey:
                    playerWrapper.firingNotifier.remove_key(key)
                elif key in playerWrapper.movementKeys.values():
                    playerWrapper.movementNotifier.remove_key(key)

    def updatePosition(self, key):
        playerWrapper: PlayerWrapper
        for playerWrapper in self.playerWrappers.values():
            if key in playerWrapper.movementKeys.values():
                playerWrapper.player.updatePosition(key)

    def allowFiring(self, canShootSignalData):
        self.playerWrappers[
            canShootSignalData.
            playerId].firingNotifier.canEmit = canShootSignalData.canEmit

    def fireCanon(self, key):
        playerWrapper: PlayerWrapper
        for playerWrapper in self.playerWrappers.values():
            if key == playerWrapper.firingKey:
                playerWrapper.player.shoot(key)

    def killEmitterHandler(self, killEmitterData):
        if killEmitterData.targetType is Enemy:
            # add points, check if the enemy is flashing and if so, spawn a positive DeusEx
            enemy = self.enemies[killEmitterData.targetId]
            playerWrapper = self.playerWrappers[killEmitterData.shooterId]
            playerWrapper.player.points += enemy.tankDetails.points
            playerWrapper.separateTankDetails.details[
                enemy.tankDetails.stringTankType]["count"] += 1
            # if the enemy is flashing then give the players a positive DeusEx
            if enemy.isFlashing:
                self.deusExSpawner.spawn(isPositive=True)
            # remove the tank, its details and delete it for good
            del self.enemiesEtds[killEmitterData.targetId]
            self.scene.removeItem(self.enemies[killEmitterData.targetId])
            sip.delete(self.enemies[killEmitterData.targetId])
            del self.enemies[killEmitterData.targetId]

            self.enemiesCurrentlyAlive -= 1
            self.enemyExplosionSound.play()
            # if there are no more enemy tank details that means that the stage is over
            if not self.enemiesEtds:
                self.stageEndInitiate()
        elif killEmitterData.targetType is Player:
            player = self.playerWrappers[killEmitterData.targetId].player
            player.resetPlayer()
            # if lives are less than 0, that means the player is dead
            if player.lives >= 0:
                self.hudPlayersLives[player.id].updateLives(player.lives)
            self.playerExplosionSound.play()

    def playerDeadEmitterHandler(self, playerId):
        playerWrapper = self.playerWrappers[playerId]
        # stop all player notifiers
        playerWrapper.firingNotifier.firingSignal.disconnect()
        playerWrapper.movementNotifier.movementSignal.disconnect()
        playerWrapper.firingNotifier.thread.terminate()
        playerWrapper.movementNotifier.thread.terminate()
        playerWrapper.player.isAlive = False
        # remove player from scene, but still keep it's data
        self.scene.removeItem(playerWrapper.player)
        # delete the reference to a player because in gameover handler
        # we will go over the rest of players and remove them from the scene
        # so now we set the player reference to None because of the check in gameover handler
        # decrease the number of players alive, if now is 0, all players are dead and the game is over
        self.playersAlive -= 1
        if self.playersAlive == 0:
            self.gameOverHandler()

    def stageEndInitiate(self):
        self.stageEndTimer = QTimer()
        self.stageEndTimer.setTimerType(Qt.PreciseTimer)
        self.stageEndTimer.setInterval(3000)
        self.stageEndTimer.timeout.connect(self.stageEnd)
        self.stageEndTimer.start()

    def stageEnd(self):
        self.stageEndTimer.stop()
        # disable all timers until next stage
        # self.nonMovementSound.stop()
        # self.movementSound.stop()
        self.movementSoundHandler.deactivate()
        self.animationTimer.stop()
        self.bulletTimer.stop()
        for timer in self.enemyMovementTimers.values():
            timer.stop()
        self.enemySpawnTimer.stop()
        self.enemyShootingTimer.stop()
        self.deusExSpawner.spawnTimer.stop()
        # stop any deus ex activity timer that is active
        for pw in self.playerWrappers.values():
            shieldTimer = getattr(self, f"playerShieldTimer{pw.player.id}",
                                  None)
            if shieldTimer is not None:
                shieldTimer.stop()
                del shieldTimer
            cantMoveTimer = getattr(self, f"playerCantMoveTimer{pw.player.id}",
                                    None)
            if cantMoveTimer is not None:
                cantMoveTimer.stop()
                del cantMoveTimer
        upgradeBaseTimer = getattr(self, f"upgradeBaseTimer", None)
        if upgradeBaseTimer is not None:
            upgradeBaseTimer.stop()
        stopTheTimeTimer = getattr(self, f"stopTheTimeTimer", None)
        if stopTheTimeTimer is not None:
            stopTheTimeTimer.stop()
        removeBaseShieldTimer = getattr(self, f"removeBaseShieldTimer", None)
        if removeBaseShieldTimer is not None:
            removeBaseShieldTimer.stop()
        # save data to be sent back to main window and disconnect and clear the players
        firstPlayerDetails = None
        firstPlayerTankDetails = None
        secondPlayerDetails = None
        secondPlayerTankDetails = None
        if self.numOfPlayers == 1:
            firstPlayerDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].getPlayerDetails()
            firstPlayerTankDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].separateTankDetails
        else:
            firstPlayerDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].getPlayerDetails()
            firstPlayerTankDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].separateTankDetails
            secondPlayerDetails = self.playerWrappers[
                self.playerData.secondPlayerDetails.id].getPlayerDetails()
            secondPlayerTankDetails = self.playerWrappers[
                self.playerData.secondPlayerDetails.id].separateTankDetails
        playerWrapper: PlayerWrapper
        for playerWrapper in self.playerWrappers.values():
            # check if player is dead, if not, disconnect from all notifiers
            # if he's dead, he already disconnected, and is already removed from the scene
            if playerWrapper.player is not None and playerWrapper.player.isAlive:
                playerWrapper.firingNotifier.firingSignal.disconnect()
                playerWrapper.movementNotifier.movementSignal.disconnect()
                playerWrapper.firingNotifier.thread.terminate()
                playerWrapper.movementNotifier.thread.terminate()
                self.scene.removeItem(playerWrapper.player)
                sip.delete(playerWrapper.firingNotifier)
                sip.delete(playerWrapper.movementNotifier)
                sip.delete(playerWrapper.player)
                del playerWrapper.firingNotifier
                del playerWrapper.movementNotifier
                del playerWrapper.player
        self.playerWrappers.clear()
        self.scene.clear()
        # send data
        if self.isOnline:
            pass
        else:
            if self.numOfPlayers == 1:
                data = LocalGameData(firstPlayerDetails,
                                     firstPlayerTankDetails)
                self.bridge.localGameStageEndSignal.emit(data)
            else:
                data = LocalGameData(firstPlayerDetails,
                                     firstPlayerTankDetails,
                                     secondPlayerDetails,
                                     secondPlayerTankDetails)
                self.bridge.localGameStageEndSignal.emit(data)

    def gameOverHandler(self):
        if self.base.isAlive:
            # self.nonMovementSound.stop()
            # self.movementSound.stop()
            self.movementSoundHandler.deactivate()
            self.deusExSpawner.spawnTimer.stop()
            self.isGameOver = True
            self.base.destroyBase()
            playerWrapper: PlayerWrapper
            for playerWrapper in self.playerWrappers.values():
                # check if player is dead, if not, disconnect from all notifiers
                # if he's dead, he already disconnected, and is removed from the scene
                if playerWrapper.player is not None and playerWrapper.player.isAlive:
                    playerWrapper.firingNotifier.firingSignal.disconnect()
                    playerWrapper.movementNotifier.movementSignal.disconnect()
                    playerWrapper.firingNotifier.thread.terminate()
                    playerWrapper.movementNotifier.thread.terminate()
                    self.scene.removeItem(playerWrapper.player)
            # self.nonMovementSound.destroy()
            # self.movementSound.destroy()
            # del self.nonMovementSound
            # del self.movementSound
            # ANIMATE GAME OVER
            self.scene.addItem(self.gameOver)
            self.gameOverTimer.start()
            self.gameOverSound.play()
            # disconnect from game over signal so there won't be more animations
            self.gameOverEmitter.gameOverSignal.disconnect()

    def animateGameOver(self):
        self.gameOver.setY(self.gameOver.y() - 2)
        if int(self.gameOver.y()) == 150:
            self.gameOverTimer.stop()
            self.gameOverHandlerSendData()

    def gameOverHandlerSendData(self):
        self.animationTimer.stop()
        self.bulletTimer.stop()
        for timer in self.enemyMovementTimers.values():
            timer.stop()
        self.enemySpawnTimer.stop()
        self.enemyShootingTimer.stop()
        self.deusExSpawner.spawnTimer.stop()
        # stop any deus ex activity timer that is active
        for pw in self.playerWrappers.values():
            shieldTimer = getattr(self, f"playerShieldTimer{pw.player.id}",
                                  None)
            if shieldTimer is not None:
                shieldTimer.stop()
                del shieldTimer
            cantMoveTimer = getattr(self, f"playerCantMoveTimer{pw.player.id}",
                                    None)
            if cantMoveTimer is not None:
                cantMoveTimer.stop()
                del cantMoveTimer
        upgradeBaseTimer = getattr(self, f"upgradeBaseTimer", None)
        if upgradeBaseTimer is not None:
            upgradeBaseTimer.stop()
        stopTheTimeTimer = getattr(self, f"stopTheTimeTimer", None)
        if stopTheTimeTimer is not None:
            stopTheTimeTimer.stop()
        removeBaseShieldTimer = getattr(self, f"removeBaseShieldTimer", None)
        if removeBaseShieldTimer is not None:
            removeBaseShieldTimer.stop()
        # save data
        firstPlayerDetails = None
        firstPlayerTankDetails = None
        secondPlayerDetails = None
        secondPlayerTankDetails = None
        if self.numOfPlayers == 1:
            firstPlayerDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].getPlayerDetails()
            firstPlayerTankDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].separateTankDetails
        else:
            firstPlayerDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].getPlayerDetails()
            firstPlayerTankDetails = self.playerWrappers[
                self.playerData.firstPlayerDetails.id].separateTankDetails
            secondPlayerDetails = self.playerWrappers[
                self.playerData.secondPlayerDetails.id].getPlayerDetails()
            secondPlayerTankDetails = self.playerWrappers[
                self.playerData.secondPlayerDetails.id].separateTankDetails
        if self.isOnline:
            pass
        else:
            if self.numOfPlayers == 1:
                data = LocalGameData(firstPlayerDetails,
                                     firstPlayerTankDetails)
                self.bridge.localGameOverSignal.emit(data)
            else:
                data = LocalGameData(firstPlayerDetails,
                                     firstPlayerTankDetails,
                                     secondPlayerDetails,
                                     secondPlayerTankDetails)
                self.bridge.localGameOverSignal.emit(data)
        playerWrapper: PlayerWrapper
        for playerWrapper in self.playerWrappers.values():
            # check if player is dead, if not, disconnect from all notifiers
            # if he's dead, he already disconnected, and is removed from the scene
            if playerWrapper.player is not None and playerWrapper.player.isAlive:
                sip.delete(playerWrapper.firingNotifier)
                sip.delete(playerWrapper.movementNotifier)
                sip.delete(playerWrapper.player)
                del playerWrapper.firingNotifier
                del playerWrapper.movementNotifier
                del playerWrapper.player
        self.playerWrappers.clear()
        self.scene.clear()

    def deusExActivate(self, deusExSignalData):
        deusExChoice = None
        if deusExSignalData.deusExType is DeusExTypes.POSITIVE:
            deusExChoice = random.choice(self.positiveDeusExActivities)
        else:
            deusExChoice = random.choice(self.negativeDeusExActivities)
        for player in deusExSignalData.markedPlayers:
            pw = self.playerWrappers[player.id]
            deusExChoice(pw)
        self.deusExSpawner.spawnTimer.start()

    # DEUS EX ACTIVITIES
    # POSITIVE
    def destroyCurrentlyAliveEnemies(self, pw=None):
        if self.enemies:
            for id, enemy in self.enemies.items():
                self.scene.removeItem(enemy)
                del self.enemiesEtds[id]
                # although this looks like it would produce an error because of deleting elements from currently
                # iterating collection, it won't because we're deleting the c++ object and not the python wrapper
                sip.delete(enemy)
            self.enemies.clear()
            self.enemiesCurrentlyAlive = 0
            self.enemyExplosionSound.play()
        # if there are no more enemy tank details that means that the stage is over
        if not self.enemiesEtds:
            self.stageEndInitiate()

    def playerShield(self, pw=None):
        pw.player.isShielded = True
        pw.player.shieldTimer.start()
        playerShieldTimer = QTimer()
        playerShieldTimer.setTimerType(Qt.PreciseTimer)
        playerShieldTimer.setInterval(15000)
        playerShieldTimer.timeout.connect(lambda: self.afterPlayerShield(pw))
        playerShieldTimer.start()
        setattr(self, f"playerShieldTimer{pw.player.id}", playerShieldTimer)

    def afterPlayerShield(self, pw=None):
        pw.player.shieldTimer.stop()
        pw.player.isShielded = False
        timer = getattr(self, f"playerShieldTimer{pw.player.id}", None)
        if timer is not None:
            timer.stop()
            del timer

    def upgradeBase(self, pw=None):
        for baseBlock in self.baseBlocks:
            if baseBlock.isHidden:
                self.scene.addItem(baseBlock)
                baseBlock.isHidden = False
            baseBlock.type = BlockType.steel
            baseBlock.updateTexture()
        self.upgradeBaseTimer = QTimer()
        self.upgradeBaseTimer.setTimerType(Qt.PreciseTimer)
        self.upgradeBaseTimer.timeout.connect(self.afterUpgradeBase)
        self.upgradeBaseTimer.setInterval(15000)
        self.upgradeBaseTimer.start()

    def afterUpgradeBase(self, pw=None):
        for baseBlock in self.baseBlocks:
            baseBlock.type = BlockType.brick
            baseBlock.updateTexture()
        self.upgradeBaseTimer.stop()
        del self.upgradeBaseTimer

    def playerLevelUp(self, pw=None):
        pw.player.levelUp()
        # check if player is blocked when his size changed, and if it is, put him somewhere where he's not blocked
        allObjects = self.scene.items()
        x1 = pw.player.x()
        y1 = pw.player.y()
        x2 = x1 + pw.player.width
        y2 = y1 + pw.player.height
        for obj in allObjects:
            oType = type(obj)
            if obj != pw.player and oType != QGraphicsRectItem:
                if type(obj) == Block:
                    if obj.type == BlockType.bush or obj.type == BlockType.ice:
                        continue
                if type(obj) == DeusEx:
                    continue
                objParent = obj.parentItem()
                objX1 = 0
                objY1 = 0
                if objParent is None:
                    objX1 = obj.x()
                    objY1 = obj.y()
                else:
                    objSceneCoords = obj.mapToScene(obj.pos())
                    objX1 = objSceneCoords.x()
                    objY1 = objSceneCoords.y()
                objX2 = objX1 + obj.boundingRect().width()
                objY2 = objY1 + obj.boundingRect().height()
                if x1 < objX2 and x2 > objX1 and y1 < objY2 and y2 > objY1:
                    # handle the x axis
                    deltaX = None
                    deltaY = None
                    if x1 < objX1:
                        deltaX = x2 - objX1
                        x1 -= deltaX
                    elif x1 > objX1:
                        deltaX = objX2 - x1
                        x1 += deltaX
                    else:
                        deltaX = 0
                    # handle the y axis
                    if y1 < objY1:
                        deltaY = y2 - objY1
                        y1 -= (deltaY + 1)
                    elif y1 > objY1:
                        deltaY = objY2 - y1
                        y1 += deltaY + 1
                    else:
                        deltaY = 0

                    # draw in paint these situations and you'll understand why i chose this approach
                    if deltaX > deltaY:
                        pw.player.setY(y1)
                    else:
                        pw.player.setX(x1)
                    continue

    def playerLifeUp(self, pw=None):
        pw.player.lives += 1
        self.hudPlayersLives[pw.player.id].updateLives(pw.player.lives)

    def stopTheTime(self, pw=None):
        self.enemyShootingTimer.stop()
        for timer in self.enemyMovementTimers.values():
            timer.stop()
        self.stopTheTimeTimer = QTimer()
        self.stopTheTimeTimer.setTimerType(Qt.PreciseTimer)
        self.stopTheTimeTimer.timeout.connect(self.afterStopTheTime)
        self.stopTheTimeTimer.setInterval(10000)
        self.stopTheTimeTimer.start()

    def afterStopTheTime(self, pw=None):
        self.enemyShootingTimer.start()
        for timer in self.enemyMovementTimers.values():
            timer.start()
        self.stopTheTimeTimer.stop()
        del self.stopTheTimeTimer

    # NEGATIVE
    def playerLevelDown(self, pw=None):
        pw.player.levelDown()
        # check if player is blocked when his size changed, and if it is, put him somewhere where he's not blocked
        allObjects = self.scene.items()
        x1 = pw.player.x()
        y1 = pw.player.y()
        x2 = x1 + pw.player.width
        y2 = y1 + pw.player.height
        for obj in allObjects:
            oType = type(obj)
            if obj != pw.player and oType != QGraphicsRectItem:
                if type(obj) == Block:
                    if obj.type == BlockType.bush or obj.type == BlockType.ice:
                        continue
                if type(obj) == DeusEx:
                    continue
                objParent = obj.parentItem()
                objX1 = 0
                objY1 = 0
                if objParent is None:
                    objX1 = obj.x()
                    objY1 = obj.y()
                else:
                    objSceneCoords = obj.mapToScene(obj.pos())
                    objX1 = objSceneCoords.x()
                    objY1 = objSceneCoords.y()
                objX2 = objX1 + obj.boundingRect().width()
                objY2 = objY1 + obj.boundingRect().height()
                if x1 < objX2 and x2 > objX1 and y1 < objY2 and y2 > objY1:
                    # handle the x axis
                    deltaX = None
                    deltaY = None
                    if x1 < objX1:
                        deltaX = x2 - objX1
                        x1 -= deltaX
                    elif x1 > objX1:
                        deltaX = objX2 - x1
                        x1 += deltaX
                    else:
                        deltaX = 0
                    # handle the y axis
                    if y1 < objY1:
                        deltaY = y2 - objY1
                        y1 -= (deltaY + 1)
                    elif y1 > objY1:
                        deltaY = objY2 - y1
                        y1 += deltaY + 1
                    else:
                        deltaY = 0

                    # draw in paint these situations and you'll understand why i chose this approach
                    if deltaX > deltaY:
                        pw.player.setY(y1)
                    else:
                        pw.player.setX(x1)
                    continue

    def playerLifeDown(self, pw=None):
        pw.player.lives -= 1
        if pw.player.lives < 0:
            pw.player.lives = 0
        self.hudPlayersLives[pw.player.id].updateLives(pw.player.lives)

    def playerLosePoints(self, pw=None):
        pw.player.points -= 1000
        if pw.player.points < 0:
            pw.player.points = 0

    def playerCantMove(self, pw):
        pw.firingNotifier.emitTimer.stop()
        pw.movementNotifier.emitTimer.stop()
        playerCantMoveTimer = QTimer()
        playerCantMoveTimer.setTimerType(Qt.PreciseTimer)
        playerCantMoveTimer.timeout.connect(
            lambda: self.afterPlayerCantMove(pw))
        playerCantMoveTimer.setInterval(10000)
        playerCantMoveTimer.start()
        setattr(self, f"playerCantMoveTimer{pw.player.id}",
                playerCantMoveTimer)

    def afterPlayerCantMove(self, pw):
        pw.firingNotifier.emitTimer.start()
        pw.movementNotifier.emitTimer.start()
        timer = getattr(self, f"playerCantMoveTimer{pw.player.id}", None)
        if timer is not None:
            timer.stop()
            del timer

    def removeBaseShield(self, pw=None):
        for baseBlock in self.baseBlocks:
            baseBlock.isHidden = True
            self.scene.removeItem(baseBlock)
        self.removeBaseShieldTimer = QTimer()
        self.removeBaseShieldTimer.setTimerType(Qt.PreciseTimer)
        self.removeBaseShieldTimer.timeout.connect(self.afterRemoveBaseShield)
        self.removeBaseShieldTimer.setInterval(15000)
        self.removeBaseShieldTimer.start()

    def afterRemoveBaseShield(self):
        for baseBlock in self.baseBlocks:
            # this check is needed because upgrade base adds the base blocks to the scene
            # practically annulling the remove base effect
            if baseBlock.isHidden:
                baseBlock.isHidden = False
                self.scene.addItem(baseBlock)
        self.removeBaseShieldTimer.stop()
        del self.removeBaseShieldTimer

    def startNewStage(self, nextMap, nextStage, gameTypeData, playerData):
        self.currentMap = nextMap
        self.currentStage = nextStage
        self.gameTypeData = gameTypeData
        self.isOnline = gameTypeData.isOnline
        self.numOfPlayers = gameTypeData.numOfPlayers
        self.playerData = playerData
        self.isGameOver = False
        self.__init_ui__()
        self.generateEtd()
        self.animationTimer.start()
        self.bulletTimer.start()
        for timer in self.enemyMovementTimers.values():
            timer.start()
        self.enemyShootingTimer.start()
        self.enemySpawnTimer.start()
        del self.gameOver
        self.gameOver = GameOver(self.config.gameOverTexture)
        self.gameOver.setPos(QPointF(150, self.fieldBottom + 50))
        self.gameOverEmitter.gameOverSignal.connect(self.gameOverHandler)
        self.generatePlayers()
        self.currentEnemyCnt = 0
        self.enemiesCurrentlyAlive = 0
        del self.deusExSpawner
        self.deusExSpawner = DeusExSpawner(self.scene, self.config, 15000,
                                           self.deusExActivate,
                                           self.deusExLocations)
        self.movementSoundHandler.activate()