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.
 
 

585 lines
23 KiB

import sys
import sqlite3
import json
import os
from datetime import datetime
from PyQt6.QtWidgets import (QApplication, QMainWindow, QListWidget, QLabel,
QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
QTableWidget, QTableWidgetItem, QHeaderView,
QLineEdit, QCheckBox, QFormLayout, QMessageBox,
QDialog, QComboBox, QFileDialog, QTabWidget)
from PyQt6.QtGui import QPixmap
from PyQt6.QtCore import Qt
class PokemonDatabaseApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Pokémon Database Viewer")
self.setGeometry(100, 100, 1200, 600)
# Create an in-memory database
self.conn = sqlite3.connect(':memory:')
self.cursor = self.conn.cursor()
# Load the database from disk into memory
self.load_database()
# Apply all existing patches
self.apply_all_patches()
self.current_pokemon_id = None
self.current_pokemon_name = None
self.current_form_id = None
self.all_pokemon = []
self.patches = {}
self.init_ui()
self.load_locations_list()
def init_ui(self):
main_layout = QVBoxLayout()
# Create tab widget
self.tab_widget = QTabWidget()
# Create and add Pokemon tab
pokemon_tab = self.create_pokemon_tab()
self.tab_widget.addTab(pokemon_tab, "Pokémon")
# Create and add Locations tab
locations_tab = self.create_locations_tab()
self.tab_widget.addTab(locations_tab, "Locations")
main_layout.addWidget(self.tab_widget)
container = QWidget()
container.setLayout(main_layout)
self.setCentralWidget(container)
self.load_pokemon_list()
def create_pokemon_tab(self):
pokemon_tab = QWidget()
tab_layout = QHBoxLayout()
# Pokémon list section
pokemon_list_layout = QVBoxLayout()
# Search box
self.search_box = QLineEdit()
self.search_box.setPlaceholderText("Search Pokémon...")
self.search_box.textChanged.connect(self.filter_pokemon_list)
pokemon_list_layout.addWidget(self.search_box)
# Pokémon list
self.pokemon_list = QListWidget()
self.pokemon_list.itemClicked.connect(self.load_pokemon_data)
pokemon_list_layout.addWidget(self.pokemon_list)
tab_layout.addLayout(pokemon_list_layout, 1)
# Pokémon details
details_layout = QVBoxLayout()
image_and_form_layout = QHBoxLayout()
# Image
self.image_label = QLabel()
self.image_label.setFixedSize(200, 200)
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
image_and_form_layout.addWidget(self.image_label)
# Form details
form_details_layout = QFormLayout()
self.form_name_edit = QLineEdit()
self.is_default_checkbox = QCheckBox()
self.image_path_edit = QLineEdit()
form_details_layout.addRow("Form Name:", self.form_name_edit)
form_details_layout.addRow("Default Form:", self.is_default_checkbox)
form_details_layout.addRow("Image Path:", self.image_path_edit)
# Save button
self.save_button = QPushButton("Save Form Changes")
self.save_button.clicked.connect(self.save_form_changes)
form_details_layout.addRow(self.save_button)
image_and_form_layout.addLayout(form_details_layout)
details_layout.addLayout(image_and_form_layout)
# Forms list and add new form button
forms_layout = QHBoxLayout()
self.forms_list = QListWidget()
self.forms_list.itemClicked.connect(self.load_form_data)
forms_layout.addWidget(self.forms_list)
add_form_button = QPushButton("Add New Form")
add_form_button.clicked.connect(self.add_new_form)
details_layout.addWidget(add_form_button)
details_layout.addWidget(QLabel("Forms:"))
details_layout.addLayout(forms_layout)
# Encounters table and add new encounter button
self.encounters_table = QTableWidget()
self.encounters_table.setColumnCount(3)
self.encounters_table.setHorizontalHeaderLabels(['Game', 'Location', 'Method'])
self.encounters_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
details_layout.addWidget(QLabel("Encounters:"))
details_layout.addWidget(self.encounters_table)
add_encounter_button = QPushButton("Add New Encounter")
add_encounter_button.clicked.connect(self.add_new_encounter)
details_layout.addWidget(add_encounter_button)
tab_layout.addLayout(details_layout, 2)
pokemon_tab.setLayout(tab_layout)
return pokemon_tab
def create_locations_tab(self):
locations_tab = QWidget()
tab_layout = QHBoxLayout()
# Locations list
locations_list_layout = QVBoxLayout()
# Search box for locations
self.locations_search_box = QLineEdit()
self.locations_search_box.setPlaceholderText("Search Locations...")
self.locations_search_box.textChanged.connect(self.filter_locations_list)
locations_list_layout.addWidget(self.locations_search_box)
# Locations list
self.locations_list = QListWidget()
self.locations_list.itemClicked.connect(self.load_location_data)
locations_list_layout.addWidget(self.locations_list)
# Add new location button
add_location_button = QPushButton("Add New Location")
add_location_button.clicked.connect(self.add_new_location)
locations_list_layout.addWidget(add_location_button)
tab_layout.addLayout(locations_list_layout, 1)
# Location details
location_details_layout = QFormLayout()
self.location_name_edit = QLineEdit()
self.location_description_edit = QLineEdit()
location_details_layout.addRow("Name:", self.location_name_edit)
location_details_layout.addRow("description:", self.location_description_edit)
# Save location changes button
save_location_button = QPushButton("Save Location Changes")
save_location_button.clicked.connect(self.save_location_changes)
location_details_layout.addRow(save_location_button)
tab_layout.addLayout(location_details_layout, 2)
locations_tab.setLayout(tab_layout)
return locations_tab
def load_database(self):
disk_conn = sqlite3.connect('pokemon_database.db')
disk_conn.backup(self.conn)
disk_conn.close()
def apply_all_patches(self):
patches_dir = "patches"
if not os.path.exists(patches_dir):
return
patch_files = sorted([f for f in os.listdir(patches_dir) if f.endswith('.json')])
for patch_file in patch_files:
with open(os.path.join(patches_dir, patch_file), 'r') as f:
patches = json.load(f)
self.apply_patches(patches)
def apply_patches(self, patches):
try:
for timestamp, patch in patches.items():
if patch["type"] == "form_update":
self.cursor.execute("""
UPDATE pokemon_forms
SET form_name = ?, is_default = ?, image_path = ?
WHERE id = ?
""", (patch["form_name"], patch["is_default"], patch["image_path"], patch["form_id"]))
elif patch["type"] == "new_form":
self.cursor.execute("""
INSERT INTO pokemon_forms (pokemon_id, form_name, is_default, image_path)
VALUES (?, ?, ?, ?)
""", (patch["pokemon_id"], patch["form_name"], patch["is_default"], patch["image_path"]))
elif patch["type"] == "new_encounter":
self.cursor.execute("""
INSERT INTO form_encounters (form_id, game_id, location_id, encounter_method_id)
VALUES (?,
(SELECT id FROM games WHERE name = ?),
(SELECT id FROM locations WHERE name = ?),
(SELECT id FROM encounter_methods WHERE name = ?))
""", (patch["form_id"], patch["game"], patch["location"], patch["method"]))
elif patch["type"] == "location_update":
self.cursor.execute("""
UPDATE locations
SET name = ?, description = ?
WHERE name = ?
""", (patch["new_name"], patch["description"], patch["old_name"]))
elif patch["type"] == "new_location":
self.cursor.execute("""
INSERT INTO locations (name, description)
VALUES (?, ?)
""", (patch["name"], patch["description"]))
self.conn.commit()
except sqlite3.Error as e:
print(f"An error occurred while applying patches: {e}")
def load_pokemon_list(self):
self.cursor.execute("SELECT national_dex_number, name FROM pokemon ORDER BY national_dex_number")
self.all_pokemon = [f"{row[0]:03d} - {row[1]}" for row in self.cursor.fetchall()]
self.pokemon_list.addItems(self.all_pokemon)
def filter_pokemon_list(self):
search_text = self.search_box.text().lower()
self.pokemon_list.clear()
for pokemon in self.all_pokemon:
if search_text in pokemon.lower():
self.pokemon_list.addItem(pokemon)
def load_pokemon_data(self, item):
self.current_pokemon_id = int(item.text().split('-')[0])
self.current_pokemon_name = item.text().split('-')[1]
# Load forms
self.forms_list.clear()
self.cursor.execute("""
SELECT form_name FROM pokemon_forms
WHERE pokemon_id = ?
ORDER BY is_default DESC, form_name
""", (self.current_pokemon_id,))
for row in self.cursor.fetchall():
self.forms_list.addItem(row[0])
# Load default form data
self.forms_list.setCurrentRow(0)
self.load_form_data(self.forms_list.item(0))
def load_form_data(self, item):
if not item:
return
form_name = item.text()
# Load form data
self.cursor.execute("""
SELECT id, form_name, is_default, image_path FROM pokemon_forms
WHERE pokemon_id = ? AND form_name = ?
""", (self.current_pokemon_id, form_name))
form_data = self.cursor.fetchone()
if form_data:
self.current_form_id, form_name, is_default, image_path = form_data
# Update form details
self.form_name_edit.setText(form_name)
self.is_default_checkbox.setChecked(bool(is_default))
self.image_path_edit.setText(image_path)
# Load image
pixmap = QPixmap(image_path)
self.image_label.setPixmap(pixmap.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))
# Load encounters
self.encounters_table.setRowCount(0)
self.cursor.execute("""
SELECT g.name, l.name, em.name
FROM form_encounters fe
JOIN games g ON fe.game_id = g.id
JOIN locations l ON fe.location_id = l.id
JOIN encounter_methods em ON fe.encounter_method_id = em.id
WHERE fe.form_id = ?
ORDER BY g.name, l.name
""", (self.current_form_id,))
for row in self.cursor.fetchall():
current_row = self.encounters_table.rowCount()
self.encounters_table.insertRow(current_row)
for col, value in enumerate(row):
self.encounters_table.setItem(current_row, col, QTableWidgetItem(str(value)))
def save_form_changes(self):
if not self.current_form_id:
return
new_form_name = self.form_name_edit.text()
new_is_default = self.is_default_checkbox.isChecked()
new_image_path = self.image_path_edit.text()
# Add changes to patches
patch = {
"type": "form_update",
"form_id": self.current_form_id,
"form_name": new_form_name,
"is_default": new_is_default,
"image_path": new_image_path
}
self.add_to_patches(patch)
try:
self.cursor.execute("""
UPDATE pokemon_forms
SET form_name = ?, is_default = ?, image_path = ?
WHERE id = ?
""", (new_form_name, new_is_default, new_image_path, self.current_form_id))
self.conn.commit()
QMessageBox.information(self, "Success", "Form data updated successfully!")
# Refresh the forms list and current form data
self.load_pokemon_data(self.pokemon_list.currentItem())
except sqlite3.Error as e:
QMessageBox.warning(self, "Error", f"An error occurred: {e}")
def add_new_form(self):
if not self.current_pokemon_id:
QMessageBox.warning(self, "Error", "Please select a Pokémon first.")
return
dialog = QDialog(self)
dialog.setWindowTitle("Add New Form")
layout = QFormLayout(dialog)
form_name_edit = QLineEdit()
layout.addRow("Form Name:", form_name_edit)
buttons = QHBoxLayout()
save_button = QPushButton("Save")
save_button.clicked.connect(dialog.accept)
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(dialog.reject)
buttons.addWidget(save_button)
buttons.addWidget(cancel_button)
layout.addRow(buttons)
if dialog.exec() == QDialog.DialogCode.Accepted:
form_name = form_name_edit.text()
try:
self.cursor.execute("""
INSERT INTO pokemon_forms (pokemon_id, form_name, is_default, image_path)
VALUES (?, ?, ?, ?)
""", (self.current_pokemon_id, form_name, False, f"images/pokemon/{self.current_pokemon_id:04d}_{self.current_pokemon_name}_({form_name}).png".replace(" ", "_")))
self.conn.commit()
new_form_id = self.cursor.lastrowid
# Add new form to patches
patch = {
"type": "new_form",
"form_id": new_form_id,
"pokemon_id": self.current_pokemon_id,
"form_name": form_name,
"is_default": False,
"image_path": f"images/pokemon/{self.current_pokemon_id:04d}_{self.current_pokemon_name}_({form_name}).png".replace(" ", "_")
}
self.add_to_patches(patch)
QMessageBox.information(self, "Success", "New form added successfully!")
self.load_pokemon_data(self.pokemon_list.currentItem())
except sqlite3.Error as e:
QMessageBox.warning(self, "Error", f"An error occurred: {e}")
def add_new_encounter(self):
if not self.current_form_id:
QMessageBox.warning(self, "Error", "Please select a form first.")
return
dialog = QDialog(self)
dialog.setWindowTitle("Add New Encounter")
layout = QFormLayout(dialog)
game_combo = QComboBox()
self.cursor.execute("SELECT name FROM games ORDER BY name")
games = [row[0] for row in self.cursor.fetchall()]
game_combo.addItems(games)
layout.addRow("Game:", game_combo)
location_combo = QComboBox()
self.cursor.execute("SELECT name FROM locations ORDER BY name")
locations = [row[0] for row in self.cursor.fetchall()]
location_combo.addItems(locations)
layout.addRow("Location:", location_combo)
method_combo = QComboBox()
self.cursor.execute("SELECT name FROM encounter_methods ORDER BY name")
methods = [row[0] for row in self.cursor.fetchall()]
method_combo.addItems(methods)
layout.addRow("Method:", method_combo)
buttons = QHBoxLayout()
save_button = QPushButton("Save")
save_button.clicked.connect(dialog.accept)
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(dialog.reject)
buttons.addWidget(save_button)
buttons.addWidget(cancel_button)
layout.addRow(buttons)
if dialog.exec() == QDialog.DialogCode.Accepted:
game = game_combo.currentText()
location = location_combo.currentText()
method = method_combo.currentText()
try:
self.cursor.execute("""
INSERT INTO form_encounters (form_id, game_id, location_id, encounter_method_id)
VALUES (?,
(SELECT id FROM games WHERE name = ?),
(SELECT id FROM locations WHERE name = ?),
(SELECT id FROM encounter_methods WHERE name = ?))
""", (self.current_form_id, game, location, method))
self.conn.commit()
new_encounter_id = self.cursor.lastrowid
# Add new encounter to patches
patch = {
"type": "new_encounter",
"encounter_id": new_encounter_id,
"form_id": self.current_form_id,
"game": game,
"location": location,
"method": method
}
self.add_to_patches(patch)
QMessageBox.information(self, "Success", "New encounter added successfully!")
self.load_form_data(self.forms_list.currentItem())
except sqlite3.Error as e:
QMessageBox.warning(self, "Error", f"An error occurred: {e}")
def add_to_patches(self, patch):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
self.patches[f"{timestamp}_{len(self.patches)}"] = patch
self.auto_save_patches()
def auto_save_patches(self):
patches_dir = "patches"
if not os.path.exists(patches_dir):
os.makedirs(patches_dir)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_name = f"{patches_dir}/patch_{timestamp}.json"
with open(file_name, 'w') as f:
json.dump(self.patches, f, indent=2)
print(f"Patches auto-saved to {file_name}")
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Save Changes',
"Do you want to save changes to the disk database?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No)
if reply == QMessageBox.StandardButton.Yes:
# Save changes to disk
disk_conn = sqlite3.connect('pokemon_database.db')
self.conn.backup(disk_conn)
disk_conn.close()
QMessageBox.information(self, "Success", "Changes saved to disk database.")
self.conn.close()
event.accept()
def filter_locations_list(self):
search_text = self.locations_search_box.text().lower()
for i in range(self.locations_list.count()):
item = self.locations_list.item(i)
if search_text in item.text().lower():
item.setHidden(False)
else:
item.setHidden(True)
def load_location_data(self, item):
location_name = item.text()
self.cursor.execute("SELECT name, description FROM locations WHERE name = ?", (location_name,))
location_data = self.cursor.fetchone()
if location_data:
self.location_name_edit.setText(location_data[0])
self.location_description_edit.setText(location_data[1])
def save_location_changes(self):
old_name = self.locations_list.currentItem().text()
new_name = self.location_name_edit.text()
new_description = self.location_description_edit.text()
try:
self.cursor.execute("""
UPDATE locations
SET name = ?, description = ?
WHERE name = ?
""", (new_name, new_description, old_name))
self.conn.commit()
# Add changes to patches
patch = {
"type": "location_update",
"old_name": old_name,
"new_name": new_name,
"description": new_description
}
self.add_to_patches(patch)
QMessageBox.information(self, "Success", "Location data updated successfully!")
self.load_locations_list()
except sqlite3.Error as e:
QMessageBox.warning(self, "Error", f"An error occurred: {e}")
def add_new_location(self):
dialog = QDialog(self)
dialog.setWindowTitle("Add New Location")
layout = QFormLayout(dialog)
name_edit = QLineEdit()
description_edit = QLineEdit()
layout.addRow("Name:", name_edit)
layout.addRow("description:", description_edit)
buttons = QHBoxLayout()
save_button = QPushButton("Save")
save_button.clicked.connect(dialog.accept)
cancel_button = QPushButton("Cancel")
cancel_button.clicked.connect(dialog.reject)
buttons.addWidget(save_button)
buttons.addWidget(cancel_button)
layout.addRow(buttons)
if dialog.exec() == QDialog.DialogCode.Accepted:
name = name_edit.text()
description = description_edit.text()
try:
self.cursor.execute("""
INSERT INTO locations (name, description)
VALUES (?, ?)
""", (name, description))
self.conn.commit()
# Add new location to patches
patch = {
"type": "new_location",
"name": name,
"description": description
}
self.add_to_patches(patch)
QMessageBox.information(self, "Success", "New location added successfully!")
self.load_locations_list()
except sqlite3.Error as e:
QMessageBox.warning(self, "Error", f"An error occurred: {e}")
def load_locations_list(self):
self.locations_list.clear()
self.cursor.execute("SELECT name FROM locations ORDER BY name")
locations = [row[0] for row in self.cursor.fetchall()]
self.locations_list.addItems(locations)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PokemonDatabaseApp()
window.show()
sys.exit(app.exec())