Example #1
0
    def draw_sep_area(self, centers: np.ndarray, show_symbols=False):
        x = self.sceneRect().x()
        y = self.sceneRect().y()
        w = self.sceneRect().width()
        h = self.sceneRect().height()
        reversed_centers = list(reversed(centers))

        num_areas = len(centers) + 1
        if num_areas != len(self.separation_areas):
            for area in self.separation_areas:
                self.removeItem(area)
            self.separation_areas.clear()

            for i in range(num_areas):
                area = QGraphicsRectItem(0, 0, 0, 0)
                if i % 2 == 0:
                    area.setBrush(settings.ZEROS_AREA_COLOR)
                else:
                    area.setBrush(settings.ONES_AREA_COLOR)
                area.setOpacity(settings.SEPARATION_OPACITY)
                area.setPen(QPen(settings.TRANSPARENT_COLOR, 0))
                self.addItem(area)
                self.separation_areas.append(area)

        start = y

        for i, area in enumerate(self.separation_areas):
            area.show()
            try:
                self.separation_areas[i].setRect(
                    x, start, w, abs(start - reversed_centers[i]))
                start += abs(start - reversed_centers[i])
            except IndexError:
                self.separation_areas[i].setRect(x, start, w, abs(start - h))

        if self.noise_area is not None:
            self.noise_area.hide()

        self.centers = centers
        self.redraw_legend(show_symbols)
Example #2
0
class ZoomableScene(QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None
        self.ones_arrow = None
        self.zeros_arrow = None
        self.selection_area = ROI(0,
                                  0,
                                  0,
                                  0,
                                  fillcolor=constants.SELECTION_COLOR,
                                  opacity=constants.SELECTION_OPACITY)
        self.addItem(self.selection_area)

    def draw_noise_area(self, y, h):
        x = self.sceneRect().x()
        w = self.sceneRect().width()

        if self.ones_area is not None:
            self.ones_area.hide()
        if self.zeros_area is not None:
            self.zeros_area.hide()

        if self.noise_area is None or self.noise_area.scene() != self:
            roi = ROI(x,
                      y,
                      w,
                      h,
                      fillcolor=constants.NOISE_COLOR,
                      opacity=constants.NOISE_OPACITY)
            # roi.setPen(QPen(constants.NOISE_COLOR, Qt.FlatCap))
            self.noise_area = roi
            self.addItem(self.noise_area)
        else:
            self.noise_area.show()
            self.noise_area.setY(y)
            self.noise_area.height = h

    def draw_sep_area(self, ymid):
        x = self.sceneRect().x()
        y = self.sceneRect().y()
        h = self.sceneRect().height()
        w = self.sceneRect().width()
        # padding = constants.SEPARATION_PADDING
        padding = 0
        if self.noise_area is not None:
            self.noise_area.hide()

        if self.ones_area is None:
            self.ones_area = QGraphicsRectItem(x, y, w, h / 2 - padding + ymid)
            self.ones_area.setBrush(constants.ONES_AREA_COLOR)
            self.ones_area.setOpacity(constants.SEPARATION_OPACITY)
            self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR,
                                       Qt.FlatCap))
            self.addItem(self.ones_area)

        else:
            self.ones_area.show()
            self.ones_area.setRect(x, y, w, h / 2 - padding + ymid)

        start = y + h / 2 + padding + ymid
        if self.zeros_area is None:
            self.zeros_area = QGraphicsRectItem(x, start, w, (y + h) - start)
            self.zeros_area.setBrush(constants.ZEROS_AREA_COLOR)
            self.zeros_area.setOpacity(constants.SEPARATION_OPACITY)
            self.zeros_area.setPen(
                QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap))
            self.addItem(self.zeros_area)
        else:
            self.zeros_area.show()
            self.zeros_area.setRect(x, start, w, (y + h) - start)

    def clear(self):
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None
        self.zeros_arrow = None
        self.ones_arrow = None
        self.selection_area = None
        super().clear()

    def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()

    def dragMoveEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()
Example #3
0
class ZoomableScene(QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None
        self.ones_arrow = None
        self.zeros_arrow = None
        self.selection_area = ROI(0, 0, 0, 0, fillcolor=constants.SELECTION_COLOR, opacity=constants.SELECTION_OPACITY)
        self.addItem(self.selection_area)

    def draw_noise_area(self, y, h):
        x = self.sceneRect().x()
        w = self.sceneRect().width()

        if self.ones_area is not None:
            self.ones_area.hide()
        if self.zeros_area is not None:
            self.zeros_area.hide()

        if self.noise_area is None or self.noise_area.scene() != self:
            roi = ROI(x, y, w, h, fillcolor=constants.NOISE_COLOR, opacity=constants.NOISE_OPACITY)
           # roi.setPen(QPen(constants.NOISE_COLOR, Qt.FlatCap))
            self.noise_area = roi
            self.addItem(self.noise_area)
        else:
            self.noise_area.show()
            self.noise_area.setY(y)
            self.noise_area.height = h

    def draw_sep_area(self, y_mid):
        x = self.sceneRect().x()
        y = self.sceneRect().y()
        h = self.sceneRect().height()
        w = self.sceneRect().width()
        if self.noise_area is not None:
            self.noise_area.hide()

        if self.ones_area is None:
            self.ones_area = QGraphicsRectItem(x, y, w, h / 2 + y_mid)
            self.ones_area.setBrush(constants.ONES_AREA_COLOR)
            self.ones_area.setOpacity(constants.SEPARATION_OPACITY)
            self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap))
            self.addItem(self.ones_area)

        else:
            self.ones_area.show()
            self.ones_area.setRect(x, y, w, h / 2 + y_mid)

        start = y + h / 2 + y_mid
        if self.zeros_area is None:
            self.zeros_area = QGraphicsRectItem(x, start, w, (y + h) - start)
            self.zeros_area.setBrush(constants.ZEROS_AREA_COLOR)
            self.zeros_area.setOpacity(constants.SEPARATION_OPACITY)
            self.zeros_area.setPen(QPen(constants.TRANSPARENT_COLOR, Qt.FlatCap))
            self.addItem(self.zeros_area)
        else:
            self.zeros_area.show()
            self.zeros_area.setRect(x, start, w, (y + h) - start)

    def clear(self):
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None
        self.zeros_arrow = None
        self.ones_arrow = None
        self.selection_area = None
        super().clear()

    def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()

    def dragMoveEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()
Example #4
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]
class OverlayGraphics(QGraphicsView):
    def __init__(self):
        super(OverlayGraphics, self).__init__()
        self.setStyleSheet("background:transparent")  # ビューの背景透明化
        self.overlayScene = QGraphicsScene()
        self.setScene(self.overlayScene)
        self.overlayScene.setSceneRect(QRectF(self.rect()))
        self.createItem()

        self.luRect = QRect()
        self.rbRect = QRect()
        self.isSelected = False
        self.setTargetMode(False)
        self.hide()  # 初期状態は非表示

    def createItem(self):
        #  ターゲットマーカの作成
        self.target_circle = QGraphicsEllipseItem(
            QtCore.QRectF(-10, -10, 20, 20))
        self.target_circle.setBrush(QBrush(Qt.red))
        self.target_circle.setPen(QPen(Qt.black))
        self.overlayScene.addItem(self.target_circle)
        self.setTargetMode(False)

        # モーダルの作成:モーダルはターゲット位置に追従する
        self.pop_rect = QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 60),
                                          self.target_circle)
        self.pop_rect.setBrush(QBrush(Qt.gray))
        self.pop_rect.setPen(QPen(Qt.gray))
        self.pop_rect.setOpacity(0.8)  # 透明度を設定

        self.operate_text = QGraphicsSimpleTextItem("", self.pop_rect)
        self.operate_text.setScale(1.7)
        self.sub_operate_text = QGraphicsSimpleTextItem("", self.pop_rect)
        self.sub_operate_text.setScale(1.7)
        self.setTargetPos(400, 180, DirectionEnum.VERTICAL.value)

    # オーバレイヤのサイズが変わると呼び出される.シーンのサイズをビューの大きさに追従(-5 はマージン)
    def resizeEvent(self, event):
        self.overlayScene.setSceneRect(
            QRectF(0, 0,
                   self.size().width() - 5,
                   self.size().height() - 5))

    # ウィンドウ左上からの位置(画面位置からウインドウ左上の位置を引く)
    def setTargetPos(self, x_pos, y_pos, direction):
        self.target_circle.setPos(QPointF(x_pos, y_pos))
        self.setPopPos(direction)

    def setPopTextPos(self, text1, text2):
        lentext1 = len(text1)
        lentext2 = len(text2)
        if lentext2 == 0:
            self.operate_text.setPos(
                (self.pop_rect.rect().size().width() / 2) -
                (lentext1 / 2 * 14), 15)
        else:
            self.operate_text.setPos(
                (self.pop_rect.rect().size().width() / 2) -
                (lentext1 / 2 * 14), 5)
            self.sub_operate_text.setPos(
                (self.pop_rect.rect().size().width() / 2) -
                (lentext2 / 2 * 14), 30)

    def setPopPos(self, direction):
        if direction == DirectionEnum.VERTICAL.value:
            # モーダルを右に表示しきれない場合に左に表示
            if self.target_circle.pos().x() > self.overlayScene.width(
            ) - self.pop_rect.rect().size().width() * 1.5:
                self.pop_rect.setPos(
                    -self.pop_rect.rect().size().width() * 1.5,
                    -self.pop_rect.rect().size().height() / 2)
            else:  # 右に表示
                self.pop_rect.setPos(self.pop_rect.rect().size().width() / 2,
                                     -self.pop_rect.rect().size().height() / 2)

        else:
            # モーダルを下に表示しきれない場合に上に表示
            if self.target_circle.pos().y() > self.overlayScene.height(
            ) - self.pop_rect.rect().size().height() * 1.5:
                self.pop_rect.setPos(
                    -self.pop_rect.rect().size().width() / 2,
                    -self.pop_rect.rect().size().height() * 1.5)
            else:  # 下に表示
                self.pop_rect.setPos(-self.pop_rect.rect().size().width() / 2,
                                     self.pop_rect.rect().size().height() / 2)

    def setTargetMode(self, active):
        self.targetMode = active
        if active:
            self.target_circle.setRect(-10, -10, 20, 20)
        else:  # ターゲットを非表示にする
            self.target_circle.setRect(0, 0, 0, 0)

    # def targetVisible(self, visible):
    #     if visible:
    #         self.target_circle.setRect(-10, -10, 20, 20)
    #     else:
    #         self.target_circle.setRect(0, 0, 0, 0)

    def feedbackShow(self, text1, text2, direction):
        if self.isSelected:
            self.operate_text.setText(text1)
            self.sub_operate_text.setText(text2)
            # ターゲットモードがアクティブでないとき,ターゲットマーカの位置は選択セルに依存
            if not self.targetMode:
                if direction == DirectionEnum.HORIZON.value:
                    x_pos = (self.luRect.left() + self.rbRect.right()) / 2 + 20
                    y_pos = self.rbRect.bottom(
                    ) - self.rbRect.height() / 2 + 20
                else:
                    x_pos = self.rbRect.right() - self.rbRect.width() / 2 + 20
                    y_pos = (self.luRect.top() + self.rbRect.bottom()) / 2 + 20

                self.setTargetPos(x_pos, y_pos, direction)
                self.setPopTextPos(text1, text2)
            self.show()
Example #6
0
class MapScene(QGraphicsScene):
    selectedObjectItemsChanged = pyqtSignal()

    ##
    # Constructor.
    ##
    def __init__(self, parent):
        super().__init__(parent)
        self.mMapDocument = None
        self.mSelectedTool = None
        self.mActiveTool = None
        self.mObjectSelectionItem = None
        self.mUnderMouse = False
        self.mCurrentModifiers = Qt.NoModifier,
        self.mDarkRectangle = QGraphicsRectItem()
        self.mDefaultBackgroundColor = Qt.darkGray

        self.mLayerItems = QVector()
        self.mObjectItems = QMap()
        self.mObjectLineWidth = 0.0
        self.mSelectedObjectItems = QSet()
        self.mLastMousePos = QPointF()
        self.mShowTileObjectOutlines = False
        self.mHighlightCurrentLayer = False
        self.mGridVisible = False

        self.setBackgroundBrush(self.mDefaultBackgroundColor)
        tilesetManager = TilesetManager.instance()
        tilesetManager.tilesetChanged.connect(self.tilesetChanged)
        tilesetManager.repaintTileset.connect(self.tilesetChanged)
        prefs = preferences.Preferences.instance()
        prefs.showGridChanged.connect(self.setGridVisible)
        prefs.showTileObjectOutlinesChanged.connect(self.setShowTileObjectOutlines)
        prefs.objectTypesChanged.connect(self.syncAllObjectItems)
        prefs.highlightCurrentLayerChanged.connect(self.setHighlightCurrentLayer)
        prefs.gridColorChanged.connect(self.update)
        prefs.objectLineWidthChanged.connect(self.setObjectLineWidth)
        self.mDarkRectangle.setPen(QPen(Qt.NoPen))
        self.mDarkRectangle.setBrush(Qt.black)
        self.mDarkRectangle.setOpacity(darkeningFactor)
        self.addItem(self.mDarkRectangle)
        self.mGridVisible = prefs.showGrid()
        self.mObjectLineWidth = prefs.objectLineWidth()
        self.mShowTileObjectOutlines = prefs.showTileObjectOutlines()
        self.mHighlightCurrentLayer = prefs.highlightCurrentLayer()
        # Install an event filter so that we can get key events on behalf of the
        # active tool without having to have the current focus.
        QCoreApplication.instance().installEventFilter(self)

    ##
    # Destructor.
    ##
    def __del__(self):
        if QCoreApplication.instance():
            QCoreApplication.instance().removeEventFilter(self)

    ##
    # Returns the map document this scene is displaying.
    ##
    def mapDocument(self):
        return self.mMapDocument

    ##
    # Sets the map this scene displays.
    ##
    def setMapDocument(self, mapDocument):
        if (self.mMapDocument):
            self.mMapDocument.disconnect()
            if (not self.mSelectedObjectItems.isEmpty()):
                self.mSelectedObjectItems.clear()
                self.selectedObjectItemsChanged.emit()

        self.mMapDocument = mapDocument
        if (self.mMapDocument):
            renderer = self.mMapDocument.renderer()
            renderer.setObjectLineWidth(self.mObjectLineWidth)
            renderer.setFlag(RenderFlag.ShowTileObjectOutlines, self.mShowTileObjectOutlines)
            self.mMapDocument.mapChanged.connect(self.mapChanged)
            self.mMapDocument.regionChanged.connect(self.repaintRegion)
            self.mMapDocument.tileLayerDrawMarginsChanged.connect(self.tileLayerDrawMarginsChanged)
            self.mMapDocument.layerAdded.connect(self.layerAdded)
            self.mMapDocument.layerRemoved.connect(self.layerRemoved)
            self.mMapDocument.layerChanged.connect(self.layerChanged)
            self.mMapDocument.objectGroupChanged.connect(self.objectGroupChanged)
            self.mMapDocument.imageLayerChanged.connect(self.imageLayerChanged)
            self.mMapDocument.currentLayerIndexChanged.connect(self.currentLayerIndexChanged)
            self.mMapDocument.tilesetTileOffsetChanged.connect(self.tilesetTileOffsetChanged)
            self.mMapDocument.objectsInserted.connect(self.objectsInserted)
            self.mMapDocument.objectsRemoved.connect(self.objectsRemoved)
            self.mMapDocument.objectsChanged.connect(self.objectsChanged)
            self.mMapDocument.objectsIndexChanged.connect(self.objectsIndexChanged)
            self.mMapDocument.selectedObjectsChanged.connect(self.updateSelectedObjectItems)

        self.refreshScene()

    ##
    # Returns whether the tile grid is visible.
    ##
    def isGridVisible(self):
        return self.mGridVisible

    ##
    # Returns the set of selected map object items.
    ##
    def selectedObjectItems(self):
        return QSet(self.mSelectedObjectItems)

    ##
    # Sets the set of selected map object items. This translates to a call to
    # MapDocument.setSelectedObjects.
    ##
    def setSelectedObjectItems(self, items):
        # Inform the map document about the newly selected objects
        selectedObjects = QList()
        #selectedObjects.reserve(items.size())
        for item in items:
            selectedObjects.append(item.mapObject())
        self.mMapDocument.setSelectedObjects(selectedObjects)

    ##
    # Returns the MapObjectItem associated with the given \a mapObject.
    ##
    def itemForObject(self, object):
        return self.mObjectItems[object]

    ##
    # Enables the selected tool at this map scene.
    # Therefore it tells that tool, that this is the active map scene.
    ##
    def enableSelectedTool(self):
        if (not self.mSelectedTool or not self.mMapDocument):
            return
        self.mActiveTool = self.mSelectedTool
        self.mActiveTool.activate(self)
        self.mCurrentModifiers = QApplication.keyboardModifiers()
        if (self.mCurrentModifiers != Qt.NoModifier):
            self.mActiveTool.modifiersChanged(self.mCurrentModifiers)
        if (self.mUnderMouse):
            self.mActiveTool.mouseEntered()
            self.mActiveTool.mouseMoved(self.mLastMousePos, Qt.KeyboardModifiers())

    def disableSelectedTool(self):
        if (not self.mActiveTool):
            return
        if (self.mUnderMouse):
            self.mActiveTool.mouseLeft()
        self.mActiveTool.deactivate(self)
        self.mActiveTool = None

    ##
    # Sets the currently selected tool.
    ##
    def setSelectedTool(self, tool):
        self.mSelectedTool = tool

    ##
    # QGraphicsScene.drawForeground override that draws the tile grid.
    ##
    def drawForeground(self, painter, rect):
        if (not self.mMapDocument or not self.mGridVisible):
            return
            
        offset = QPointF()

        # Take into account the offset of the current layer
        layer = self.mMapDocument.currentLayer()
        if layer:
            offset = layer.offset()
            painter.translate(offset)
        
        prefs = preferences.Preferences.instance()
        self.mMapDocument.renderer().drawGrid(painter, rect.translated(-offset), prefs.gridColor())

    ##
    # Override for handling enter and leave events.
    ##
    def event(self, event):
        x = event.type()
        if x==QEvent.Enter:
            self.mUnderMouse = True
            if (self.mActiveTool):
                self.mActiveTool.mouseEntered()
        elif x==QEvent.Leave:
            self.mUnderMouse = False
            if (self.mActiveTool):
                self.mActiveTool.mouseLeft()
        else:
            pass

        return super().event(event)

    def keyPressEvent(self, event):
        if (self.mActiveTool):
            self.mActiveTool.keyPressed(event)
        if (not (self.mActiveTool and event.isAccepted())):
            super().keyPressEvent(event)

    def mouseMoveEvent(self, mouseEvent):
        self.mLastMousePos = mouseEvent.scenePos()
        if (not self.mMapDocument):
            return
        super().mouseMoveEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            self.mActiveTool.mouseMoved(mouseEvent.scenePos(), mouseEvent.modifiers())
            mouseEvent.accept()

    def mousePressEvent(self, mouseEvent):
        super().mousePressEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            mouseEvent.accept()
            self.mActiveTool.mousePressed(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        super().mouseReleaseEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            mouseEvent.accept()
            self.mActiveTool.mouseReleased(mouseEvent)

    ##
    # Override to ignore drag enter events.
    ##
    def dragEnterEvent(self, event):
        event.ignore()

    ##
    # Sets whether the tile grid is visible.
    ##
    def setGridVisible(self, visible):
        if (self.mGridVisible == visible):
            return
        self.mGridVisible = visible
        self.update()

    def setObjectLineWidth(self, lineWidth):
        if (self.mObjectLineWidth == lineWidth):
            return
        self.mObjectLineWidth = lineWidth
        if (self.mMapDocument):
            self.mMapDocument.renderer().setObjectLineWidth(lineWidth)
            # Changing the line width can change the size of the object items
            if (not self.mObjectItems.isEmpty()):
                for item in self.mObjectItems:
                    item[1].syncWithMapObject()
                self.update()

    def setShowTileObjectOutlines(self, enabled):
        if (self.mShowTileObjectOutlines == enabled):
            return
        self.mShowTileObjectOutlines = enabled
        if (self.mMapDocument):
            self.mMapDocument.renderer().setFlag(RenderFlag.ShowTileObjectOutlines, enabled)
            if (not self.mObjectItems.isEmpty()):
                self.update()

    ##
    # Sets whether the current layer should be highlighted.
    ##
    def setHighlightCurrentLayer(self, highlightCurrentLayer):
        if (self.mHighlightCurrentLayer == highlightCurrentLayer):
            return
        self.mHighlightCurrentLayer = highlightCurrentLayer
        self.updateCurrentLayerHighlight()

    ##
    # Refreshes the map scene.
    ##
    def refreshScene(self):
        self.mLayerItems.clear()
        self.mObjectItems.clear()
        self.removeItem(self.mDarkRectangle)
        self.clear()
        self.addItem(self.mDarkRectangle)
        if (not self.mMapDocument):
            self.setSceneRect(QRectF())
            return

        self.updateSceneRect()
        
        map = self.mMapDocument.map()
        self.mLayerItems.resize(map.layerCount())
        if (map.backgroundColor().isValid()):
            self.setBackgroundBrush(map.backgroundColor())
        else:
            self.setBackgroundBrush(self.mDefaultBackgroundColor)
        layerIndex = 0
        for layer in map.layers():
            layerItem = self.createLayerItem(layer)
            layerItem.setZValue(layerIndex)
            self.addItem(layerItem)
            self.mLayerItems[layerIndex] = layerItem
            layerIndex += 1

        tileSelectionItem = TileSelectionItem(self.mMapDocument)
        tileSelectionItem.setZValue(10000 - 2)
        self.addItem(tileSelectionItem)
        self.mObjectSelectionItem = ObjectSelectionItem(self.mMapDocument)
        self.mObjectSelectionItem.setZValue(10000 - 1)
        self.addItem(self.mObjectSelectionItem)
        self.updateCurrentLayerHighlight()

    ##
    # Repaints the specified region. The region is in tile coordinates.
    ##
    def repaintRegion(self, region, layer):
        renderer = self.mMapDocument.renderer()
        margins = self.mMapDocument.map().drawMargins()
        for r in region.rects():
            boundingRect = QRectF(renderer.boundingRect(r))
            self.update(QRectF(renderer.boundingRect(r).adjusted(-margins.left(),
                                                      -margins.top(),
                                                      margins.right(),
                                                      margins.bottom())))
            boundingRect.translate(layer.offset())
            self.update(boundingRect)

    def currentLayerIndexChanged(self):
        self.updateCurrentLayerHighlight()
        # New layer may have a different offset, affecting the grid
        if self.mGridVisible:
            self.update()
        
    ##
    # Adapts the scene, layers and objects to new map size, orientation or
    # background color.
    ##
    def mapChanged(self):
        self.updateSceneRect()
        for item in self.mLayerItems:
            tli = item
            if type(tli) == TileLayerItem:
                tli.syncWithTileLayer()

        for item in self.mObjectItems.values():
            item.syncWithMapObject()
        map = self.mMapDocument.map()
        if (map.backgroundColor().isValid()):
            self.setBackgroundBrush(map.backgroundColor())
        else:
            self.setBackgroundBrush(self.mDefaultBackgroundColor)

    def tilesetChanged(self, tileset):
        if (not self.mMapDocument):
            return
        if (contains(self.mMapDocument.map().tilesets(), tileset)):
            self.update()

    def tileLayerDrawMarginsChanged(self, tileLayer):
        index = self.mMapDocument.map().layers().indexOf(tileLayer)
        item = self.mLayerItems.at(index)
        item.syncWithTileLayer()

    def layerAdded(self, index):
        layer = self.mMapDocument.map().layerAt(index)
        layerItem = self.createLayerItem(layer)
        self.addItem(layerItem)
        self.mLayerItems.insert(index, layerItem)
        z = 0
        for item in self.mLayerItems:
            item.setZValue(z)
            z += 1

    def layerRemoved(self, index):
        self.mLayerItems.remove(index)

    ##
    # A layer has changed. This can mean that the layer visibility, opacity or
    # offset changed.
    ##
    def layerChanged(self, index):
        layer = self.mMapDocument.map().layerAt(index)
        layerItem = self.mLayerItems.at(index)
        layerItem.setVisible(layer.isVisible())
        multiplier = 1
        if (self.mHighlightCurrentLayer and self.mMapDocument.currentLayerIndex() < index):
            multiplier = opacityFactor
        layerItem.setOpacity(layer.opacity() * multiplier)
        layerItem.setPos(layer.offset())

        # Layer offset may have changed, affecting the scene rect and grid
        self.updateSceneRect()
        if self.mGridVisible:
            self.update()

    ##
    # When an object group has changed it may mean its color or drawing order
    # changed, which affects all its objects.
    ##
    def objectGroupChanged(self, objectGroup):
        self.objectsChanged(objectGroup.objects())
        self.objectsIndexChanged(objectGroup, 0, objectGroup.objectCount() - 1)

    ##
    # When an image layer has changed, it may change size and it may look
    # differently.
    ##
    def imageLayerChanged(self, imageLayer):
        index = self.mMapDocument.map().layers().indexOf(imageLayer)
        item = self.mLayerItems.at(index)
        item.syncWithImageLayer()
        item.update()

    ##
    # When the tile offset of a tileset has changed, it can affect the bounding
    # rect of all tile layers and tile objects. It also requires a full repaint.
    ##
    def tilesetTileOffsetChanged(self, tileset):
        self.update()
        for item in self.mLayerItems:
            tli = item
            if type(tli) == TileLayerItem:
                tli.syncWithTileLayer()
        for item in self.mObjectItems:
            cell = item.mapObject().cell()
            if (not cell.isEmpty() and cell.tile.tileset() == tileset):
                item.syncWithMapObject()

    ##
    # Inserts map object items for the given objects.
    ##
    def objectsInserted(self, objectGroup, first, last):
        ogItem = None
        # Find the object group item for the object group
        for item in self.mLayerItems:
            ogi = item
            if type(ogi)==ObjectGroupItem:
                if (ogi.objectGroup() == objectGroup):
                    ogItem = ogi
                    break

        drawOrder = objectGroup.drawOrder()
        for i in range(first, last+1):
            object = objectGroup.objectAt(i)
            item = MapObjectItem(object, self.mMapDocument, ogItem)
            if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder):
                item.setZValue(item.y())
            else:
                item.setZValue(i)
            self.mObjectItems.insert(object, item)

    ##
    # Removes the map object items related to the given objects.
    ##
    def objectsRemoved(self, objects):
        for o in objects:
            i = self.mObjectItems.find(o)
            self.mSelectedObjectItems.remove(i)
            # python would not force delete QGraphicsItem
            self.removeItem(i)
            self.mObjectItems.erase(o)

    ##
    # Updates the map object items related to the given objects.
    ##
    def objectsChanged(self, objects):
        for object in objects:
            item = self.itemForObject(object)
            item.syncWithMapObject()

    ##
    # Updates the Z value of the objects when appropriate.
    ##
    def objectsIndexChanged(self, objectGroup, first, last):
        if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.IndexOrder):
            return
        for i in range(first, last+1):
            item = self.itemForObject(objectGroup.objectAt(i))
            item.setZValue(i)

    def updateSelectedObjectItems(self):
        objects = self.mMapDocument.selectedObjects()
        items = QSet()
        for object in objects:
            item = self.itemForObject(object)
            if item:
                items.insert(item)

        self.mSelectedObjectItems = items
        self.selectedObjectItemsChanged.emit()

    def syncAllObjectItems(self):
        for item in self.mObjectItems:
            item.syncWithMapObject()

    def createLayerItem(self, layer):
        layerItem = None
        tl = layer.asTileLayer()
        if tl:
            layerItem = TileLayerItem(tl, self.mMapDocument)
        else:
            og = layer.asObjectGroup()
            if og:
                drawOrder = og.drawOrder()
                ogItem = ObjectGroupItem(og)
                objectIndex = 0
                for object in og.objects():
                    item = MapObjectItem(object, self.mMapDocument, ogItem)
                    if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder):
                        item.setZValue(item.y())
                    else:
                        item.setZValue(objectIndex)
                    self.mObjectItems.insert(object, item)
                    objectIndex += 1

                layerItem = ogItem
            else:
                il = layer.asImageLayer()
                if il:
                    layerItem = ImageLayerItem(il, self.mMapDocument)

        layerItem.setVisible(layer.isVisible())
        return layerItem

    def updateSceneRect(self):
        mapSize = self.mMapDocument.renderer().mapSize()
        sceneRect = QRectF(0, 0, mapSize.width(), mapSize.height())

        margins = self.mMapDocument.map().computeLayerOffsetMargins()
        sceneRect.adjust(-margins.left(),
                         -margins.top(),
                         margins.right(),
                         margins.bottom())

        self.setSceneRect(sceneRect)
        self.mDarkRectangle.setRect(sceneRect)
        
    def updateCurrentLayerHighlight(self):
        if (not self.mMapDocument):
            return
        currentLayerIndex = self.mMapDocument.currentLayerIndex()
        if (not self.mHighlightCurrentLayer or currentLayerIndex == -1):
            self.mDarkRectangle.setVisible(False)
            # Restore opacity for all layers
            for i in range(self.mLayerItems.size()):
                layer = self.mMapDocument.map().layerAt(i)
                self.mLayerItems.at(i).setOpacity(layer.opacity())

            return

        # Darken layers below the current layer
        self.mDarkRectangle.setZValue(currentLayerIndex - 0.5)
        self.mDarkRectangle.setVisible(True)
        # Set layers above the current layer to half opacity
        for i in range(1, self.mLayerItems.size()):
            layer = self.mMapDocument.map().layerAt(i)
            if currentLayerIndex < i:
                _x = opacityFactor
            else:
                _x = 1
            multiplier = _x
            self.mLayerItems.at(i).setOpacity(layer.opacity() * multiplier)

    def eventFilter(self, object, event):
        x = event.type()
        if x==QEvent.KeyPress or x==QEvent.KeyRelease:
                keyEvent = event
                newModifiers = keyEvent.modifiers()
                if (self.mActiveTool and newModifiers != self.mCurrentModifiers):
                    self.mActiveTool.modifiersChanged(newModifiers)
                    self.mCurrentModifiers = newModifiers
        else:
            pass

        return False
Example #7
0
class ZoomableScene(QGraphicsScene):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None

        self.y_mid = 0

        self.always_show_symbols_legend = False

        self.ones_caption = None
        self.zeros_caption = None

        self.ones_arrow = None
        self.zeros_arrow = None
        self.selection_area = HorizontalSelection(
            0,
            0,
            0,
            0,
            fillcolor=constants.SELECTION_COLOR,
            opacity=constants.SELECTION_OPACITY)
        self.addItem(self.selection_area)

    def draw_noise_area(self, y, h):
        x = self.sceneRect().x()
        w = self.sceneRect().width()

        if self.ones_area is not None:
            self.ones_area.hide()
        if self.zeros_area is not None:
            self.zeros_area.hide()

        if self.noise_area is None or self.noise_area.scene() != self:
            roi = HorizontalSelection(x,
                                      y,
                                      w,
                                      h,
                                      fillcolor=constants.NOISE_COLOR,
                                      opacity=constants.NOISE_OPACITY)
            self.noise_area = roi
            self.addItem(self.noise_area)
        else:
            self.noise_area.show()
            self.noise_area.setY(y)
            self.noise_area.height = h

    def redraw_legend(self, force_show=False):
        if not (force_show or self.always_show_symbols_legend):
            if self.zeros_caption is not None:
                self.zeros_caption.hide()
            if self.ones_caption is not None:
                self.ones_caption.hide()
            return

        if self.ones_caption is None:
            font = QFont()
            font.setPointSize(32)
            font.setBold(True)
            self.ones_caption = self.addSimpleText("1", font)

        if self.zeros_caption is None:
            font = QFont()
            font.setPointSize(32)
            font.setBold(True)
            self.zeros_caption = self.addSimpleText("0", font)

        view_rect = self.parent().view_rect()  # type: QRectF

        self.ones_caption.show()
        self.zeros_caption.show()
        padding = view_rect.height() / 20
        scale_x, scale_y = util.calc_x_y_scale(self.ones_area.rect(),
                                               self.parent())

        y_mid = self.y_mid
        fm = QFontMetrics(self.ones_caption.font())
        self.ones_caption.setPos(
            view_rect.x() + view_rect.width() - fm.width("1") * scale_x,
            y_mid - fm.height() * scale_y - padding)
        self.ones_caption.setTransform(QTransform.fromScale(scale_x, scale_y),
                                       False)

        scale_x, scale_y = util.calc_x_y_scale(self.zeros_area.rect(),
                                               self.parent())

        self.zeros_caption.setPos(
            view_rect.x() + view_rect.width() - fm.width("0") * scale_x,
            y_mid + padding)
        self.zeros_caption.setTransform(QTransform.fromScale(scale_x, scale_y),
                                        False)

    def draw_sep_area(self, y_mid, show_symbols=False):
        x = self.sceneRect().x()
        y = self.sceneRect().y()
        h = self.sceneRect().height()
        w = self.sceneRect().width()
        if self.noise_area is not None:
            self.noise_area.hide()

        if self.ones_area is None:
            self.ones_area = QGraphicsRectItem(x, y, w, h / 2 + y_mid)
            self.ones_area.setBrush(constants.ONES_AREA_COLOR)
            self.ones_area.setOpacity(constants.SEPARATION_OPACITY)
            self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR, 0))
            self.addItem(self.ones_area)
        else:
            self.ones_area.show()
            self.ones_area.setRect(x, y, w, h / 2 + y_mid)

        start = y + h / 2 + y_mid
        if self.zeros_area is None:
            self.zeros_area = QGraphicsRectItem(x, start, w, (y + h) - start)
            self.zeros_area.setBrush(constants.ZEROS_AREA_COLOR)
            self.zeros_area.setOpacity(constants.SEPARATION_OPACITY)
            self.zeros_area.setPen(QPen(constants.TRANSPARENT_COLOR, 0))
            self.addItem(self.zeros_area)
        else:
            self.zeros_area.show()
            self.zeros_area.setRect(x, start, w, (y + h) - start)

        self.y_mid = y_mid
        self.redraw_legend(show_symbols)

    def clear(self):
        self.noise_area = None
        self.ones_area = None
        self.zeros_area = None
        self.zeros_arrow = None
        self.ones_arrow = None
        self.selection_area = None
        super().clear()

    def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()

    def dragMoveEvent(self, event: QGraphicsSceneDragDropEvent):
        event.accept()
Example #8
0
class MapScene(QGraphicsScene):
    selectedObjectItemsChanged = pyqtSignal()

    ##
    # Constructor.
    ##
    def __init__(self, parent):
        super().__init__(parent)
        self.mMapDocument = None
        self.mSelectedTool = None
        self.mActiveTool = None
        self.mObjectSelectionItem = None
        self.mUnderMouse = False
        self.mCurrentModifiers = Qt.NoModifier,
        self.mDarkRectangle = QGraphicsRectItem()
        self.mDefaultBackgroundColor = Qt.darkGray

        self.mLayerItems = QVector()
        self.mObjectItems = QMap()
        self.mObjectLineWidth = 0.0
        self.mSelectedObjectItems = QSet()
        self.mLastMousePos = QPointF()
        self.mShowTileObjectOutlines = False
        self.mHighlightCurrentLayer = False
        self.mGridVisible = False

        self.setBackgroundBrush(self.mDefaultBackgroundColor)
        tilesetManager = TilesetManager.instance()
        tilesetManager.tilesetChanged.connect(self.tilesetChanged)
        tilesetManager.repaintTileset.connect(self.tilesetChanged)
        prefs = preferences.Preferences.instance()
        prefs.showGridChanged.connect(self.setGridVisible)
        prefs.showTileObjectOutlinesChanged.connect(
            self.setShowTileObjectOutlines)
        prefs.objectTypesChanged.connect(self.syncAllObjectItems)
        prefs.highlightCurrentLayerChanged.connect(
            self.setHighlightCurrentLayer)
        prefs.gridColorChanged.connect(self.update)
        prefs.objectLineWidthChanged.connect(self.setObjectLineWidth)
        self.mDarkRectangle.setPen(QPen(Qt.NoPen))
        self.mDarkRectangle.setBrush(Qt.black)
        self.mDarkRectangle.setOpacity(darkeningFactor)
        self.addItem(self.mDarkRectangle)
        self.mGridVisible = prefs.showGrid()
        self.mObjectLineWidth = prefs.objectLineWidth()
        self.mShowTileObjectOutlines = prefs.showTileObjectOutlines()
        self.mHighlightCurrentLayer = prefs.highlightCurrentLayer()
        # Install an event filter so that we can get key events on behalf of the
        # active tool without having to have the current focus.
        QCoreApplication.instance().installEventFilter(self)

    ##
    # Destructor.
    ##
    def __del__(self):
        if QCoreApplication.instance():
            QCoreApplication.instance().removeEventFilter(self)

    ##
    # Returns the map document this scene is displaying.
    ##
    def mapDocument(self):
        return self.mMapDocument

    ##
    # Sets the map this scene displays.
    ##
    def setMapDocument(self, mapDocument):
        if (self.mMapDocument):
            self.mMapDocument.disconnect()
            if (not self.mSelectedObjectItems.isEmpty()):
                self.mSelectedObjectItems.clear()
                self.selectedObjectItemsChanged.emit()

        self.mMapDocument = mapDocument
        if (self.mMapDocument):
            renderer = self.mMapDocument.renderer()
            renderer.setObjectLineWidth(self.mObjectLineWidth)
            renderer.setFlag(RenderFlag.ShowTileObjectOutlines,
                             self.mShowTileObjectOutlines)
            self.mMapDocument.mapChanged.connect(self.mapChanged)
            self.mMapDocument.regionChanged.connect(self.repaintRegion)
            self.mMapDocument.tileLayerDrawMarginsChanged.connect(
                self.tileLayerDrawMarginsChanged)
            self.mMapDocument.layerAdded.connect(self.layerAdded)
            self.mMapDocument.layerRemoved.connect(self.layerRemoved)
            self.mMapDocument.layerChanged.connect(self.layerChanged)
            self.mMapDocument.objectGroupChanged.connect(
                self.objectGroupChanged)
            self.mMapDocument.imageLayerChanged.connect(self.imageLayerChanged)
            self.mMapDocument.currentLayerIndexChanged.connect(
                self.currentLayerIndexChanged)
            self.mMapDocument.tilesetTileOffsetChanged.connect(
                self.tilesetTileOffsetChanged)
            self.mMapDocument.objectsInserted.connect(self.objectsInserted)
            self.mMapDocument.objectsRemoved.connect(self.objectsRemoved)
            self.mMapDocument.objectsChanged.connect(self.objectsChanged)
            self.mMapDocument.objectsIndexChanged.connect(
                self.objectsIndexChanged)
            self.mMapDocument.selectedObjectsChanged.connect(
                self.updateSelectedObjectItems)

        self.refreshScene()

    ##
    # Returns whether the tile grid is visible.
    ##
    def isGridVisible(self):
        return self.mGridVisible

    ##
    # Returns the set of selected map object items.
    ##
    def selectedObjectItems(self):
        return QSet(self.mSelectedObjectItems)

    ##
    # Sets the set of selected map object items. This translates to a call to
    # MapDocument.setSelectedObjects.
    ##
    def setSelectedObjectItems(self, items):
        # Inform the map document about the newly selected objects
        selectedObjects = QList()
        #selectedObjects.reserve(items.size())
        for item in items:
            selectedObjects.append(item.mapObject())
        self.mMapDocument.setSelectedObjects(selectedObjects)

    ##
    # Returns the MapObjectItem associated with the given \a mapObject.
    ##
    def itemForObject(self, object):
        return self.mObjectItems[object]

    ##
    # Enables the selected tool at this map scene.
    # Therefore it tells that tool, that this is the active map scene.
    ##
    def enableSelectedTool(self):
        if (not self.mSelectedTool or not self.mMapDocument):
            return
        self.mActiveTool = self.mSelectedTool
        self.mActiveTool.activate(self)
        self.mCurrentModifiers = QApplication.keyboardModifiers()
        if (self.mCurrentModifiers != Qt.NoModifier):
            self.mActiveTool.modifiersChanged(self.mCurrentModifiers)
        if (self.mUnderMouse):
            self.mActiveTool.mouseEntered()
            self.mActiveTool.mouseMoved(self.mLastMousePos,
                                        Qt.KeyboardModifiers())

    def disableSelectedTool(self):
        if (not self.mActiveTool):
            return
        if (self.mUnderMouse):
            self.mActiveTool.mouseLeft()
        self.mActiveTool.deactivate(self)
        self.mActiveTool = None

    ##
    # Sets the currently selected tool.
    ##
    def setSelectedTool(self, tool):
        self.mSelectedTool = tool

    ##
    # QGraphicsScene.drawForeground override that draws the tile grid.
    ##
    def drawForeground(self, painter, rect):
        if (not self.mMapDocument or not self.mGridVisible):
            return

        offset = QPointF()

        # Take into account the offset of the current layer
        layer = self.mMapDocument.currentLayer()
        if layer:
            offset = layer.offset()
            painter.translate(offset)

        prefs = preferences.Preferences.instance()
        self.mMapDocument.renderer().drawGrid(painter,
                                              rect.translated(-offset),
                                              prefs.gridColor())

    ##
    # Override for handling enter and leave events.
    ##
    def event(self, event):
        x = event.type()
        if x == QEvent.Enter:
            self.mUnderMouse = True
            if (self.mActiveTool):
                self.mActiveTool.mouseEntered()
        elif x == QEvent.Leave:
            self.mUnderMouse = False
            if (self.mActiveTool):
                self.mActiveTool.mouseLeft()
        else:
            pass

        return super().event(event)

    def keyPressEvent(self, event):
        if (self.mActiveTool):
            self.mActiveTool.keyPressed(event)
        if (not (self.mActiveTool and event.isAccepted())):
            super().keyPressEvent(event)

    def mouseMoveEvent(self, mouseEvent):
        self.mLastMousePos = mouseEvent.scenePos()
        if (not self.mMapDocument):
            return
        super().mouseMoveEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            self.mActiveTool.mouseMoved(mouseEvent.scenePos(),
                                        mouseEvent.modifiers())
            mouseEvent.accept()

    def mousePressEvent(self, mouseEvent):
        super().mousePressEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            mouseEvent.accept()
            self.mActiveTool.mousePressed(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        super().mouseReleaseEvent(mouseEvent)
        if (mouseEvent.isAccepted()):
            return
        if (self.mActiveTool):
            mouseEvent.accept()
            self.mActiveTool.mouseReleased(mouseEvent)

    ##
    # Override to ignore drag enter events.
    ##
    def dragEnterEvent(self, event):
        event.ignore()

    ##
    # Sets whether the tile grid is visible.
    ##
    def setGridVisible(self, visible):
        if (self.mGridVisible == visible):
            return
        self.mGridVisible = visible
        self.update()

    def setObjectLineWidth(self, lineWidth):
        if (self.mObjectLineWidth == lineWidth):
            return
        self.mObjectLineWidth = lineWidth
        if (self.mMapDocument):
            self.mMapDocument.renderer().setObjectLineWidth(lineWidth)
            # Changing the line width can change the size of the object items
            if (not self.mObjectItems.isEmpty()):
                for item in self.mObjectItems:
                    item[1].syncWithMapObject()
                self.update()

    def setShowTileObjectOutlines(self, enabled):
        if (self.mShowTileObjectOutlines == enabled):
            return
        self.mShowTileObjectOutlines = enabled
        if (self.mMapDocument):
            self.mMapDocument.renderer().setFlag(
                RenderFlag.ShowTileObjectOutlines, enabled)
            if (not self.mObjectItems.isEmpty()):
                self.update()

    ##
    # Sets whether the current layer should be highlighted.
    ##
    def setHighlightCurrentLayer(self, highlightCurrentLayer):
        if (self.mHighlightCurrentLayer == highlightCurrentLayer):
            return
        self.mHighlightCurrentLayer = highlightCurrentLayer
        self.updateCurrentLayerHighlight()

    ##
    # Refreshes the map scene.
    ##
    def refreshScene(self):
        self.mLayerItems.clear()
        self.mObjectItems.clear()
        self.removeItem(self.mDarkRectangle)
        self.clear()
        self.addItem(self.mDarkRectangle)
        if (not self.mMapDocument):
            self.setSceneRect(QRectF())
            return

        self.updateSceneRect()

        map = self.mMapDocument.map()
        self.mLayerItems.resize(map.layerCount())
        if (map.backgroundColor().isValid()):
            self.setBackgroundBrush(map.backgroundColor())
        else:
            self.setBackgroundBrush(self.mDefaultBackgroundColor)
        layerIndex = 0
        for layer in map.layers():
            layerItem = self.createLayerItem(layer)
            layerItem.setZValue(layerIndex)
            self.addItem(layerItem)
            self.mLayerItems[layerIndex] = layerItem
            layerIndex += 1

        tileSelectionItem = TileSelectionItem(self.mMapDocument)
        tileSelectionItem.setZValue(10000 - 2)
        self.addItem(tileSelectionItem)
        self.mObjectSelectionItem = ObjectSelectionItem(self.mMapDocument)
        self.mObjectSelectionItem.setZValue(10000 - 1)
        self.addItem(self.mObjectSelectionItem)
        self.updateCurrentLayerHighlight()

    ##
    # Repaints the specified region. The region is in tile coordinates.
    ##
    def repaintRegion(self, region, layer):
        renderer = self.mMapDocument.renderer()
        margins = self.mMapDocument.map().drawMargins()
        for r in region.rects():
            boundingRect = QRectF(renderer.boundingRect(r))
            self.update(
                QRectF(
                    renderer.boundingRect(r).adjusted(-margins.left(),
                                                      -margins.top(),
                                                      margins.right(),
                                                      margins.bottom())))
            boundingRect.translate(layer.offset())
            self.update(boundingRect)

    def currentLayerIndexChanged(self):
        self.updateCurrentLayerHighlight()
        # New layer may have a different offset, affecting the grid
        if self.mGridVisible:
            self.update()

    ##
    # Adapts the scene, layers and objects to new map size, orientation or
    # background color.
    ##
    def mapChanged(self):
        self.updateSceneRect()
        for item in self.mLayerItems:
            tli = item
            if type(tli) == TileLayerItem:
                tli.syncWithTileLayer()

        for item in self.mObjectItems.values():
            item.syncWithMapObject()
        map = self.mMapDocument.map()
        if (map.backgroundColor().isValid()):
            self.setBackgroundBrush(map.backgroundColor())
        else:
            self.setBackgroundBrush(self.mDefaultBackgroundColor)

    def tilesetChanged(self, tileset):
        if (not self.mMapDocument):
            return
        if (contains(self.mMapDocument.map().tilesets(), tileset)):
            self.update()

    def tileLayerDrawMarginsChanged(self, tileLayer):
        index = self.mMapDocument.map().layers().indexOf(tileLayer)
        item = self.mLayerItems.at(index)
        item.syncWithTileLayer()

    def layerAdded(self, index):
        layer = self.mMapDocument.map().layerAt(index)
        layerItem = self.createLayerItem(layer)
        self.addItem(layerItem)
        self.mLayerItems.insert(index, layerItem)
        z = 0
        for item in self.mLayerItems:
            item.setZValue(z)
            z += 1

    def layerRemoved(self, index):
        self.mLayerItems.remove(index)

    ##
    # A layer has changed. This can mean that the layer visibility, opacity or
    # offset changed.
    ##
    def layerChanged(self, index):
        layer = self.mMapDocument.map().layerAt(index)
        layerItem = self.mLayerItems.at(index)
        layerItem.setVisible(layer.isVisible())
        multiplier = 1
        if (self.mHighlightCurrentLayer
                and self.mMapDocument.currentLayerIndex() < index):
            multiplier = opacityFactor
        layerItem.setOpacity(layer.opacity() * multiplier)
        layerItem.setPos(layer.offset())

        # Layer offset may have changed, affecting the scene rect and grid
        self.updateSceneRect()
        if self.mGridVisible:
            self.update()

    ##
    # When an object group has changed it may mean its color or drawing order
    # changed, which affects all its objects.
    ##
    def objectGroupChanged(self, objectGroup):
        self.objectsChanged(objectGroup.objects())
        self.objectsIndexChanged(objectGroup, 0, objectGroup.objectCount() - 1)

    ##
    # When an image layer has changed, it may change size and it may look
    # differently.
    ##
    def imageLayerChanged(self, imageLayer):
        index = self.mMapDocument.map().layers().indexOf(imageLayer)
        item = self.mLayerItems.at(index)
        item.syncWithImageLayer()
        item.update()

    ##
    # When the tile offset of a tileset has changed, it can affect the bounding
    # rect of all tile layers and tile objects. It also requires a full repaint.
    ##
    def tilesetTileOffsetChanged(self, tileset):
        self.update()
        for item in self.mLayerItems:
            tli = item
            if type(tli) == TileLayerItem:
                tli.syncWithTileLayer()
        for item in self.mObjectItems:
            cell = item.mapObject().cell()
            if (not cell.isEmpty() and cell.tile.tileset() == tileset):
                item.syncWithMapObject()

    ##
    # Inserts map object items for the given objects.
    ##
    def objectsInserted(self, objectGroup, first, last):
        ogItem = None
        # Find the object group item for the object group
        for item in self.mLayerItems:
            ogi = item
            if type(ogi) == ObjectGroupItem:
                if (ogi.objectGroup() == objectGroup):
                    ogItem = ogi
                    break

        drawOrder = objectGroup.drawOrder()
        for i in range(first, last + 1):
            object = objectGroup.objectAt(i)
            item = MapObjectItem(object, self.mMapDocument, ogItem)
            if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder):
                item.setZValue(item.y())
            else:
                item.setZValue(i)
            self.mObjectItems.insert(object, item)

    ##
    # Removes the map object items related to the given objects.
    ##
    def objectsRemoved(self, objects):
        for o in objects:
            i = self.mObjectItems.find(o)
            self.mSelectedObjectItems.remove(i)
            # python would not force delete QGraphicsItem
            self.removeItem(i)
            self.mObjectItems.erase(o)

    ##
    # Updates the map object items related to the given objects.
    ##
    def objectsChanged(self, objects):
        for object in objects:
            item = self.itemForObject(object)
            item.syncWithMapObject()

    ##
    # Updates the Z value of the objects when appropriate.
    ##
    def objectsIndexChanged(self, objectGroup, first, last):
        if (objectGroup.drawOrder() != ObjectGroup.DrawOrder.IndexOrder):
            return
        for i in range(first, last + 1):
            item = self.itemForObject(objectGroup.objectAt(i))
            item.setZValue(i)

    def updateSelectedObjectItems(self):
        objects = self.mMapDocument.selectedObjects()
        items = QSet()
        for object in objects:
            item = self.itemForObject(object)
            if item:
                items.insert(item)

        self.mSelectedObjectItems = items
        self.selectedObjectItemsChanged.emit()

    def syncAllObjectItems(self):
        for item in self.mObjectItems:
            item.syncWithMapObject()

    def createLayerItem(self, layer):
        layerItem = None
        tl = layer.asTileLayer()
        if tl:
            layerItem = TileLayerItem(tl, self.mMapDocument)
        else:
            og = layer.asObjectGroup()
            if og:
                drawOrder = og.drawOrder()
                ogItem = ObjectGroupItem(og)
                objectIndex = 0
                for object in og.objects():
                    item = MapObjectItem(object, self.mMapDocument, ogItem)
                    if (drawOrder == ObjectGroup.DrawOrder.TopDownOrder):
                        item.setZValue(item.y())
                    else:
                        item.setZValue(objectIndex)
                    self.mObjectItems.insert(object, item)
                    objectIndex += 1

                layerItem = ogItem
            else:
                il = layer.asImageLayer()
                if il:
                    layerItem = ImageLayerItem(il, self.mMapDocument)

        layerItem.setVisible(layer.isVisible())
        return layerItem

    def updateSceneRect(self):
        mapSize = self.mMapDocument.renderer().mapSize()
        sceneRect = QRectF(0, 0, mapSize.width(), mapSize.height())

        margins = self.mMapDocument.map().computeLayerOffsetMargins()
        sceneRect.adjust(-margins.left(), -margins.top(), margins.right(),
                         margins.bottom())

        self.setSceneRect(sceneRect)
        self.mDarkRectangle.setRect(sceneRect)

    def updateCurrentLayerHighlight(self):
        if (not self.mMapDocument):
            return
        currentLayerIndex = self.mMapDocument.currentLayerIndex()
        if (not self.mHighlightCurrentLayer or currentLayerIndex == -1):
            self.mDarkRectangle.setVisible(False)
            # Restore opacity for all layers
            for i in range(self.mLayerItems.size()):
                layer = self.mMapDocument.map().layerAt(i)
                self.mLayerItems.at(i).setOpacity(layer.opacity())

            return

        # Darken layers below the current layer
        self.mDarkRectangle.setZValue(currentLayerIndex - 0.5)
        self.mDarkRectangle.setVisible(True)
        # Set layers above the current layer to half opacity
        for i in range(1, self.mLayerItems.size()):
            layer = self.mMapDocument.map().layerAt(i)
            if currentLayerIndex < i:
                _x = opacityFactor
            else:
                _x = 1
            multiplier = _x
            self.mLayerItems.at(i).setOpacity(layer.opacity() * multiplier)

    def eventFilter(self, object, event):
        x = event.type()
        if x == QEvent.KeyPress or x == QEvent.KeyRelease:
            keyEvent = event
            newModifiers = keyEvent.modifiers()
            if (self.mActiveTool and newModifiers != self.mCurrentModifiers):
                self.mActiveTool.modifiersChanged(newModifiers)
                self.mCurrentModifiers = newModifiers
        else:
            pass

        return False