Skip to content

Commit

Permalink
Merge pull request #234 from Leviaria/main
Browse files Browse the repository at this point in the history
GUI
  • Loading branch information
Leviaria authored Sep 13, 2024
2 parents 778ad0b + 3d2f992 commit 8c56c03
Show file tree
Hide file tree
Showing 15 changed files with 634 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ extension-pkg-allow-list=
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
extension-pkg-whitelist=PyQt6

# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
Expand Down
60 changes: 22 additions & 38 deletions clashroyalebuildabot/bot/bot.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import os
import random
import sys
import threading
import time

import keyboard
from loguru import logger
import yaml

from clashroyalebuildabot.constants import ALL_TILES
from clashroyalebuildabot.constants import ALLY_TILES
from clashroyalebuildabot.constants import DEBUG_DIR
from clashroyalebuildabot.constants import DISPLAY_CARD_DELTA_X
from clashroyalebuildabot.constants import DISPLAY_CARD_HEIGHT
from clashroyalebuildabot.constants import DISPLAY_CARD_INIT_X
Expand All @@ -19,7 +15,6 @@
from clashroyalebuildabot.constants import DISPLAY_HEIGHT
from clashroyalebuildabot.constants import LEFT_PRINCESS_TILES
from clashroyalebuildabot.constants import RIGHT_PRINCESS_TILES
from clashroyalebuildabot.constants import SRC_DIR
from clashroyalebuildabot.constants import TILE_HEIGHT
from clashroyalebuildabot.constants import TILE_INIT_X
from clashroyalebuildabot.constants import TILE_INIT_Y
Expand All @@ -39,22 +34,17 @@ class Bot:
is_paused_logged = False
is_resumed_logged = True

def __init__(self, actions, auto_start=True):
def __init__(self, actions, config):
self.actions = actions
self.auto_start = auto_start
self.auto_start = config["bot"]["auto_start_game"]
self.end_of_game_clicked = False

self._setup_logger()
self.should_run = True

cards = [action.CARD for action in actions]
if len(cards) != 8:
raise ValueError(f"Must provide 8 cards but was given: {cards}")
self.cards_to_actions = dict(zip(cards, actions))

config_path = os.path.join(SRC_DIR, "config.yaml")
with open(config_path, encoding="utf-8") as file:
config = yaml.safe_load(file)

self.visualizer = Visualizer(**config["visuals"])
self.emulator = Emulator(**config["adb"])
self.detector = Detector(cards=cards)
Expand All @@ -78,34 +68,24 @@ def _log_and_wait(prefix, delay):
logger.info(message)
time.sleep(delay)

@staticmethod
def _setup_logger():
config_path = os.path.join(SRC_DIR, "config.yaml")
with open(config_path, encoding="utf-8") as file:
config = yaml.safe_load(file)
log_level = config.get("bot", {}).get("log_level", "INFO").upper()
logger.remove()
logger.add(sys.stdout, level=log_level)
logger.add(
os.path.join(DEBUG_DIR, "bot.log"),
rotation="500 MB",
level=log_level,
)

@staticmethod
def _handle_keyboard_shortcut():
while True:
keyboard.wait("ctrl+p")
if pause_event.is_set():
logger.info("Bot paused.")
pause_event.clear()
Bot.is_paused_logged = True
Bot.is_resumed_logged = False
else:
logger.info("Bot resumed.")
pause_event.set()
Bot.is_resumed_logged = True
Bot.is_paused_logged = False
Bot.pause_or_resume()

@staticmethod
def pause_or_resume():
if pause_event.is_set():
logger.info("Bot paused.")
pause_event.clear()
Bot.is_paused_logged = True
Bot.is_resumed_logged = False
else:
logger.info("Bot resumed.")
pause_event.set()
Bot.is_resumed_logged = True
Bot.is_paused_logged = False

@staticmethod
def _get_nearest_tile(x, y):
Expand Down Expand Up @@ -232,11 +212,15 @@ def step(self):

def run(self):
try:
while True:
while self.should_run:
if not pause_event.is_set():
time.sleep(0.1)
continue

self.step()
logger.info("Thanks for using CRBAB, see you next time!")
except KeyboardInterrupt:
logger.info("Thanks for using CRBAB, see you next time!")

def stop(self):
self.should_run = False
4 changes: 3 additions & 1 deletion clashroyalebuildabot/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ bot:
log_level: "DEBUG"

# Create a deck code when the game starts
load_deck: True
load_deck: False
auto_start_game: False
enable_gui: True

adb:
# The IP address of your device or emulator.
Expand Down
29 changes: 29 additions & 0 deletions clashroyalebuildabot/gui/animations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from PyQt6.QtCore import QEasingCurve
from PyQt6.QtCore import QPropertyAnimation
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QGraphicsDropShadowEffect


def start_play_button_animation(self):
self.glow_effect = QGraphicsDropShadowEffect(self)
self.glow_effect.setBlurRadius(
10
) # Initial blur radius for the glow effect
self.glow_effect.setColor(Qt.GlobalColor.cyan)
self.glow_effect.setOffset(
0, 0
) # Center the shadow to create the glow effect
self.start_stop_button.setGraphicsEffect(self.glow_effect)

_start_glow_animation(self)


def _start_glow_animation(self):
"""Create a glow effect animation."""
self.glow_animation = QPropertyAnimation(self.glow_effect, b"blurRadius")
self.glow_animation.setStartValue(0)
self.glow_animation.setEndValue(25)
self.glow_animation.setDuration(2000)
self.glow_animation.setEasingCurve(QEasingCurve.Type.SineCurve)
self.glow_animation.setLoopCount(-1)
self.glow_animation.start()
45 changes: 45 additions & 0 deletions clashroyalebuildabot/gui/gameplay_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from PyQt6.QtGui import QImage
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import QLabel
from PyQt6.QtWidgets import QVBoxLayout
from PyQt6.QtWidgets import QWidget


class ImageStreamWindow(QWidget):
def __init__(self):
super().__init__()

self.image = QLabel(self)
self.inactiveIndicator = QLabel(self)
self.inactiveIndicator.setText(
"The visualizer is disabled. Enable it in the Settings tab."
)
self.inactiveIndicator.setStyleSheet(
"background-color: #FFA500; color: white; padding: 5px; height: fit-content; width: fit-content;"
)
self.inactiveIndicator.setMaximumHeight(30)
layout = QVBoxLayout()
layout.addWidget(self.inactiveIndicator)
layout.addWidget(self.image)
self.setLayout(layout)

def update_frame(self, annotated_image):
height, width, channel = annotated_image.shape
bytes_per_line = 3 * width
q_image = QImage(
annotated_image.data.tobytes(),
width,
height,
bytes_per_line,
QImage.Format.Format_RGB888,
)

pixmap = QPixmap.fromImage(q_image)
self.image.setPixmap(pixmap)

def update_active_state(self, active):
if not active:
self.inactiveIndicator.show()
else:
self.inactiveIndicator.hide()
self.image.clear()
Loading

0 comments on commit 8c56c03

Please sign in to comment.