You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

482 lines
20 KiB

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)
event_system.add_listener('gather_marks_info', self.gather_marks_info)
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 gather_marks_info(self, data):
event_system.emit_sync('clear_log_display')
self.logger.info("Starting to gather marks information...")
pokemon_list = event_system.call_sync('get_pokemon_list')
for pfic, name, form_name, national_dex in pokemon_list:
pokemon_data = event_system.call_sync('get_pokemon_data', pfic)
#Rule 1
# 1. If a pokemon form has a previous evolution from within the same generation,
# use the mark of the previous evolution. This should be recursive within the same generation.
chain = event_system.call_sync('get_evolution_chain', pfic)
if chain:
find_me = lambda x: x[0] == pfic
target_index = next((i for i, item in enumerate(chain) if find_me(item)), -1)
base_form_in_generation = None
for i in range(target_index, -1, -1):
chain_pfic, chain_name, chain_form_name, method = chain[i]
chain_pokemon_data = event_system.call_sync('get_pokemon_data', chain_pfic)
if chain_pokemon_data[3] == pokemon_data[3]:
base_form_in_generation = chain_pfic
else:
break
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())