diff --git a/cv_engine/util.py b/cv_engine/util.py
index e2780c1..0a17f50 100644
--- a/cv_engine/util.py
+++ b/cv_engine/util.py
@@ -18,6 +18,8 @@ class EngineConfig:
db_file = 'cardvault.db'
# Default path to store temporary files
cache_path = os.path.join(os.path.expanduser('~'), '.cache', 'cardvault')
+ # Icon cache path
+ icon_cache_path = os.path.join(os.path.expanduser('~'), '.cache', 'cardvault', 'icons')
class EngineConstants:
diff --git a/cv_gtk3/card_view.py b/cv_gtk3/card_view.py
new file mode 100644
index 0000000..b9e36d6
--- /dev/null
+++ b/cv_gtk3/card_view.py
@@ -0,0 +1,96 @@
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+from cv_gtk3.gtk_util import GTKUtilities
+from cv_engine.util import MTGConstants
+
+
+class CardView(Gtk.ScrolledWindow):
+ """ Class for displaying a list of cards in an GTKTreeView """
+ def __init__(self, ui_file, filtered):
+ """ Constructor for a card list display
+ :param ui_file: Full path to an CardView glade file
+ :param filtered: Should the card list be filterable
+ """
+ self.filtered = filtered
+ self.cards = []
+ # Call constructor of superclass
+ super(CardView, self).__init__()
+ self.set_hexpand(True)
+ self.set_vexpand(True)
+ # Build UI
+ self.ui = Gtk.Builder()
+ self.ui.add_from_file(ui_file)
+ self.tree = self.ui.get_object('cardTree')
+ self.store = self.ui.get_object('cardStore')
+ self.store.set_sort_func(4, self.compare_rarity, None)
+ # Add the TreeView
+ self.add(self.tree)
+
+ def get_selected_cards(self):
+ """ Get the currently selected cards in the TreeView
+ :return: List od card objects
+ """
+ (model, path_list) = self.ui.get_object("cardTree").get_selection().get_selected_rows()
+ selected_ids = []
+ for path in path_list:
+ tree_iter = model.get_iter(path)
+ selected_ids.append(model.get_value(tree_iter, 0))
+ return [card for card in self.cards if card.multiverse_id in selected_ids]
+
+ def update(self, card_list):
+ """ Update the card view with a new list of cards
+ :param card_list:
+ """
+ self.cards = card_list
+ self.ui.get_object("cardStore").clear()
+ # Disable update if tree is filtered (performance)
+ if self.filtered:
+ self.tree.freeze_child_notify()
+ # Fill list with new cards
+ for card in card_list:
+ if card.multiverse_id is None: continue
+ # TODO load row color base on card status (owned, wanted,...)
+ color = 'black'
+ mana_cost = None
+ if not card.types.__contains__('Land'):
+ mana_cost = GTKUtilities.get_mana_icons(card.mana_cost)
+ item = [card.multiverse_id,
+ card.name,
+ ' '.join(card.supertypes or ''),
+ ' '.join(card.types or ''),
+ card.rarity,
+ card.power,
+ card.toughness,
+ ', '.join(card.printings or ''),
+ mana_cost,
+ card.cmc,
+ card.set_name,
+ color,
+ card.original_text]
+ self.store.append(item)
+ # Reactivate update for filtered trees
+ if self.filtered:
+ self.tree.thaw_child_notify()
+
+ @staticmethod
+ def compare_rarity(model, row1, row2, _):
+ """ Compare function for two the rarities of two cards
+ :param model: The tree view model
+ :param row1: The first row to compare
+ :param row2: The second row to compare
+ :param _: ignored
+ :return: Integer value based on comparison
+ """
+ sort_column = 4
+ value1 = model.get_value(row1, sort_column)
+ value2 = model.get_value(row2, sort_column)
+ if MTGConstants.rarities[value1.lower()] < MTGConstants.rarities[value2.lower()]:
+ return -1
+ elif value1 == value2:
+ return 0
+ else:
+ return 1
+
+
diff --git a/cv_gtk3/gtk_util.py b/cv_gtk3/gtk_util.py
new file mode 100644
index 0000000..fb9a847
--- /dev/null
+++ b/cv_gtk3/gtk_util.py
@@ -0,0 +1,61 @@
+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 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
+ 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 icon in glyphs:
+ x_pos = glyphs.index(icon) * 105
+ try:
+ loaded = GTKUtilities.mana_icons[icon]
+ except KeyError:
+ 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)
+ pixbuf = pixbuf.scale_simple(image.width / 5, image.height / 5, GdkPixbuf.InterpType.HYPER)
+ except Exception as err:
+ print(err)
+ return
+ return pixbuf
\ No newline at end of file
diff --git a/cv_gtk3/gtkui.py b/cv_gtk3/gtkui.py
index 5927c4a..f7cefd6 100644
--- a/cv_gtk3/gtkui.py
+++ b/cv_gtk3/gtkui.py
@@ -1,41 +1,40 @@
import gi
+import os
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from cv_engine import engine
-from cv_engine.util import Utilities
from cv_gtk3.main_window import MainWindowFunctions
from cv_gtk3.setting import GUISettings
+from cv_gtk3.signal_handlers import handlers
class CardvaultGTK(MainWindowFunctions):
- """
- Main UI class for the GTK interface
- """
+ """ Main UI class for the GTK interface """
def __init__(self):
# Start engine (without config file)
self.engine = engine.CardvaultEngine()
-
+ # Set Glade file location
+ GUISettings.glade_file_path = os.path.join(os.path.dirname(__file__), 'gui')
# Load Glade files
- glade_files = ['mainwindow.glade', 'overlays.glade', 'search.glade', 'dialogs.glade']
+ glade_files = ['mainwindow.glade', 'search.glade', 'overlays.glade']
self.ui = Gtk.Builder()
for file in glade_files:
- self.ui.add_from_file(Utilities.expand_file_path(__file__, ['gui', file]))
-
+ self.ui.add_from_file(os.path.join(GUISettings.glade_file_path, file))
# Set pages for the ui to use
GUISettings.pages = {
"search": self.ui.get_object("searchView"),
}
-
# Call constructor of superclasses
MainWindowFunctions.__init__(self, self.ui)
-
- self.ui.get_object('mainWindow').connect('delete-event', Gtk.main_quit)
+ # Create Signal handlers and connect them to the UI
+ self.handlers = handlers.Handlers(self)
+ self.ui.connect_signals(self.handlers)
+ # Initialize starting view
self.ui.get_object('mainWindow').show_all()
self.hide_initial_widgets()
-
self.switch_page('search')
if __name__ == '__main__':
diff --git a/cv_gtk3/gui/mainwindow.glade b/cv_gtk3/gui/mainwindow.glade
index 012b07d..e44902d 100644
--- a/cv_gtk3/gui/mainwindow.glade
+++ b/cv_gtk3/gui/mainwindow.glade
@@ -73,16 +73,6 @@
False
-
-
-
@@ -155,7 +145,7 @@
Search
True
True
-
+
@@ -168,7 +158,7 @@
True
True
searchViewItem
-
+
@@ -181,7 +171,7 @@
True
True
searchViewItem
-
+
@@ -200,12 +190,12 @@
True
False
-
diff --git a/cv_gtk3/gui/search.glade b/cv_gtk3/gui/search.glade
index 0a124be..8e0dfbf 100644
--- a/cv_gtk3/gui/search.glade
+++ b/cv_gtk3/gui/search.glade
@@ -366,22 +366,6 @@
1
-
-
-
- False
- True
- end
- 2
-
-
False
diff --git a/cv_gtk3/setting.py b/cv_gtk3/setting.py
index 3d897f6..fc1fafe 100644
--- a/cv_gtk3/setting.py
+++ b/cv_gtk3/setting.py
@@ -1,10 +1,10 @@
class GUISettings:
- """
- Settings for the GUI
- """
+ """ Settings for the GUI """
# Collection of all pages the UI can use
pages = {}
# Currently viewed page
current_page = ''
# Title for the GTK window
application_title = 'Cardvault'
+ # Location of Glade UI files
+ glade_file_path = ''
diff --git a/cv_gtk3/signal_handlers/handlers.py b/cv_gtk3/signal_handlers/handlers.py
index 57f7bcb..4e006c0 100644
--- a/cv_gtk3/signal_handlers/handlers.py
+++ b/cv_gtk3/signal_handlers/handlers.py
@@ -1,10 +1,14 @@
-class Handlers:
- """
- Class containing all signal handlers for the GTK GUI
- """
+from cv_gtk3.signal_handlers.menu_bar import MenuBarHandlers
+from cv_gtk3.signal_handlers.search import SearchPageHandlers
+
+
+class Handlers(MenuBarHandlers, SearchPageHandlers):
+ """ Class containing all signal handlers for the GTK GUI """
def __init__(self, app):
- """
- Initialize handler class
+ """ Initialize handler class
:param app: reference to an CardvaultGTK object
"""
self.app = app
+ # Call constructors of superclasses
+ MenuBarHandlers.__init__(self, self.app)
+ SearchPageHandlers.__init__(self, self.app)
diff --git a/cv_gtk3/signal_handlers/menu_bar.py b/cv_gtk3/signal_handlers/menu_bar.py
new file mode 100644
index 0000000..d9204b3
--- /dev/null
+++ b/cv_gtk3/signal_handlers/menu_bar.py
@@ -0,0 +1,44 @@
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+class MenuBarHandlers:
+ """
+ Class for handling signals from the menu bar
+ """
+ def __init__(self, app):
+ """
+ Constructor
+ :param app: Reference to a CardvaultGTK object
+ """
+ self.app = app
+
+ def do_save_library(self, menu_item):
+ pass
+
+ def do_export_library(self, menu_item):
+ pass
+
+ def do_import_library(self, menu_item):
+ pass
+
+ def do_delete_user_library(self, menu_item):
+ pass
+
+ def do_delete_card_data(self, menu_item):
+ pass
+
+ def do_change_view(self, menu_item):
+ pass
+
+ def do_prefs_open(self, manu_item):
+ pass
+
+ @staticmethod
+ def do_delete_event(*args):
+ """
+ Signal will be sent when app should close
+ :param args: Arguments to the delete event
+ """
+ Gtk.main_quit()
\ No newline at end of file
diff --git a/cv_gtk3/signal_handlers/search.py b/cv_gtk3/signal_handlers/search.py
new file mode 100644
index 0000000..acb3ede
--- /dev/null
+++ b/cv_gtk3/signal_handlers/search.py
@@ -0,0 +1,56 @@
+import os
+
+from cv_gtk3.card_view import CardView
+from cv_gtk3.setting import GUISettings
+
+
+class SearchPageHandlers:
+ """ Class for handling Signals from the search page """
+ def __init__(self, app):
+ """ Constructor
+ :param app: Reference to an CardvaultGTK object
+ """
+ self.app = app
+
+ # Build the card view
+ overlay = self.app.ui.get_object("searchResults")
+ card_list = CardView(ui_file=os.path.join(GUISettings.glade_file_path, 'cardtree.glade'), filtered=False)
+ card_list.set_name("resultsScroller")
+ # TODO Context menu for card view
+ # card_list.tree.connect("row-activated", self.on_search_card_selected)
+ # card_list.selection.connect("changed", self.on_search_selection_changed)
+ overlay.add(card_list)
+ overlay.add_overlay(self.app.ui.get_object("searchOverlay"))
+ overlay.show_all()
+
+ def do_search_cards(self, *args):
+ pass
+
+ @staticmethod
+ def do_clear_mana_filter(button_grid):
+ """ Reset filter buttons in mana grid """
+ for button in button_grid.get_children():
+ if hasattr(button, 'set_active'):
+ button.set_active(False)
+
+ @staticmethod
+ def do_clear_set_filter(entry, *_):
+ """ Reset set filter combo box """
+ entry.set_text('')
+
+ def do_search_clear_all_clicked(self, *_):
+ """ Rest all controls in search view """
+ self.app.ui.get_object("searchEntry").set_text("")
+ self.do_clear_mana_filter(self.app.ui.get_object("manaFilterGrid"))
+ self.app.ui.get_object("rarityCombo").set_active(0)
+ self.app.ui.get_object("typeCombo").set_active(0)
+ self.app.ui.get_object("setEntry").set_text("")
+
+ def search_tree_popup_showed(self, _):
+ pass
+
+ def do_show_card_details(self, _):
+ pass
+
+ def do_search_add_to_lib(self, _):
+ pass