import sys from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QListWidget, QLineEdit, QLabel, QCheckBox, QPushButton, QFormLayout, QListWidgetItem, QSplitter, QTreeWidget, QTreeWidgetItem, QDialog, QDialogButtonBox, QComboBox, QMessageBox, QSpinBox, QMenu, QTabWidget, QTextEdit) from PyQt6.QtCore import Qt, QSize, qInstallMessageHandler, QThread, pyqtSignal from PyQt6.QtGui import QPixmap, QFontMetrics, QColor, QAction import sqlite3 import json import os import traceback import time import pdb import debugpy # Add the parent directory to the Python path sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Now try to import from db_controller import DBController from pokemon_db_ui import PokemonUI from DataGatherers.update_location_information import process_pokemon_for_location_data from DataGatherers.cache_manager import CacheManager from event_system import event_system import logging class UILogHandler(logging.Handler): def __init__(self): super().__init__() def emit(self, record): log_entry = self.format(record) # Format the log message event_system.emit('update_log_display', log_entry) class PokemonFormGatherer(QThread): progress_signal = pyqtSignal(str) finished_signal = pyqtSignal() def run(self): cache = CacheManager() debugpy.debug_this_thread() from DataGatherers.pokemondb_scraper import retrieve_all_pokemon_forms retrieve_all_pokemon_forms(cache, progress_callback=self.progress_signal.emit) self.finished_signal.emit() class PokemonHomeStatusUpdater(QThread): progress_signal = pyqtSignal(str) finished_signal = pyqtSignal() def run(self): cache = CacheManager() debugpy.debug_this_thread() from DataGatherers.update_storable_in_home import update_storable_in_home update_storable_in_home(cache, progress_callback=self.progress_signal.emit) self.finished_signal.emit() class PokemonEvolutionUpdater(QThread): progress_signal = pyqtSignal(str) finished_signal = pyqtSignal() def run(self): time.sleep(0.1) #pdb.set_trace() debugpy.debug_this_thread() cache = CacheManager() from DataGatherers.Update_evolution_information import update_evolution_chains update_evolution_chains(cache, progress_callback=self.progress_signal.emit) self.finished_signal.emit() class PokemonEncounterUpdater(QThread): progress_signal = pyqtSignal(str) finished_signal = pyqtSignal() def run(self): time.sleep(0.1) debugpy.debug_this_thread() cache = CacheManager() from DataGatherers.update_location_information import update_location_information update_location_information(cache, progress_callback=self.progress_signal.emit) self.finished_signal.emit() class DBEditor(QMainWindow): def __init__(self): super().__init__() self.setup_logging() self.setWindowTitle("Pokémon Database Editor") self.setGeometry(100, 100, 1000, 600) event_system.add_listener('save_changes', self.save_changes) event_system.add_listener('add_new_encounter', self.add_new_encounter) event_system.add_listener('refresh_pokemon_encounters', self.refresh_pokemon_encounters) event_system.add_listener('gather_pokemon_forms', self.gather_pokemon_forms) event_system.add_listener('gather_home_storage_info', self.gather_home_storage_info) event_system.add_listener('gather_evolution_info', self.gather_evolution_info) event_system.add_listener('gather_encounter_info', self.gather_encounter_info) event_system.add_listener('reinitialize_database', self.reinitialize_database) self.conn = sqlite3.connect(':memory:', check_same_thread=False) # Use in-memory database for runtime self.cursor = self.conn.cursor() self.init_database() self.init_ui() def init_database(self): self.db_controller = DBController() self.db_controller.init_database() def setup_logging(self): # Create the logger with the name 'ui_feedback' self.logger = logging.getLogger('ui_feedback') self.logger.setLevel(logging.DEBUG) # Set the logging level # Create handlers console_handler = logging.StreamHandler() # Log to the console file_handler = logging.FileHandler('ui_feedback.log') # Log to a file ui_handler = UILogHandler() # Create formatters and add them to handlers formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler.setFormatter(formatter) file_handler.setFormatter(formatter) ui_handler.setFormatter(formatter) # Add the handler to the logger self.logger.addHandler(ui_handler) # Add handlers to the logger self.logger.addHandler(console_handler) self.logger.addHandler(file_handler) def load_and_apply_patches(self): try: with open('patches.json', 'r') as f: patches = json.load(f) except FileNotFoundError: patches = {} # Apply patches to the in-memory database for patch_key, patch in patches.items(): if patch_key.startswith('evolution_'): from_pfic, to_pfic = patch_key.split('_')[1:] if patch['action'] == 'delete': self.cursor.execute(''' DELETE FROM evolution_chains WHERE from_pfic = ? AND to_pfic = ? ''', (from_pfic, to_pfic)) elif patch['action'] == 'update': self.cursor.execute(''' UPDATE evolution_chains SET from_pfic = ?, to_pfic = ?, method = ? WHERE from_pfic = ? AND to_pfic = ? ''', (patch['new_from_pfic'], patch['new_to_pfic'], patch['new_method'], from_pfic, to_pfic)) elif patch['action'] == 'add': self.cursor.execute(''' INSERT OR REPLACE INTO evolution_chains (from_pfic, to_pfic, method) VALUES (?, ?, ?) ''', (patch['from_pfic'], patch['to_pfic'], patch['method'])) else: # pokemon_storage patches self.cursor.execute(''' UPDATE pokemon_storage SET storable_in_home = ? WHERE PFIC = ? ''', (patch['storable_in_home'], patch_key)) self.conn.commit() return patches def save_changes(self, data): # Your existing code to save changes pass def add_new_encounter(self, data): # Your existing code to add a new encounter pass def init_ui(self): # Create a central widget for the main window central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # Create the PokemonUI as a child widget self.pokemon_ui = PokemonUI(self) main_layout.addWidget(self.pokemon_ui) def update_home_storable(self): if hasattr(self, 'current_pfic'): storable_in_home = self.home_checkbox.isChecked() self.cursor.execute('UPDATE pokemon_storage SET storable_in_home = ? WHERE PFIC = ?', (storable_in_home, self.current_pfic)) self.conn.commit() self.filter_pokemon_list() # Reapply the filter def edit_evolution(self, item, column): parent = item.parent() if not parent: return # Don't edit the root item #from_pfic = parent.data(0, Qt.ItemDataRole.UserRole) #to_pfic = item.data(0, Qt.ItemDataRole.UserRole) #method = item.text(1) #dialog = EvolutionEditDialog(self, from_pfic, to_pfic, method) #result = dialog.exec() #if result == QDialog.DialogCode.Accepted: # new_from_pfic = dialog.from_combo.currentData() # new_to_pfic = dialog.to_combo.currentData() # new_method = dialog.method_edit.text() # Update the in-memory database # self.cursor.execute(''' # UPDATE evolution_chains # SET from_pfic = ?, to_pfic = ?, method = ? # WHERE from_pfic = ? AND to_pfic = ? # ''', (new_from_pfic, new_to_pfic, new_method, from_pfic, to_pfic)) # # Create or update the patch # patch_key = f"evolution_{from_pfic}_{to_pfic}" # self.patches[patch_key] = { # 'action': 'update', # 'new_from_pfic': new_from_pfic, # 'new_to_pfic': new_to_pfic, # 'new_method': new_method # } # self.save_patches() # Refresh the evolution chain display # self.load_evolution_chain(self.current_pfic) #elif result == 2: # Delete action # confirm = QMessageBox.question(self, "Confirm Deletion", "Are you sure you want to delete this evolution?", # QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) # if confirm == QMessageBox.StandardButton.Yes: # # Delete from the in-memory database # self.cursor.execute(''' # DELETE FROM evolution_chains # WHERE from_pfic = ? AND to_pfic = ? # ''', (from_pfic, to_pfic)) # Create a delete patch # patch_key = f"evolution_{from_pfic}_{to_pfic}" # self.patches[patch_key] = {'action': 'delete'} # self.save_patches() # Refresh the evolution chain display # self.load_evolution_chain(self.current_pfic) def closeEvent(self, event): self.conn.close() event.accept() def check_pokemon_has_encounters(self, pfic): return self.encounter_cache.get(pfic, False) def manage_exclusive_groups(self): dialog = QDialog(self) dialog.setWindowTitle("Manage Exclusive Encounter Groups") layout = QVBoxLayout(dialog) group_list = QListWidget() self.cursor.execute('SELECT id, group_name, description FROM exclusive_encounter_groups') for group_id, group_name, description in self.cursor.fetchall(): item = QListWidgetItem(f"{group_name} - {description}") item.setData(Qt.ItemDataRole.UserRole, group_id) group_list.addItem(item) layout.addWidget(group_list) add_button = QPushButton("Add Group") edit_button = QPushButton("Edit Group") delete_button = QPushButton("Delete Group") button_layout = QHBoxLayout() button_layout.addWidget(add_button) button_layout.addWidget(edit_button) button_layout.addWidget(delete_button) layout.addLayout(button_layout) add_button.clicked.connect(lambda: self.add_edit_exclusive_group(dialog, group_list)) edit_button.clicked.connect(lambda: self.add_edit_exclusive_group(dialog, group_list, group_list.currentItem())) delete_button.clicked.connect(lambda: self.delete_exclusive_group(dialog, group_list, group_list.currentItem())) dialog.exec() def add_edit_exclusive_group(self, parent_dialog, group_list, item=None): dialog = QDialog(parent_dialog) dialog.setWindowTitle("Add/Edit Exclusive Group") layout = QFormLayout(dialog) name_edit = QLineEdit() description_edit = QLineEdit() game_combo = QComboBox() layout.addRow("Group Name:", name_edit) layout.addRow("Description:", description_edit) layout.addRow("Game:", game_combo) # Populate game combo box self.cursor.execute('SELECT id, name FROM games ORDER BY name') for game_id, game_name in self.cursor.fetchall(): game_combo.addItem(game_name, game_id) if item: group_id = item.data(Qt.ItemDataRole.UserRole) self.cursor.execute('SELECT group_name, description, game_id FROM exclusive_encounter_groups WHERE id = ?', (group_id,)) group_name, description, game_id = self.cursor.fetchone() name_edit.setText(group_name) description_edit.setText(description) game_combo.setCurrentIndex(game_combo.findData(game_id)) buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) buttons.accepted.connect(dialog.accept) buttons.rejected.connect(dialog.reject) layout.addRow(buttons) if dialog.exec() == QDialog.DialogCode.Accepted: name = name_edit.text() description = description_edit.text() game_id = game_combo.currentData() if item: self.cursor.execute('UPDATE exclusive_encounter_groups SET group_name = ?, description = ?, game_id = ? WHERE id = ?', (name, description, game_id, group_id)) item.setText(f"{name} - {description} ({game_combo.currentText()})") else: self.cursor.execute('INSERT INTO exclusive_encounter_groups (group_name, description, game_id) VALUES (?, ?, ?)', (name, description, game_id)) group_id = self.cursor.lastrowid new_item = QListWidgetItem(f"{name} - {description} ({game_combo.currentText()})") new_item.setData(Qt.ItemDataRole.UserRole, group_id) group_list.addItem(new_item) self.conn.commit() def delete_exclusive_group(self, parent_dialog, group_list, item): if item: group_id = item.data(Qt.ItemDataRole.UserRole) reply = QMessageBox.question(parent_dialog, 'Delete Group', 'Are you sure you want to delete this group?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: self.cursor.execute('DELETE FROM exclusive_encounter_groups WHERE id = ?', (group_id,)) self.cursor.execute('UPDATE encounters SET exclusive_group_id = NULL WHERE exclusive_group_id = ?', (group_id,)) self.conn.commit() group_list.takeItem(group_list.row(item)) def refresh_pokemon_encounters(self, pfic): pokemon_data = event_system.call_sync('get_pokemon_data', pfic) if pokemon_data: name, form_name, national_dex, generation, storable_in_home, is_baby_form = pokemon_data # Create a temporary connection for this operation temp_conn = sqlite3.connect('pokemon_forms.db') try: with open('./DataGatherers/DefaultForms.json', 'r') as f: default_forms = json.load(f) except FileNotFoundError: default_forms = [] # Create a cache manager instance cache = CacheManager() event_system.emit_sync('clear_encounters_for_pokemon', pfic) # Process the Pokémon data process_pokemon_for_location_data(pfic, name, form_name, national_dex, default_forms, cache, temp_conn) event_system.emit_sync('refresh_in_memory_db', temp_conn) # Close the temporary connection temp_conn.close() event_system.emit_sync('refresh_pokemon_details_panel', pfic) print(f"Refreshed encounters for {name} {form_name if form_name else ''}") def gather_pokemon_forms(self, data): event_system.emit_sync('clear_log_display') self.logger.info("Starting to gather Pokémon forms...") self.form_gatherer = PokemonFormGatherer() self.form_gatherer.progress_signal.connect(self.update_progress) self.form_gatherer.finished_signal.connect(self.gathering_finished) self.form_gatherer.start() def update_progress(self, message): self.logger.info(message) def gathering_finished(self): self.logger.info("Finished gathering Pokémon forms.") def gather_home_storage_info(self, data): event_system.emit_sync('clear_log_display') self.logger.info("Starting to gather Home storage information...") self.home_storage_updater = PokemonHomeStatusUpdater() self.home_storage_updater.progress_signal.connect(self.update_progress) self.home_storage_updater.finished_signal.connect(self.updating_home_storage_finished) self.home_storage_updater.start() def updating_home_storage_finished(self): self.logger.info("Finished updating Home storage information.") def gather_evolution_info(self, data): event_system.call_sync('clear_log_display') self.logger.info("Starting to gather evolution information...") self.evolution_updater = PokemonEvolutionUpdater() self.evolution_updater.progress_signal.connect(self.update_progress) self.evolution_updater.finished_signal.connect(self.updating_evolution_info_finished) self.evolution_updater.start() def updating_evolution_info_finished(self): self.logger.info("Finished updating evolution information.") def gather_encounter_info(self, data): event_system.call_sync('clear_log_display') self.logger.info("Starting to gather encounter information...") self.encounter_updater = PokemonEncounterUpdater() self.encounter_updater.progress_signal.connect(self.update_progress) self.encounter_updater.finished_signal.connect(self.updating_encounter_info_finished) self.encounter_updater.start() def updating_encounter_info_finished(self): self.logger.info("Finished updating encounter information.") def reinitialize_database(self, data): reply = QMessageBox.question(self, 'Confirm Action', 'Are you sure you want to clear and reinitialize the database? This action cannot be undone.', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: self.logger.info("Starting database reinitialization...") # Implement the logic to clear and reinitialize the database self.conn.close() os.remove('pokemon_forms.db') # Remove the existing database file self.conn = sqlite3.connect('pokemon_forms.db') self.cursor = self.conn.cursor() self.init_database() # Reinitialize the database structure self.logger.info("Database reinitialized successfully.") QMessageBox.information(self, 'Database Reinitialized', 'The database has been cleared and reinitialized.') def qt_message_handler(mode, context, message): print(f"Qt Message: {mode} {context} {message}") def exception_hook(exctype, value, tb): print(''.join(traceback.format_exception(exctype, value, tb))) sys.exit(1) sys.excepthook = exception_hook if __name__ == '__main__': app = QApplication(sys.argv) qInstallMessageHandler(qt_message_handler) editor = DBEditor() editor.show() sys.exit(app.exec())