diff --git a/clashroyalebuildabot/gui/new_gui.py b/clashroyalebuildabot/gui/new_gui.py
new file mode 100644
index 0000000..c811543
--- /dev/null
+++ b/clashroyalebuildabot/gui/new_gui.py
@@ -0,0 +1,339 @@
+import logging
+
+from PyQt6.QtWidgets import (
+ QApplication,
+ QMainWindow,
+ QWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QPushButton,
+ QLabel,
+ QTextEdit,
+ QFrame,
+ QTabWidget,
+ QCheckBox,
+ QLineEdit,
+ QComboBox,
+ QFormLayout,
+ QDoubleSpinBox,
+ QGroupBox,
+ QGridLayout,
+)
+from PyQt6.QtGui import QFont
+from PyQt6.QtCore import Qt, QMetaObject, Q_ARG
+from threading import Thread
+
+
+from clashroyalebuildabot import Bot
+from clashroyalebuildabot.actions.generic.action import Action
+import sys
+
+class QTextEditLogger(logging.Handler):
+ def __init__(self, text_edit):
+ super().__init__()
+ self.text_edit = text_edit
+
+ def emit(self, record):
+ log_entry = self.format(record)
+ QMetaObject.invokeMethod(
+ self.text_edit,
+ "append",
+ Qt.ConnectionType.QueuedConnection,
+ Q_ARG(str, log_entry)
+ )
+
+class MainWindow(QMainWindow):
+ def __init__(self, config, actions):
+ super().__init__()
+
+ self.config = config
+ self.actions = actions
+ self.bot = None
+ self.bot_thread = None
+ self.is_running = False
+
+
+
+ self.setWindowTitle("Clash Royale Build-A-Bot")
+ self.setGeometry(100, 100, 900, 600)
+
+ main_widget = QWidget(self)
+ self.setCentralWidget(main_widget)
+ main_layout = QVBoxLayout(main_widget)
+
+ top_bar = QFrame()
+ top_bar.setStyleSheet("background-color: #1E272E;")
+ top_bar_layout = QHBoxLayout(top_bar)
+
+ left_layout = QVBoxLayout()
+ left_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
+
+ server_name = QLabel("Clash Royale Build-A-Bot")
+ server_name.setStyleSheet("font-weight: bold; font-size: 16pt; color: white;")
+ left_layout.addWidget(server_name)
+
+ server_details = QLabel('https://github.com/Pbatch/ClashRoyaleBuildABot')
+ server_details.setOpenExternalLinks(True)
+ server_details.setStyleSheet("color: #57A6FF;")
+ left_layout.addWidget(server_details)
+
+ self.server_id_label = QLabel("Status")
+ self.server_id_label.setStyleSheet("color: #999;")
+ left_layout.addWidget(self.server_id_label)
+
+ port_link = QLabel('127.0.0.1:5555')
+ port_link.setOpenExternalLinks(True)
+ left_layout.addWidget(port_link)
+
+ top_bar_layout.addLayout(left_layout)
+
+ right_layout = QVBoxLayout()
+ right_layout.setAlignment(Qt.AlignmentFlag.AlignRight)
+
+ button_layout = QHBoxLayout()
+ button_layout.setAlignment(Qt.AlignmentFlag.AlignRight)
+
+ self.start_stop_button = QPushButton("▶")
+ self.start_stop_button.setFont(QFont("Arial", 18))
+ self.start_stop_button.setStyleSheet("""
+ QPushButton {
+ background-color: #4B6EAF;
+ color: white;
+ min-width: 32px;
+ max-width: 32px;
+ min-height: 32px;
+ max-height: 32px;
+ border-radius: 5px;
+ }
+ QPushButton:hover {
+ background-color: #5C7EBF;
+ }
+ """)
+ self.start_stop_button.clicked.connect(self.toggle_start_stop)
+
+ restart_button = QPushButton("↻")
+ restart_button.setFont(QFont("Arial", 18))
+ restart_button.setStyleSheet("""
+ QPushButton {
+ background-color: #4B6EAF;
+ color: white;
+ min-width: 32px;
+ max-width: 32px;
+ min-height: 32px;
+ max-height: 32px;
+ border-radius: 5px;
+ }
+ QPushButton:hover {
+ background-color: #5C7EBF;
+ }
+ """)
+ restart_button.clicked.connect(self.restart_bot)
+
+ delete_button = QPushButton("🗑")
+ delete_button.setFont(QFont("Arial", 18))
+ delete_button.setStyleSheet("""
+ QPushButton {
+ background-color: #e04f5f;
+ color: white;
+ min-width: 32px;
+ max-width: 32px;
+ min-height: 32px;
+ max-height: 32px;
+ border-radius: 5px;
+ }
+ QPushButton:hover {
+ background-color: #F06F6F;
+ }
+ """)
+
+ button_layout.addWidget(self.start_stop_button)
+ button_layout.addWidget(restart_button)
+ button_layout.addWidget(delete_button)
+
+ top_bar_layout.addStretch()
+ top_bar_layout.addLayout(right_layout)
+ top_bar_layout.addLayout(button_layout)
+
+ main_layout.addWidget(top_bar)
+
+ tab_widget = QTabWidget()
+ tab_widget.setStyleSheet("""
+ QTabWidget::pane { border: 0; }
+ QTabBar::tab {
+ background: #333;
+ color: white;
+ padding: 8px;
+ margin-bottom: -1px;
+ }
+ QTabBar::tab:selected {
+ background: #1E272E;
+ border-bottom: 2px solid #57A6FF;
+ }
+ """)
+
+ logs_tab = QWidget()
+ logs_layout = QVBoxLayout(logs_tab)
+ self.log_display = QTextEdit()
+ self.log_display.setReadOnly(True)
+ self.log_display.setText("Lorem Ipsum - Press Start for Text")
+ self.log_display.setStyleSheet("background-color: #1e1e1e; color: lightgrey; font-family: monospace;")
+ logs_layout.addWidget(self.log_display)
+ tab_widget.addTab(logs_tab, "Logs")
+
+ settings_tab = QWidget()
+ settings_layout = QGridLayout(settings_tab)
+
+ bot_group = QGroupBox("Bot ")
+ bot_layout = QFormLayout()
+ self.log_level_dropdown = QComboBox()
+ self.log_level_dropdown.addItems(["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"])
+ bot_layout.addRow("Log Level:", self.log_level_dropdown)
+
+ self.adb_ip_input = QLineEdit()
+ self.adb_ip_input.setPlaceholderText("127.0.0.1")
+ self.device_serial_input = QLineEdit()
+ self.device_serial_input.setPlaceholderText("emulator-5554")
+ bot_layout.addRow("ADB IP Address:", self.adb_ip_input)
+ bot_layout.addRow("Device Serial:", self.device_serial_input)
+
+ bot_group.setLayout(bot_layout)
+
+ visuals_group = QGroupBox("Visuals Settings")
+ visuals_layout = QFormLayout()
+ self.save_labels_checkbox = QCheckBox("Save labels")
+ self.save_images_checkbox = QCheckBox("Save images")
+ self.show_images_checkbox = QCheckBox("Show images")
+ visuals_layout.addRow(self.save_labels_checkbox)
+ visuals_layout.addRow(self.save_images_checkbox)
+ visuals_layout.addRow(self.show_images_checkbox)
+ visuals_group.setLayout(visuals_layout)
+
+ ingame_group = QGroupBox("Ingame Settings")
+ ingame_layout = QFormLayout()
+
+ self.play_action_delay_input = QDoubleSpinBox()
+ self.play_action_delay_input.setRange(0.1, 5.0)
+ self.play_action_delay_input.setSingleStep(0.1)
+ self.play_action_delay_input.setValue(0.5)
+ ingame_layout.addRow("Action Delay (sec):", self.play_action_delay_input)
+
+ self.load_deck_checkbox = QCheckBox("Create deck code on game start")
+ ingame_layout.addRow(self.load_deck_checkbox)
+
+ self.auto_start_game_checkbox = QCheckBox("Auto start game")
+ ingame_layout.addRow(self.auto_start_game_checkbox)
+
+ ingame_group.setLayout(ingame_layout)
+
+
+
+ settings_layout.addWidget(bot_group, 0, 0)
+ settings_layout.addWidget(visuals_group, 1, 0)
+ settings_layout.addWidget(ingame_group, 0, 1, 2, 1)
+
+ tab_widget.addTab(settings_tab, "Settings")
+
+ stats_tab = QWidget()
+ tab_widget.addTab(stats_tab, "Stats")
+
+ main_layout.addWidget(tab_widget)
+
+ self.setStyleSheet("""
+ QMainWindow {
+ background-color: #0D1117;
+ }
+ QLabel {
+ color: white;
+ padding: 2px;
+ }
+ QPushButton {
+ border: none;
+ padding: 8px;
+ }
+ QFrame {
+ background-color: #1E272E;
+ }
+ """)
+
+
+
+ def toggle_start_stop(self):
+ if self.is_running:
+ self.stop_bot()
+ else:
+ self.start_bot()
+
+ def start_bot(self):
+ if self.is_running:
+ return
+ self.update_config()
+ self.is_running = True
+ self.bot_thread = Thread(target=self.bot_task)
+ self.bot_thread.daemon = True
+ self.bot_thread.start()
+ self.start_stop_button.setText("■")
+ self.start_stop_button.setFont(QFont("Arial", 18))
+ self.start_stop_button.setStyleSheet("""
+ QPushButton {
+ background-color: #e04f5f;
+ color: white;
+ min-width: 32px;
+ max-width: 32px;
+ min-height: 32px;
+ max-height: 32px;
+ border-radius: 5px;
+ }
+ QPushButton:hover {
+ background-color: #F06F6F;
+ }
+ """)
+ self.server_id_label.setText("Status - Running")
+ self.append_log("Bot started")
+
+ def stop_bot(self):
+ if not self.bot:
+ return
+ self.bot.stop()
+ self.is_running = False
+ self.start_stop_button.setText("▶")
+ self.start_stop_button.setFont(QFont("Arial", 18))
+ self.start_stop_button.setStyleSheet("""
+ QPushButton {
+ background-color: #4B6EAF;
+ color: white;
+ min-width: 32px;
+ max-width: 32px;
+ min-height: 32px;
+ max-height: 32px;
+ border-radius: 5px;
+ }
+ QPushButton:hover {
+ background-color: #5C7EBF;
+ }
+ """)
+ self.server_id_label.setText("Status - Stopped")
+ self.append_log("Bot stopped")
+
+ def restart_bot(self):
+ if self.is_running:
+ self.stop_bot()
+ self.update_config()
+ self.start_bot()
+
+ def update_config(self):
+ self.config["visuals"]["save_labels"] = self.save_labels_checkbox.isChecked()
+ self.config["visuals"]["save_images"] = self.save_images_checkbox.isChecked()
+ self.config["visuals"]["show_images"] = self.show_images_checkbox.isChecked()
+ self.config["bot"]["load_deck"] = self.load_deck_checkbox.isChecked()
+ self.config["bot"]["auto_start_game"] = self.auto_start_game_checkbox.isChecked()
+ self.config["bot"]["log_level"] = self.log_level_dropdown.currentText()
+ self.config["ingame"]["play_action"] = float(self.play_action_delay_input.value())
+ self.config["adb"]["ip"] = self.adb_ip_input.text()
+ self.config["adb"]["device_serial"] = self.device_serial_input.text()
+
+ def bot_task(self):
+ self.bot = Bot(actions=self.actions, config=self.config)
+ self.bot.run()
+
+ def append_log(self, message):
+ self.log_display.append(message)
diff --git a/main.py b/main.py
index fcf02e5..44510c3 100644
--- a/main.py
+++ b/main.py
@@ -1,3 +1,4 @@
+import logging
import os
from datetime import datetime
import signal
@@ -6,6 +7,7 @@
import time
import yaml
+from PyQt6.QtWidgets import QApplication
from loguru import logger
from clashroyalebuildabot.actions import ArchersAction
@@ -19,6 +21,7 @@
from clashroyalebuildabot.bot import Bot
from clashroyalebuildabot.constants import SRC_DIR
from clashroyalebuildabot.gui.gui import Gui
+from clashroyalebuildabot.gui.new_gui import MainWindow, QTextEditLogger
start_time = datetime.now()
@@ -65,8 +68,18 @@ def main():
# GUI MODE
if config["bot"]["enable_gui"]:
- gui = Gui(config=config, actions=actions)
- gui.run()
+ # gui = Gui(config=config, actions=actions)
+ # gui.run()
+ app = QApplication(sys.argv)
+ window = MainWindow(config, actions)
+
+ # Create and add the custom logging handler
+ log_handler = QTextEditLogger(window.log_display)
+ log_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
+ logger.add(log_handler)
+
+ window.show()
+ sys.exit(app.exec())
else:
# REGULAR MODE
bot = Bot(actions=actions, config=config)