Add build script.
@@ -1,2 +0,0 @@
|
|||||||
from cardvault import application
|
|
||||||
application.main()
|
|
||||||
17
build.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
Package cardvault using zipapp into an executable zip archive
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import zipapp
|
||||||
|
|
||||||
|
INTERPRETER = '/usr/bin/env python3'
|
||||||
|
TARGET_FILENAME = 'cardvault'
|
||||||
|
|
||||||
|
# The bundled file should be placed into the build directory
|
||||||
|
target_path = os.path.join(os.path.dirname(__file__), 'build')
|
||||||
|
# Make sure it exists
|
||||||
|
if not os.path.isdir(target_path):
|
||||||
|
os.mkdir(target_path)
|
||||||
|
target = os.path.join(target_path, TARGET_FILENAME)
|
||||||
|
# Create archive
|
||||||
|
zipapp.create_archive(source='cardvault', target=target, interpreter=INTERPRETER)
|
||||||
@@ -1 +0,0 @@
|
|||||||
from cardvault import application
|
|
||||||
10
cardvault/__main__.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
path = os.path.realpath(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(0, os.path.dirname(path))
|
||||||
|
|
||||||
|
from cv_gtk3 import gtk_ui
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
gtk_ui.main()
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
@@ -8,8 +7,7 @@ from cv_engine.util import EngineConfig, EngineConstants, Utilities
|
|||||||
|
|
||||||
class CardvaultEngine:
|
class CardvaultEngine:
|
||||||
def __init__(self, config_file=False):
|
def __init__(self, config_file=False):
|
||||||
"""
|
""" Create a new cv_engine instance
|
||||||
Create a new cv_engine instance
|
|
||||||
:param config_file: File path of the configuration file
|
:param config_file: File path of the configuration file
|
||||||
"""
|
"""
|
||||||
if config_file:
|
if config_file:
|
||||||
@@ -18,23 +16,20 @@ class CardvaultEngine:
|
|||||||
self.database = CardvaultDB(db_file_path)
|
self.database = CardvaultDB(db_file_path)
|
||||||
|
|
||||||
def get_card(self, card_id):
|
def get_card(self, card_id):
|
||||||
"""
|
""" Load a card object from database
|
||||||
Load a card object from database
|
|
||||||
:param card_id: multiverse id of a card
|
:param card_id: multiverse id of a card
|
||||||
:return: an cv_engine.model.Card object
|
:return: an cv_engine.model.Card object
|
||||||
"""
|
"""
|
||||||
return self.database.card_load(card_id)
|
return self.database.card_load(card_id)
|
||||||
|
|
||||||
def get_library(self) -> list:
|
def get_library(self) -> list:
|
||||||
"""
|
""" Get the complete library of cards
|
||||||
Get the complete library of cards
|
|
||||||
:return: Alphabetically ordered list of all cards in library
|
:return: Alphabetically ordered list of all cards in library
|
||||||
"""
|
"""
|
||||||
return self.database.lib_get_all()
|
return self.database.lib_get_all()
|
||||||
|
|
||||||
def get_all_categories(self) -> dict:
|
def get_all_categories(self) -> dict:
|
||||||
"""
|
""" Get all categories an the cards that are contained within them
|
||||||
Get all categories an the cards that are contained within them
|
|
||||||
:return: A dict with the category names and cv_engine.models.Card objects as values
|
:return: A dict with the category names and cv_engine.models.Card objects as values
|
||||||
"""
|
"""
|
||||||
categories = self.database.category_get_all()
|
categories = self.database.category_get_all()
|
||||||
@@ -54,25 +49,5 @@ class CardvaultEngine:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# Test code
|
||||||
engine = CardvaultEngine()
|
engine = CardvaultEngine()
|
||||||
|
|
||||||
# Insert Data into Datasbase
|
|
||||||
# print("Database insert test:")
|
|
||||||
# engine.database.db_clear_data_card()
|
|
||||||
# cards = Utilities.parse_mtgjson_cards(json.load(open("/home/luxick/Downloads/AllSets-x.json")))
|
|
||||||
# engine.database.card_insert_many(cards)
|
|
||||||
|
|
||||||
# Compare JSON Data to Data in Database
|
|
||||||
# for card in Utilities.parse_mtgjson_cards(json.load(open("/home/luxick/Downloads/AllSets-x.json"))):
|
|
||||||
# if card.multiverse_id:
|
|
||||||
# print('From JSON: {}'.format(card.names))
|
|
||||||
# print('From DB: {}\n'.format(engine.database.card_load(card.multiverse_id).names))
|
|
||||||
|
|
||||||
# Search test
|
|
||||||
# term = 'fire'
|
|
||||||
# for result in engine.database.card_search_by_name(term):
|
|
||||||
# print(str(result), end='\n\n')
|
|
||||||
# Fast load test
|
|
||||||
# engine.database.card_fast_load()
|
|
||||||
# all_ids = engine.database.db_all_multiverse_ids()
|
|
||||||
# print('Loaded IDs: {}'.format(len(all_ids)))
|
|
||||||
@@ -35,14 +35,6 @@ class EngineConstants:
|
|||||||
config_path = os.path.join(os.path.expanduser('~'), '.config', 'cardvault')
|
config_path = os.path.join(os.path.expanduser('~'), '.config', 'cardvault')
|
||||||
|
|
||||||
|
|
||||||
class GTKConstants:
|
|
||||||
"""
|
|
||||||
Constants for the GTK Ui
|
|
||||||
"""
|
|
||||||
# Directory in witch glade ui files are stored
|
|
||||||
glade_files = '/gui'
|
|
||||||
|
|
||||||
|
|
||||||
class MTGConstants:
|
class MTGConstants:
|
||||||
"""
|
"""
|
||||||
This class contains constants that can be used within the whole program
|
This class contains constants that can be used within the whole program
|
||||||
@@ -98,13 +90,3 @@ class Utilities:
|
|||||||
cards.append(c)
|
cards.append(c)
|
||||||
output = output + cards
|
output = output + cards
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def expand_file_path(base_file, sub_dirs) -> str:
|
|
||||||
"""
|
|
||||||
Get absolute file path relative to another file
|
|
||||||
:param base_file: Current file from witch to expand
|
|
||||||
:param sub_dirs: List of sub directories to desired file
|
|
||||||
:return: Full file path of chosen file
|
|
||||||
"""
|
|
||||||
return os.path.join(os.path.dirname(base_file), *sub_dirs)
|
|
||||||
@@ -8,9 +8,8 @@ from cv_engine.util import MTGConstants
|
|||||||
|
|
||||||
class CardView(Gtk.ScrolledWindow):
|
class CardView(Gtk.ScrolledWindow):
|
||||||
""" Class for displaying a list of cards in an GTKTreeView """
|
""" Class for displaying a list of cards in an GTKTreeView """
|
||||||
def __init__(self, ui_file, filtered):
|
def __init__(self, filtered):
|
||||||
""" Constructor for a card list display
|
""" Constructor for a card list display
|
||||||
:param ui_file: Full path to an CardView glade file
|
|
||||||
:param filtered: Should the card list be filterable
|
:param filtered: Should the card list be filterable
|
||||||
"""
|
"""
|
||||||
self.filtered = filtered
|
self.filtered = filtered
|
||||||
@@ -21,12 +20,13 @@ class CardView(Gtk.ScrolledWindow):
|
|||||||
self.set_vexpand(True)
|
self.set_vexpand(True)
|
||||||
# Build UI
|
# Build UI
|
||||||
self.ui = Gtk.Builder()
|
self.ui = Gtk.Builder()
|
||||||
self.ui.add_from_file(ui_file)
|
self.ui.add_from_string(GTKUtilities.load_ui_resource('cardtree.glade'))
|
||||||
self.tree = self.ui.get_object('cardTree')
|
self.tree = self.ui.get_object('cardTree')
|
||||||
self.store = self.ui.get_object('cardStore')
|
self.store = self.ui.get_object('cardStore')
|
||||||
self.store.set_sort_func(4, self.compare_rarity, None)
|
self.store.set_sort_func(4, self.compare_rarity, None)
|
||||||
# Add the TreeView
|
# Add the TreeView
|
||||||
self.add(self.tree)
|
self.add(self.tree)
|
||||||
|
#self.tree.connect("row-activated", self.on_row_double_click)
|
||||||
|
|
||||||
def get_selected_cards(self):
|
def get_selected_cards(self):
|
||||||
""" Get the currently selected cards in the TreeView
|
""" Get the currently selected cards in the TreeView
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import gi
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
|
||||||
@@ -17,13 +18,11 @@ class CardvaultGTK(MainWindowFunctions):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Start engine (without config file)
|
# Start engine (without config file)
|
||||||
self.engine = engine.CardvaultEngine()
|
self.engine = engine.CardvaultEngine()
|
||||||
# Set Glade file location
|
|
||||||
GUISettings.glade_file_path = os.path.join(os.path.dirname(__file__), 'gui')
|
|
||||||
# Load Glade files
|
# Load Glade files
|
||||||
glade_files = ['mainwindow.glade', 'search.glade', 'overlays.glade']
|
glade_files = ['mainwindow.glade', 'search.glade', 'overlays.glade']
|
||||||
self.ui = Gtk.Builder()
|
self.ui = Gtk.Builder()
|
||||||
for file in glade_files:
|
for file in glade_files:
|
||||||
self.ui.add_from_file(os.path.join(GUISettings.glade_file_path, file))
|
self.ui.add_from_string(GTKUtilities.load_ui_resource(file))
|
||||||
# Set pages for the ui to use
|
# Set pages for the ui to use
|
||||||
GUISettings.pages = {
|
GUISettings.pages = {
|
||||||
"search": self.ui.get_object("searchView"),
|
"search": self.ui.get_object("searchView"),
|
||||||
@@ -34,8 +33,7 @@ class CardvaultGTK(MainWindowFunctions):
|
|||||||
if not os.path.isdir(util.EngineConfig.icon_cache_path):
|
if not os.path.isdir(util.EngineConfig.icon_cache_path):
|
||||||
os.mkdir(util.EngineConfig.icon_cache_path)
|
os.mkdir(util.EngineConfig.icon_cache_path)
|
||||||
# Load single mana icons
|
# Load single mana icons
|
||||||
GTKUtilities.mana_icons = GTKUtilities.load_icon_cache(os.path.join(os.path.dirname(__file__), 'resources',
|
GTKUtilities.mana_icons = GTKUtilities.load_icon_cache(os.path.join(GTKUtilities.resources_path, 'mana'))
|
||||||
'mana'))
|
|
||||||
# Load the the pre constructed icon cache
|
# Load the the pre constructed icon cache
|
||||||
GTKUtilities.precon_icon_cache = GTKUtilities.load_icon_cache(util.EngineConfig.icon_cache_path)
|
GTKUtilities.precon_icon_cache = GTKUtilities.load_icon_cache(util.EngineConfig.icon_cache_path)
|
||||||
# Call constructor of superclasses
|
# Call constructor of superclasses
|
||||||
@@ -48,7 +46,12 @@ class CardvaultGTK(MainWindowFunctions):
|
|||||||
self.hide_initial_widgets()
|
self.hide_initial_widgets()
|
||||||
self.switch_page('search')
|
self.switch_page('search')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
def main():
|
||||||
CardvaultGTK()
|
CardvaultGTK()
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
176
cardvault/cv_gtk3/gtk_util.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from gi.repository import GdkPixbuf
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
except ImportError as err:
|
||||||
|
print('PIL imaging library is not installed')
|
||||||
|
|
||||||
|
from cv_engine.util import EngineConfig
|
||||||
|
|
||||||
|
|
||||||
|
class GTKUtilities:
|
||||||
|
""" Access to image caches and utilities for use in the GTK application """
|
||||||
|
# Loaded mana symbols Format: {'B': GDKPixbuf, '3': GDKPixbuf}
|
||||||
|
mana_icons = {}
|
||||||
|
# Cache for combined mana cost icons
|
||||||
|
precon_icon_cache = {}
|
||||||
|
# Path of Gtk resources relative to cardvault base package
|
||||||
|
resources_path = os.path.join('cv_gtk3', 'resources')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_path_from_base_dir(*dirs):
|
||||||
|
return os.path.join(os.path.dirname(os.path.dirname(__file__)), *dirs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_mana_icons(mana_string):
|
||||||
|
""" Return the combined mana symbols for a mana string
|
||||||
|
:param mana_string: String in the format '{3}{U}{B}'
|
||||||
|
:return: GdkPixbuf containing the combined symbols
|
||||||
|
"""
|
||||||
|
if not mana_string:
|
||||||
|
return
|
||||||
|
icon_list = re.findall("{(.*?)}", mana_string.replace("/", "-"))
|
||||||
|
icon_name = "_".join(icon_list)
|
||||||
|
try:
|
||||||
|
icon = GTKUtilities.precon_icon_cache[icon_name]
|
||||||
|
except KeyError:
|
||||||
|
icon = GTKUtilities.create_mana_icons(mana_string)
|
||||||
|
GTKUtilities.precon_icon_cache[icon_name] = icon
|
||||||
|
# Scale icon for display
|
||||||
|
if icon:
|
||||||
|
icon = icon.scale_simple(icon.get_width() / 5, icon.get_height() / 5, GdkPixbuf.InterpType.HYPER)
|
||||||
|
return icon
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_mana_icons(mana_string):
|
||||||
|
# Convert the string to a List
|
||||||
|
glyphs = re.findall("{(.*?)}", mana_string)
|
||||||
|
if len(glyphs) == 0:
|
||||||
|
return
|
||||||
|
# Compute horizontal size for the final image
|
||||||
|
size = len(glyphs) * 105
|
||||||
|
image = Image.new("RGBA", (size, 105))
|
||||||
|
for index, icon in enumerate(glyphs):
|
||||||
|
x_pos = index * 105
|
||||||
|
try:
|
||||||
|
# Try loading mana icon and converting to PIL.Image for combining
|
||||||
|
loaded = GTKUtilities.pixbuf_to_image(GTKUtilities.mana_icons[icon])
|
||||||
|
except KeyError:
|
||||||
|
print('Mana icon "{}" is not loaded.'.format(icon))
|
||||||
|
return
|
||||||
|
image.paste(loaded, (x_pos, 0))
|
||||||
|
# Save pre build icon file
|
||||||
|
path = os.path.join(EngineConfig.icon_cache_path, "_".join(glyphs) + ".png")
|
||||||
|
image.save(path)
|
||||||
|
try:
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
|
||||||
|
except Exception as ex:
|
||||||
|
print(ex)
|
||||||
|
return
|
||||||
|
return pixbuf
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pixbuf_to_image(pix):
|
||||||
|
"""Convert gdkpixbuf to PIL image"""
|
||||||
|
data = pix.get_pixels()
|
||||||
|
w = pix.props.width
|
||||||
|
h = pix.props.height
|
||||||
|
stride = pix.props.rowstride
|
||||||
|
mode = "RGB"
|
||||||
|
if pix.props.has_alpha:
|
||||||
|
mode = "RGBA"
|
||||||
|
im = Image.frombytes(mode, (w, h), data, "raw", mode, stride)
|
||||||
|
|
||||||
|
return im
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_ui_resource(resource_name):
|
||||||
|
""" Load GUI resource depending on the execution mode (from a directory or from a zip file)
|
||||||
|
:param resource_name: Name of the glade file
|
||||||
|
:return: String content of the resource file
|
||||||
|
"""
|
||||||
|
if os.path.isdir(os.path.dirname(__file__)):
|
||||||
|
return GTKUtilities.load_ui_resource_file(os.path.join(GTKUtilities.resources_path, 'gui', resource_name))
|
||||||
|
else:
|
||||||
|
zip_path = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
return GTKUtilities.load_ui_resource_zip(zip_path,
|
||||||
|
os.path.join(GTKUtilities.resources_path, 'gui', resource_name))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_ui_resource_file(resource_path):
|
||||||
|
""" LOad GUI resource from file path
|
||||||
|
:param resource_path: Relative path of the resource based on the cardvault base package
|
||||||
|
:return: String content of the resource file
|
||||||
|
"""
|
||||||
|
full_path = GTKUtilities.get_path_from_base_dir(resource_path)
|
||||||
|
with open(full_path, 'r') as file:
|
||||||
|
return file.read()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_ui_resource_zip(archive_file, resource_path):
|
||||||
|
""" Load GUI resource from a zip archive (for usage in release mode)
|
||||||
|
:param archive_file: Full path of the archive file
|
||||||
|
:param resource_path: Path of the resources within the archive
|
||||||
|
:return: String representation of the file content
|
||||||
|
"""
|
||||||
|
with ZipFile(archive_file, 'r') as archive:
|
||||||
|
return archive.read(resource_path).decode('utf-8')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_icon_cache(icon_path):
|
||||||
|
""" Get a dictionary with all available mana icons
|
||||||
|
:param icon_path: Relative path of icon resource files
|
||||||
|
:return: Dict with icon names and Gdkpixbuf objects
|
||||||
|
"""
|
||||||
|
if os.path.isdir(GTKUtilities.get_path_from_base_dir(icon_path)):
|
||||||
|
return GTKUtilities.load_icon_cache_file(GTKUtilities.get_path_from_base_dir(icon_path))
|
||||||
|
else:
|
||||||
|
zip_path = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
return GTKUtilities.load_icon_cache_zip(zip_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_icon_cache_file(icon_path):
|
||||||
|
""" Load icon cache from absolute paths at file system
|
||||||
|
:param icon_path: Relative path of icon resource files
|
||||||
|
:return: Dict with icon names and Gdkpixbuf object
|
||||||
|
"""
|
||||||
|
icons = {}
|
||||||
|
files = os.listdir(icon_path)
|
||||||
|
for file in files:
|
||||||
|
try:
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(os.path.join(icon_path, file))
|
||||||
|
# Strip filename extension
|
||||||
|
icon_name = os.path.splitext(file)[0]
|
||||||
|
icons[icon_name] = pixbuf
|
||||||
|
except Exception as ex:
|
||||||
|
print('Error while loading icon file "{}"'.format(ex))
|
||||||
|
return icons
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_icon_cache_zip(zip_path):
|
||||||
|
""" Load icon cache from zipped archive
|
||||||
|
:param zip_path: Full path of the zip archive
|
||||||
|
:return: Dict with icon names and Gdkpixbuf object
|
||||||
|
"""
|
||||||
|
with ZipFile(zip_path, 'r') as archive:
|
||||||
|
icon_path = os.path.join('cv_gtk3', 'resources', 'mana')
|
||||||
|
files = [path for path in archive.namelist() if os.path.isfile(path.startswith(icon_path))]
|
||||||
|
icons = {}
|
||||||
|
for file in files:
|
||||||
|
with archive.open(file) as data:
|
||||||
|
try:
|
||||||
|
loader = GdkPixbuf.PixbufLoader()
|
||||||
|
loader.write(data.read())
|
||||||
|
pixbuf = loader.get_pixbuf()
|
||||||
|
loader.close()
|
||||||
|
# Strip filename extension
|
||||||
|
icon_name = os.path.splitext(file)[0]
|
||||||
|
icons[icon_name] = pixbuf
|
||||||
|
except Exception as ex:
|
||||||
|
print('Error while loading icon file "{0}"\n{1}'.format(file, ex))
|
||||||
|
return icons
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<property name="name">Card Vault</property>
|
<property name="name">Card Vault</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Card Vault</property>
|
<property name="title" translatable="yes">Card Vault</property>
|
||||||
<property name="default_width">900</property>
|
<property name="default_width">1200</property>
|
||||||
<property name="default_height">700</property>
|
<property name="default_height">700</property>
|
||||||
<property name="icon_name">cardvault</property>
|
<property name="icon_name">cardvault</property>
|
||||||
<signal name="delete-event" handler="do_delete_event" swapped="no"/>
|
<signal name="delete-event" handler="do_delete_event" swapped="no"/>
|
||||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
cardvault/cv_gtk3/signal_handlers/__init__.py
Normal file
@@ -1,14 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from cv_gtk3.card_view import CardView
|
from cv_gtk3.card_view import CardView
|
||||||
from cv_gtk3.setting import GUISettings
|
from cv_gtk3.gtk_util import GTKUtilities
|
||||||
|
|
||||||
from cv_gtk3.gtkui import CardvaultGTK
|
|
||||||
|
|
||||||
|
|
||||||
class SearchPageHandlers:
|
class SearchPageHandlers:
|
||||||
""" Class for handling Signals from the search page """
|
""" Class for handling Signals from the search page """
|
||||||
def __init__(self, app: 'CardvaultGTK'):
|
def __init__(self, app):
|
||||||
""" Constructor
|
""" Constructor
|
||||||
:param app: Reference to an CardvaultGTK object
|
:param app: Reference to an CardvaultGTK object
|
||||||
"""
|
"""
|
||||||
@@ -16,7 +14,7 @@ class SearchPageHandlers:
|
|||||||
|
|
||||||
# Build the card view
|
# Build the card view
|
||||||
overlay = self.app.ui.get_object("searchResults")
|
overlay = self.app.ui.get_object("searchResults")
|
||||||
self.card_list = CardView(ui_file=os.path.join(GUISettings.glade_file_path, 'cardtree.glade'), filtered=False)
|
self.card_list = CardView(filtered=False)
|
||||||
self.card_list.set_name("resultsScroller")
|
self.card_list.set_name("resultsScroller")
|
||||||
# TODO Context menu for card view
|
# TODO Context menu for card view
|
||||||
# card_list.tree.connect("row-activated", self.on_search_card_selected)
|
# card_list.tree.connect("row-activated", self.on_search_card_selected)
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=0.5
|
|
||||||
Type=Application
|
|
||||||
Name=Card Vault
|
|
||||||
Comment=Organize and plan your MTG card collection
|
|
||||||
TryExec=cardvault
|
|
||||||
Exec=cardvault
|
|
||||||
Icon=cardvault
|
|
||||||
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,95 +0,0 @@
|
|||||||
import re
|
|
||||||
import os
|
|
||||||
from gi.repository import GdkPixbuf
|
|
||||||
try:
|
|
||||||
from PIL import Image as PImage
|
|
||||||
except ImportError as err:
|
|
||||||
print('PIL imaging library is not installed')
|
|
||||||
|
|
||||||
from cv_engine.util import EngineConfig
|
|
||||||
|
|
||||||
|
|
||||||
class GTKUtilities:
|
|
||||||
""" Access to image caches and utilities for use in the GTK application """
|
|
||||||
# Loaded mana symbols Format: {'B': GDKPixbuf, '3': GDKPixbuf}
|
|
||||||
mana_icons = {}
|
|
||||||
# Cache for combined mana cost icons
|
|
||||||
precon_icon_cache = {}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_icon_cache(path):
|
|
||||||
icons = {}
|
|
||||||
if not os.path.isdir(path):
|
|
||||||
os.mkdir(path)
|
|
||||||
files = os.listdir(path)
|
|
||||||
for file in files:
|
|
||||||
try:
|
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(os.path.join(path, file))
|
|
||||||
# Strip filename extension
|
|
||||||
icon_name = os.path.splitext(file)[0]
|
|
||||||
icons[icon_name] = pixbuf
|
|
||||||
except Exception as ex:
|
|
||||||
print('Error while loading icon file "{}"'.format(ex))
|
|
||||||
return icons
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_mana_icons(mana_string):
|
|
||||||
""" Return the combined mana symbols for a mana string
|
|
||||||
:param mana_string: String in the format '{3}{U}{B}'
|
|
||||||
:return: GdkPixbuf containing the combined symbols
|
|
||||||
"""
|
|
||||||
if not mana_string:
|
|
||||||
return
|
|
||||||
icon_list = re.findall("{(.*?)}", mana_string.replace("/", "-"))
|
|
||||||
icon_name = "_".join(icon_list)
|
|
||||||
try:
|
|
||||||
icon = GTKUtilities.precon_icon_cache[icon_name]
|
|
||||||
except KeyError:
|
|
||||||
icon = GTKUtilities.create_mana_icons(mana_string)
|
|
||||||
GTKUtilities.precon_icon_cache[icon_name] = icon
|
|
||||||
# Scale icon for display
|
|
||||||
if icon:
|
|
||||||
icon = icon.scale_simple(icon.get_width() / 5, icon.get_height() / 5, GdkPixbuf.InterpType.HYPER)
|
|
||||||
return icon
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_mana_icons(mana_string):
|
|
||||||
# Convert the string to a List
|
|
||||||
glyphs = re.findall("{(.*?)}", mana_string)
|
|
||||||
if len(glyphs) == 0:
|
|
||||||
return
|
|
||||||
# Compute horizontal size for the final image
|
|
||||||
size = len(glyphs) * 105
|
|
||||||
image = PImage.new("RGBA", (size, 105))
|
|
||||||
for index, icon in enumerate(glyphs):
|
|
||||||
x_pos = index * 105
|
|
||||||
try:
|
|
||||||
# Try loading mana icon and converting to PIL.Image for combining
|
|
||||||
loaded = GTKUtilities.pixbuf_to_image(GTKUtilities.mana_icons[icon])
|
|
||||||
except KeyError:
|
|
||||||
print('Mana icon "{}" is not loaded.'.format(icon))
|
|
||||||
return
|
|
||||||
image.paste(loaded, (x_pos, 0))
|
|
||||||
# Save pre build icon file
|
|
||||||
path = os.path.join(EngineConfig.icon_cache_path, "_".join(glyphs) + ".png")
|
|
||||||
image.save(path)
|
|
||||||
try:
|
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
|
|
||||||
except Exception as err:
|
|
||||||
print(err)
|
|
||||||
return
|
|
||||||
return pixbuf
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def pixbuf_to_image(pix):
|
|
||||||
"""Convert gdkpixbuf to PIL image"""
|
|
||||||
data = pix.get_pixels()
|
|
||||||
w = pix.props.width
|
|
||||||
h = pix.props.height
|
|
||||||
stride = pix.props.rowstride
|
|
||||||
mode = "RGB"
|
|
||||||
if pix.props.has_alpha:
|
|
||||||
mode = "RGBA"
|
|
||||||
im = PImage.frombytes(mode, (w, h), data, "raw", mode, stride)
|
|
||||||
|
|
||||||
return im
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
@@ -56,8 +54,8 @@ class Application:
|
|||||||
self.wants = Dict[str, List[Type[mtgsdk.Card]]]
|
self.wants = Dict[str, List[Type[mtgsdk.Card]]]
|
||||||
self.load_user_data()
|
self.load_user_data()
|
||||||
|
|
||||||
self.ui.get_object('statusbar_icon').set_from_icon_name(
|
self.ui.get_object('statusbar_icon').set_from_icon_name(util.online_icons[self.is_online()],
|
||||||
util.online_icons[self.is_online()], Gtk.IconSize.BUTTON)
|
Gtk.IconSize.BUTTON)
|
||||||
self.ui.get_object('statusbar_icon').set_tooltip_text(util.online_tooltips[self.is_online()])
|
self.ui.get_object('statusbar_icon').set_tooltip_text(util.online_tooltips[self.is_online()])
|
||||||
|
|
||||||
self.handlers = handlers.Handlers(self)
|
self.handlers = handlers.Handlers(self)
|
||||||
@@ -95,35 +93,34 @@ class Application:
|
|||||||
builder.add_from_file(util.get_ui_filename("detailswindow.glade"))
|
builder.add_from_file(util.get_ui_filename("detailswindow.glade"))
|
||||||
builder.add_from_file(util.get_ui_filename("overlays.glade"))
|
builder.add_from_file(util.get_ui_filename("overlays.glade"))
|
||||||
window = builder.get_object("cardDetails")
|
window = builder.get_object("cardDetails")
|
||||||
window.set_title(card.name)
|
window.set_title(card.get('name'))
|
||||||
# Card Image
|
# Card Image
|
||||||
container = builder.get_object("imageContainer")
|
container = builder.get_object("imageContainer")
|
||||||
pixbuf = util.load_card_image(card, 63 * 5, 88 * 5, self.image_cache)
|
pixbuf = util.load_card_image(card, 63 * 5, 88 * 5, self.image_cache)
|
||||||
image = Gtk.Image().new_from_pixbuf(pixbuf)
|
image = Gtk.Image().new_from_pixbuf(pixbuf)
|
||||||
container.add(image)
|
container.add(image)
|
||||||
# Name
|
# Name
|
||||||
builder.get_object("cardName").set_text(card.name)
|
builder.get_object("cardName").set_text(card.get('name'))
|
||||||
# Types
|
# Types
|
||||||
supertypes = ""
|
supertypes = ""
|
||||||
if card.subtypes is not None:
|
if card.get('subtypes'):
|
||||||
supertypes = " - " + " ".join(card.subtypes)
|
supertypes = " - " + " ".join(card.get('subtypes'))
|
||||||
types = " ".join(card.types) + supertypes
|
types = " ".join(card.get('types')) + supertypes
|
||||||
builder.get_object("cardTypes").set_text(types)
|
builder.get_object("cardTypes").set_text(types)
|
||||||
# Rarity
|
# Rarity
|
||||||
builder.get_object("cardRarity").set_text(card.rarity if card.rarity else "")
|
builder.get_object("cardRarity").set_text(card.get('rarity') or "")
|
||||||
# Release
|
# Release
|
||||||
builder.get_object("cardReleaseDate").set_text(card.release_date if card.release_date else "")
|
builder.get_object("cardReleaseDate").set_text(card.get('release_date') or "")
|
||||||
# Set
|
# Set
|
||||||
builder.get_object("cardSet").set_text(card.set_name)
|
builder.get_object("cardSet").set_text(card.get('set_name'))
|
||||||
# Printings
|
# Printings
|
||||||
prints = []
|
all_sets = self.get_all_sets()
|
||||||
for set in card.printings:
|
prints = [all_sets[set_name].get('name') for set_name in card.get('printings')]
|
||||||
prints.append(self.get_all_sets()[set].name)
|
|
||||||
builder.get_object("cardPrintings").set_text(", ".join(prints))
|
builder.get_object("cardPrintings").set_text(", ".join(prints))
|
||||||
# Legalities
|
# Legalities
|
||||||
grid = builder.get_object("legalitiesGrid")
|
grid = builder.get_object("legalitiesGrid")
|
||||||
rows = 1
|
rows = 1
|
||||||
for legality in card.legalities if card.legalities else {}:
|
for legality in card.get('legalities') or {}:
|
||||||
date_label = Gtk.Label()
|
date_label = Gtk.Label()
|
||||||
date_label.set_halign(Gtk.Align.END)
|
date_label.set_halign(Gtk.Align.END)
|
||||||
text_label = Gtk.Label()
|
text_label = Gtk.Label()
|
||||||
@@ -140,9 +137,9 @@ class Application:
|
|||||||
grid.show_all()
|
grid.show_all()
|
||||||
|
|
||||||
# Rulings
|
# Rulings
|
||||||
if card.rulings:
|
if card.get('rulings'):
|
||||||
store = builder.get_object("rulesStore")
|
store = builder.get_object("rulesStore")
|
||||||
for rule in card.rulings:
|
for rule in card.get('rulings'):
|
||||||
store.append([rule["date"], rule["text"]])
|
store.append([rule["date"], rule["text"]])
|
||||||
else:
|
else:
|
||||||
builder.get_object("ruleBox").set_visible(False)
|
builder.get_object("ruleBox").set_visible(False)
|
||||||
@@ -330,7 +327,7 @@ class Application:
|
|||||||
def get_wanted_card_ids(self) -> List[str]:
|
def get_wanted_card_ids(self) -> List[str]:
|
||||||
all_ids = []
|
all_ids = []
|
||||||
for cards in self.wants.values():
|
for cards in self.wants.values():
|
||||||
next_ids = [card.multiverse_id for card in cards]
|
next_ids = [card['multiverse_id'] for card in cards]
|
||||||
all_ids = list(set(all_ids) | set(next_ids))
|
all_ids = list(set(all_ids) | set(next_ids))
|
||||||
return all_ids
|
return all_ids
|
||||||
|
|
||||||
@@ -442,10 +439,7 @@ class Application:
|
|||||||
|
|
||||||
def get_all_sets(self) -> dict:
|
def get_all_sets(self) -> dict:
|
||||||
if not self.is_online():
|
if not self.is_online():
|
||||||
l = self.db.set_get_all()
|
out = {s['code']: s for s in self.db.set_get_all()}
|
||||||
out = {}
|
|
||||||
for s in l:
|
|
||||||
out[s.code] = s
|
|
||||||
else:
|
else:
|
||||||
out = util.load_sets(util.get_root_filename('sets'))
|
out = util.load_sets(util.get_root_filename('sets'))
|
||||||
return out
|
return out
|
||||||
@@ -6,7 +6,6 @@ from cardvault import application
|
|||||||
from gi.repository import Gtk, GdkPixbuf, Gdk
|
from gi.repository import Gtk, GdkPixbuf, Gdk
|
||||||
|
|
||||||
from typing import Dict, Type
|
from typing import Dict, Type
|
||||||
from mtgsdk import Card
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
@@ -14,7 +13,7 @@ gi.require_version('Gdk', '3.0')
|
|||||||
|
|
||||||
|
|
||||||
class CardList(Gtk.ScrolledWindow):
|
class CardList(Gtk.ScrolledWindow):
|
||||||
def __init__(self, filtered, app: 'application.Application', row_colors: Dict[str, str]):
|
def __init__(self, filtered, app, row_colors: Dict[str, str]):
|
||||||
Gtk.ScrolledWindow.__init__(self)
|
Gtk.ScrolledWindow.__init__(self)
|
||||||
self.set_hexpand(True)
|
self.set_hexpand(True)
|
||||||
self.set_vexpand(True)
|
self.set_vexpand(True)
|
||||||
@@ -49,7 +48,7 @@ class CardList(Gtk.ScrolledWindow):
|
|||||||
output[card_id] = card
|
output[card_id] = card
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def update(self, library: Dict[str, Type[Card]]):
|
def update(self, library: Dict[str, dict]):
|
||||||
self.store.clear()
|
self.store.clear()
|
||||||
if library is None:
|
if library is None:
|
||||||
return
|
return
|
||||||
@@ -63,22 +62,24 @@ class CardList(Gtk.ScrolledWindow):
|
|||||||
all_wants = self.app.get_wanted_card_ids()
|
all_wants = self.app.get_wanted_card_ids()
|
||||||
|
|
||||||
for card in library.values():
|
for card in library.values():
|
||||||
if card.multiverse_id is not None:
|
if card['multiverse_id'] is not None:
|
||||||
color = self.get_row_color(card, self.app.library, all_wants, self.row_colors)
|
color = self.get_row_color(card, self.app.library, all_wants, self.row_colors)
|
||||||
mana_cost = None if card.types.__contains__("Land") else self.app.get_mana_icons(card.mana_cost)
|
mana_cost = None
|
||||||
item = [card.multiverse_id,
|
if not card.get('types').__contains__("Land"):
|
||||||
card.name,
|
mana_cost = self.app.get_mana_icons(card.get('mana_cost'))
|
||||||
" ".join(card.supertypes if card.supertypes else ""),
|
item = [card['multiverse_id'],
|
||||||
" ".join(card.types),
|
card['name'],
|
||||||
card.rarity,
|
" ".join(card.get('supertypes') or ""),
|
||||||
card.power,
|
" ".join(card.get('types') or ""),
|
||||||
card.toughness,
|
card.get('rarity'),
|
||||||
", ".join(card.printings),
|
card.get('power'),
|
||||||
|
card.get('toughness'),
|
||||||
|
", ".join(card.get('printings') or ""),
|
||||||
mana_cost,
|
mana_cost,
|
||||||
card.cmc,
|
card.get('cmc'),
|
||||||
card.set_name,
|
card.get('set_name'),
|
||||||
color,
|
color,
|
||||||
card.original_text]
|
card.get('original_text')]
|
||||||
self.store.append(item)
|
self.store.append(item)
|
||||||
end = time.time()
|
end = time.time()
|
||||||
util.log("Time to build Table: " + str(round(end - start, 3)) + "s", util.LogLevel.Info)
|
util.log("Time to build Table: " + str(round(end - start, 3)) + "s", util.LogLevel.Info)
|
||||||
@@ -103,9 +104,9 @@ class CardList(Gtk.ScrolledWindow):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_row_color(card, lib: dict, wants: dict, colors: dict) -> str:
|
def get_row_color(card, lib: dict, wants: dict, colors: dict) -> str:
|
||||||
if lib.__contains__(card.multiverse_id):
|
if lib.__contains__(card.get('multiverse_id')):
|
||||||
return colors["owned"]
|
return colors["owned"]
|
||||||
elif wants.__contains__(card.multiverse_id):
|
elif wants.__contains__(card.get('multiverse_id')):
|
||||||
return colors["wanted"]
|
return colors["wanted"]
|
||||||
else:
|
else:
|
||||||
return colors["unowned"]
|
return colors["unowned"]
|
||||||