diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..6725d42
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,19 @@
+# Card Vault
+A desktop application for building and organizing MTG card libraries and decks.
+
+## Features
+
+* Online card search
+* Create a library of owned cards
+* Import and Export Libraries
+
+## TODO
+
+* Organize cards in library
+* Build decklists from cards in collection
+* Want lists
+* Full offline functionality
+
+### Maybe
+* Create fancy exports of decks and wants
+* Display prices for cards
diff --git a/bin/cardvault b/bin/cardvault
new file mode 100644
index 0000000..0e47180
--- /dev/null
+++ b/bin/cardvault
@@ -0,0 +1,2 @@
+from cardvault import application
+application.main()
\ No newline at end of file
diff --git a/cardvault/__init__.py b/cardvault/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/cardvault/application.py b/cardvault/application.py
new file mode 100644
index 0000000..3ee1e5b
--- /dev/null
+++ b/cardvault/application.py
@@ -0,0 +1,263 @@
+import sys
+try:
+ import gi
+ gi.require_version('Gtk', '3.0')
+ from gi.repository import Gtk
+ from gi.repository import Pango
+ from gi.repository import GdkPixbuf
+except ImportError as ex:
+ print("Couldn't import GTK dependencies. Make sure you "
+ "installed the PyGTK package and %s module." % ex.name)
+ sys.exit(-1)
+
+import os
+import copy
+import re
+
+from cardvault import handlers
+from cardvault import util
+from cardvault import search_funct
+from cardvault import lib_funct
+
+
+class Application:
+
+ # ---------------------------------Initialize the Application----------------------------------------------
+ def __init__(self):
+
+ # Load ui files
+ self.ui = Gtk.Builder()
+ self.ui.add_from_file(util.get_ui_filename("mainwindow.glade"))
+ self.ui.add_from_file(util.get_ui_filename("overlays.glade"))
+ self.ui.add_from_file(util.get_ui_filename("search.glade"))
+ self.ui.add_from_file(util.get_ui_filename("library.glade"))
+
+ self.current_page = None
+ self.unsaved_changes = False
+
+ not_found = self.ui.get_object("pageNotFound")
+ self.pages = {
+ "search": self.ui.get_object("searchView"),
+ "library": self.ui.get_object("libraryView"),
+ "decks": not_found
+ }
+
+ # Load configuration file
+ self.configfile = util.get_root_filename("config.json")
+ self.config = util.parse_config(self.configfile, util.default_config)
+
+ util.LOG_LEVEL = self.config["log_level"]
+
+ # Load data from cache path
+ self.image_cache = util.reload_image_cache(util.CACHE_PATH + "images/")
+ self.precon_icons = util.reload_preconstructed_icons(util.CACHE_PATH + "icons/")
+ self.mana_icons = util.load_mana_icons(os.path.dirname(__file__) + "/resources/mana/")
+
+ self.sets = util.load_sets(util.get_root_filename("sets"))
+
+ self.library = None
+ self.tags = None
+ self.load_library()
+
+ self.handlers = handlers.Handlers(self)
+ self.ui.connect_signals(self.handlers)
+
+ search_funct.init_search_view(self)
+
+ lib_funct.init_library_view(self)
+
+ self.ui.get_object("mainWindow").connect('delete-event', Gtk.main_quit)
+ self.ui.get_object("mainWindow").show_all()
+ self.push_status("Card Vault ready.")
+
+ view_menu = self.ui.get_object("viewMenu")
+ start_page = [page for page in view_menu.get_children() if page.get_name() == util.START_PAGE]
+ start_page[0].activate()
+
+ def push_status(self, msg):
+ status_bar = self.ui.get_object("statusBar")
+ status_bar.pop(0)
+ status_bar.push(0, msg)
+
+ def show_card_details(self, card):
+ builder = Gtk.Builder()
+ builder.add_from_file(util.get_ui_filename("detailswindow.glade"))
+ builder.add_from_file(util.get_ui_filename("overlays.glade"))
+ window = builder.get_object("cardDetails")
+ window.set_title(card.name)
+ # Card Image
+ container = builder.get_object("imageContainer")
+ pixbuf = self.get_card_image(card, 63 * 5, 88 * 5)
+ image = Gtk.Image().new_from_pixbuf(pixbuf)
+ container.add(image)
+ # Name
+ builder.get_object("cardName").set_text(card.name)
+ # Types
+ supertypes = ""
+ if card.subtypes is not None:
+ supertypes = " - " + " ".join(card.subtypes)
+ types = " ".join(card.types) + supertypes
+ builder.get_object("cardTypes").set_text(types)
+ # Rarity
+ builder.get_object("cardRarity").set_text(card.rarity if card.rarity else "")
+ # Release
+ builder.get_object("cardReleaseDate").set_text(card.release_date if card.release_date else "")
+ # Set
+ builder.get_object("cardSet").set_text(card.set_name)
+ # Printings
+ prints = []
+ for set in card.printings:
+ prints.append(self.sets[set].name)
+ builder.get_object("cardPrintings").set_text(", ".join(prints))
+ # Legalities
+ grid = builder.get_object("legalitiesGrid")
+ rows = 1
+ for legality in card.legalities if card.legalities else {}:
+ date_label = Gtk.Label()
+ date_label.set_halign(Gtk.Align.END)
+ text_label = Gtk.Label()
+ text_label.set_line_wrap_mode(Pango.WrapMode.WORD)
+ text_label.set_line_wrap(True)
+ text_label.set_halign(Gtk.Align.END)
+ color = self.config['legality_colors'][legality["legality"]]
+ date_label.set_markup("" + legality["format"] + ":" + "")
+ text_label.set_markup("" + legality["legality"] + "")
+ grid.attach(date_label, 0, rows + 2, 1, 1)
+ grid.attach(text_label, 1, rows + 2, 1, 1)
+
+ rows += 1
+ grid.show_all()
+
+ # Rulings
+ if card.rulings:
+ grid = builder.get_object("rulesGrid")
+ rows = 1
+ for rule in card.rulings:
+ date_label = Gtk.Label(rule["date"])
+ text_label = Gtk.Label(rule["text"])
+ text_label.set_line_wrap_mode(Pango.WrapMode.WORD)
+ text_label.set_line_wrap(True)
+ text_label.set_justify(Gtk.Justification.LEFT)
+ text_label.set_halign(Gtk.Align.START)
+
+ grid.attach(date_label, 0, rows+2, 1, 1)
+ grid.attach(text_label, 1, rows+2, 1, 1)
+
+ rows += 1
+ grid.show_all()
+ else:
+ builder.get_object("ruleBox").set_visible(False)
+
+ window.show_all()
+
+ def eval_key_pressed(widget,event):
+ key, modifier = Gtk.accelerator_parse('Escape')
+ keyval = event.keyval
+ if keyval == key:
+ window.destroy()
+
+ window.connect("key-press-event", eval_key_pressed)
+
+ def show_question_dialog(self, title, message):
+ dialog = Gtk.MessageDialog(self.ui.get_object("mainWindow"), 0, Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.YES_NO, title)
+ dialog.format_secondary_text(message)
+ response = dialog.run()
+ dialog.destroy()
+ return response
+
+ def show_message(self, title, message):
+ dialog = Gtk.MessageDialog(self.ui.get_object("mainWindow"), 0, Gtk.MessageType.INFO,
+ Gtk.ButtonsType.OK, title)
+ dialog.format_secondary_text(message)
+ dialog.run()
+ dialog.destroy()
+
+ def save_library(self):
+ # Save library file
+ util.save_file(util.get_root_filename("library"), self.library)
+ # Save tags file
+ util.save_file(util.get_root_filename("tags"), self.tags)
+ self.unsaved_changes = False
+ self.push_status("Library saved")
+
+ def load_library(self):
+ # Load library file
+ self.library = util.load_file(util.get_root_filename("library"))
+ # Load tags file
+ self.tags = util.load_file(util.get_root_filename("tags"))
+ self.push_status("Library loaded")
+
+ def get_untagged_cards(self):
+ lib = copy.copy(self.library)
+ for ids in self.tags.values():
+ for card_id in ids:
+ try:
+ del lib[card_id]
+ except KeyError:
+ pass
+ return lib
+
+ def get_tagged_cards(self, tag):
+ if not tag:
+ return self.library
+ else:
+ lib = {}
+ for card_id in self.tags[tag]:
+ lib[card_id] = self.library[card_id]
+ return lib
+
+ def tag_card(self, card, tag):
+ list = self.tags[tag]
+ list.append(card.multiverse_id)
+ self.unsaved_changes = True
+
+ def add_tag(self, tag):
+ self.tags[tag] = []
+ self.push_status("Added Tag \"" + tag + "\"")
+ self.unsaved_changes = True
+
+ def remove_tag(self, tag):
+ del self.tags[tag]
+ self.push_status("Removed Tag \"" + tag + "\"")
+ self.unsaved_changes = True
+
+ def add_card_to_lib(self, card, tag=None):
+ if tag is not None:
+ self.tag_card(card, tag)
+ self.library[card.multiverse_id] = card
+ self.push_status(card.name + " added to library")
+ self.unsaved_changes = True
+
+ def remove_card_from_lib(self, card):
+ del self.library[card.multiverse_id]
+ self.push_status(card.name + " removed from library")
+ self.unsaved_changes = True
+
+ def get_card_image(self, card, sizex, sizey):
+ # Try using file from local cache, or load online
+ try:
+ pixbuf = self.image_cache[card.multiverse_id]
+ except KeyError as err:
+ util.log("No local image for " + card.name + ". Loading from " + card.image_url, util.LogLevel.Info)
+ pixbuf = util.load_card_image_online(card, sizex, sizey)
+ self.image_cache[card.multiverse_id] = pixbuf
+ return pixbuf
+
+ def get_mana_icons(self, mana_string):
+ if not mana_string:
+ util.log("No mana string provided", util.LogLevel.Warning)
+ return
+ icon_list = re.findall("{(.*?)}", mana_string)
+ icon_name = "_".join(icon_list)
+ try:
+ icon = self.precon_icons[icon_name]
+ except KeyError:
+ icon = util.create_mana_icons(self.mana_icons, mana_string)
+ self.precon_icons[icon_name] = icon
+ return icon
+
+
+def main():
+ win = Application()
+ Gtk.main()
diff --git a/cardvault/cardlist.py b/cardvault/cardlist.py
index cac04f7..1dea2d7 100644
--- a/cardvault/cardlist.py
+++ b/cardvault/cardlist.py
@@ -1,6 +1,5 @@
import gi
-import util
-import logger
+from cardvault import util
from gi.repository import Gtk, GdkPixbuf, Gdk
import time
gi.require_version('Gtk', '3.0')
@@ -8,7 +7,7 @@ gi.require_version('Gdk', '3.0')
class CardList(Gtk.ScrolledWindow):
- def __init__(self, with_filter):
+ def __init__(self, with_filter, app):
Gtk.ScrolledWindow.__init__(self)
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
self.set_hexpand(True)
@@ -16,6 +15,7 @@ class CardList(Gtk.ScrolledWindow):
self.filtered = with_filter
self.lib = {}
+ self.app = app
# Columns are these:
# 0 Multiverse ID
@@ -136,11 +136,16 @@ class CardList(Gtk.ScrolledWindow):
if card.multiverse_id is not None:
- if util.library.__contains__(card_id) and colorize:
+ if self.app.library.__contains__(card_id) and colorize:
color = util.card_view_colors["owned"]
else:
color = util.card_view_colors["unowned"]
+ if card.type == "Land":
+ mana_cost = None
+ else:
+ mana_cost = self.app.get_mana_icons(card.mana_cost)
+
item =[
card.multiverse_id,
card.name,
@@ -150,13 +155,13 @@ class CardList(Gtk.ScrolledWindow):
card.power,
card.toughness,
", ".join(card.printings),
- util.get_mana_icons(card.mana_cost),
+ mana_cost,
card.cmc,
card.set_name,
color]
self.store.append(item)
end = time.time()
- logger.log("Time to build Table: " + str(round(end - start, 3)), logger.LogLevel.Info)
+ util.log("Time to build Table: " + str(round(end - start, 3)), util.LogLevel.Info)
if self.filtered:
self.list.set_model(self.filter_and_sort)
self.list.thaw_child_notify()
diff --git a/cardvault/config.py b/cardvault/config.py
deleted file mode 100644
index 4168eab..0000000
--- a/cardvault/config.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import gi
-import os
-gi.require_version('Gtk', '3.0')
-gi.require_version('Gdk', '3.0')
-from gi.repository import Gdk
-
-
-# Title of the Program Window
-application_title = "Card Vault v0.5"
-
-# Path of image cache
-cache_path = os.path.dirname(__file__) + "/.cache/"
-image_cache_path = os.path.dirname(__file__) + "/.cache/images/"
-icon_cache_path = os.path.dirname(__file__) + "/.cache/icons/"
-
-# Colors to use in the Application
-green_color = Gdk.color_parse('#87ff89')
-red_color = Gdk.color_parse('#ff6d6d')
-
-# When True Search view will list a card multiple times for each set they appear in
-show_from_all_sets = True
-
-start_page = "search"
-
-log_level = 3
diff --git a/cardvault/handlers.py b/cardvault/handlers.py
index e4650be..d1f7943 100644
--- a/cardvault/handlers.py
+++ b/cardvault/handlers.py
@@ -1,11 +1,14 @@
import gi
-import config
-import lib_funct
-import search_funct
-import util
+import datetime
+import os
from gi.repository import Gtk
+
gi.require_version('Gtk', '3.0')
+from cardvault import lib_funct
+from cardvault import search_funct
+from cardvault import util
+
class Handlers:
def __init__(self, app):
@@ -14,14 +17,44 @@ class Handlers:
# ----------------Main Window-----------------
def do_save_library(self, item):
- util.save_library()
+ self.app.save_library()
def do_export_library(self, item):
- util.export_library()
+ dialog = Gtk.FileChooserDialog("Export Library", self.app.ui.get_object("mainWindow"),
+ Gtk.FileChooserAction.SAVE,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+ dialog.set_current_name("mtg_export-" + datetime.datetime.now().strftime("%Y-%m-%d"))
+ dialog.set_current_folder(os.path.expanduser("~"))
+ response = dialog.run()
+
+ if response == Gtk.ResponseType.OK:
+ # prepare export file
+ file = {"library": self.app.library, "tags": self.app.tags}
+ util.export_library(dialog.get_filename, file)
+
+ dialog.destroy()
def do_import_library(self, item):
- util.import_library()
- self.app.current_page.emit('show')
+ # Show file picker dialog for import
+ dialog = Gtk.FileChooserDialog("Import Library", self.app.ui.get_object("mainWindow"),
+ Gtk.FileChooserAction.OPEN,
+ (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+ dialog.set_current_folder(os.path.expanduser("~"))
+ response = dialog.run()
+ if response == Gtk.ResponseType.OK:
+ # Show confirmation message
+ override_question = self.app.show_question_dialog("Import Library",
+ "Importing a library will override your current library. "
+ "Proceed?")
+ if override_question == Gtk.ResponseType.YES:
+ (library, tags) = util.import_library(dialog.get_filename())
+ self.app.library = library
+ self.app.tags = tags
+ # Cause current page to reload with imported data
+ self.app.current_page.emit('show')
+ dialog.destroy()
def on_view_changed(self, item):
if item.get_active():
@@ -34,22 +67,24 @@ class Handlers:
container.show_all()
self.app.current_page.emit('show')
- app_title = new_page.get_name() + " - " + config.application_title
+ app_title = new_page.get_name() + " - " + util.APPLICATION_TITLE
self.app.ui.get_object("mainWindow").set_title(app_title)
def do_delete_event(self, arg1, arg2):
- if util.unsaved_changes:
- response = util.show_question_dialog("Unsaved Changes", "You have unsaved changes in your library. "
+ if self.app.unsaved_changes:
+ response = self.app.show_question_dialog("Unsaved Changes", "You have unsaved changes in your library. "
"Save before exiting?")
if response == Gtk.ResponseType.YES:
- util.save_library()
+ self.app.save_library()
# ----------------Search-----------------
def do_search_cards(self, sender):
search_term = self.app.ui.get_object("searchEntry").get_text()
- results = search_funct.search_cards(search_term)
+ filters = search_funct.get_filters(self.app)
+
+ results = search_funct.search_cards(search_term, filters)
card_list = self.app.ui.get_object("searchResults").get_child()
card_list.update(results, colorize=True)
@@ -72,10 +107,10 @@ class Handlers:
tree_iter = model.get_iter(path)
card_id = model.get_value(tree_iter, 0)
card = card_view.lib[card_id]
- search_funct.add_to_library(card)
+ self.app.add_card_to_lib(card)
search_funct.reload_serach_view(self.app)
- #----------------Library-----------------
+ # ----------------Library-----------------
def do_reload_library(self, view):
lib_funct.reload_library(self.app)
@@ -103,11 +138,11 @@ class Handlers:
selected_cards = card_view.get_selected_cards()
tag = entry.get_text()
if tag != "":
- lib_funct.tag_cards(selected_cards, tag)
+ lib_funct.tag_cards(selected_cards, tag, self.app)
lib_funct.reload_library(self.app, tag)
entry.set_text("")
- def on_drag_data_received(self, widget, drag_context, x,y, data,info, time):
+ def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
print("drag received")
def on_tag_selected(self, selection, path, column):
@@ -119,7 +154,7 @@ class Handlers:
# Handlers for TreeViews etc. wich have been not added by Glade
- #----------------Search-----------------
+ # ----------------Search-----------------
def on_search_card_selected(self, tree, row_no, column):
(model, path_list) = tree.get_selection().get_selected_rows()
@@ -150,5 +185,3 @@ class Handlers:
card_list = self.app.ui.get_object("libraryContainer").get_child()
card = card_list.lib[card_id]
self.app.show_card_details(card)
-
-
diff --git a/cardvault/lib_funct.py b/cardvault/lib_funct.py
index 77f0047..4958aa5 100644
--- a/cardvault/lib_funct.py
+++ b/cardvault/lib_funct.py
@@ -1,5 +1,4 @@
-import cardlist
-import util
+from cardvault import cardlist
import gi
gi.require_version('Gtk', '3.0')
@@ -7,7 +6,7 @@ gi.require_version('Gtk', '3.0')
def init_library_view(app):
# Create Tree View for library
container = app.ui.get_object("libraryContainer")
- card_list = cardlist.CardList(True)
+ card_list = cardlist.CardList(True, app)
card_list.set_name("libScroller")
card_list.list.connect("row-activated", app.handlers.on_library_card_selected)
container.add(card_list)
@@ -20,10 +19,10 @@ def init_library_view(app):
def reload_library(app, tag=None):
if tag == "untagged":
- lib = util.get_untagged_cards()
+ lib = app.get_untagged_cards()
tag = None
else:
- lib = util.get_library(tag)
+ lib = app.get_tagged_cards(tag)
reload_tag_list(app, tag)
card_tree = app.ui.get_object("libraryContainer").get_child()
if lib:
@@ -35,9 +34,8 @@ def reload_library(app, tag=None):
app.ui.get_object("noResults").set_visible(True)
-
def add_new_tag(name, app):
- util.add_tag(name)
+ app.add_tag(name)
reload_tag_list(app, True)
@@ -46,17 +44,17 @@ def reload_tag_list(app, preserve=False):
(path, column) = tree.get_cursor()
store = tree.get_model()
store.clear()
- for tag, ids in util.tags.items():
+ for tag, ids in app.tags.items():
store.append([tag, tag + " (" + str(len(ids)) + ")"])
if preserve:
tree.set_cursor(path if path else 0)
-def tag_cards(card_list, tag):
+def tag_cards(card_list, tag, app):
# Check if tag exist and create if necessary
- if not util.tags.__contains__(tag):
- util.add_tag(tag)
+ if not app.tags.__contains__(tag):
+ app.add_tag(tag)
for card in card_list.values():
- if not util.tags[tag].__contains__(card.multiverse_id):
- util.tag_card(card, tag)
+ if not app.tags[tag].__contains__(card.multiverse_id):
+ app.tag_card(card, tag)
diff --git a/cardvault/logger.py b/cardvault/logger.py
deleted file mode 100644
index aaf2675..0000000
--- a/cardvault/logger.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import config
-import enum
-
-
-class LogLevel(enum.Enum):
- Error = 1
- Warning = 2
- Info = 3
-
-
-def log(message, log_level):
- if log_level.value <= config.log_level:
- level_string = "[" + log_level.name + "] "
- print(level_string + message)
diff --git a/cardvault/network.py b/cardvault/network.py
deleted file mode 100644
index ffd3cf7..0000000
--- a/cardvault/network.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from urllib import request
-from urllib.error import URLError, HTTPError
-from mtgsdk import Set
-
-
-def net_load_sets():
- try:
- sets = Set.all()
- except:
- return ""
- return sets
diff --git a/cardvault/resources/mana/BP.png b/cardvault/resources/mana/B-P.png
similarity index 100%
rename from cardvault/resources/mana/BP.png
rename to cardvault/resources/mana/B-P.png
diff --git a/cardvault/resources/mana/BR.png b/cardvault/resources/mana/B-R.png
similarity index 100%
rename from cardvault/resources/mana/BR.png
rename to cardvault/resources/mana/B-R.png
diff --git a/cardvault/resources/mana/GP.png b/cardvault/resources/mana/G-P.png
similarity index 100%
rename from cardvault/resources/mana/GP.png
rename to cardvault/resources/mana/G-P.png
diff --git a/cardvault/resources/mana/GU.png b/cardvault/resources/mana/G-U.png
similarity index 100%
rename from cardvault/resources/mana/GU.png
rename to cardvault/resources/mana/G-U.png
diff --git a/cardvault/resources/mana/GW.png b/cardvault/resources/mana/G-W.png
similarity index 100%
rename from cardvault/resources/mana/GW.png
rename to cardvault/resources/mana/G-W.png
diff --git a/cardvault/resources/mana/RG.png b/cardvault/resources/mana/R-G.png
similarity index 100%
rename from cardvault/resources/mana/RG.png
rename to cardvault/resources/mana/R-G.png
diff --git a/cardvault/resources/mana/RP.png b/cardvault/resources/mana/R-P.png
similarity index 100%
rename from cardvault/resources/mana/RP.png
rename to cardvault/resources/mana/R-P.png
diff --git a/cardvault/resources/mana/RW.png b/cardvault/resources/mana/R-W.png
similarity index 100%
rename from cardvault/resources/mana/RW.png
rename to cardvault/resources/mana/R-W.png
diff --git a/cardvault/resources/mana/UB.png b/cardvault/resources/mana/U-B.png
similarity index 100%
rename from cardvault/resources/mana/UB.png
rename to cardvault/resources/mana/U-B.png
diff --git a/cardvault/resources/mana/UP.png b/cardvault/resources/mana/U-P.png
similarity index 100%
rename from cardvault/resources/mana/UP.png
rename to cardvault/resources/mana/U-P.png
diff --git a/cardvault/resources/mana/UR.png b/cardvault/resources/mana/U-R.png
similarity index 100%
rename from cardvault/resources/mana/UR.png
rename to cardvault/resources/mana/U-R.png
diff --git a/cardvault/resources/mana/WB.png b/cardvault/resources/mana/W-B.png
similarity index 100%
rename from cardvault/resources/mana/WB.png
rename to cardvault/resources/mana/W-B.png
diff --git a/cardvault/resources/mana/WP.png b/cardvault/resources/mana/W-P.png
similarity index 100%
rename from cardvault/resources/mana/WP.png
rename to cardvault/resources/mana/W-P.png
diff --git a/cardvault/resources/mana/WU.png b/cardvault/resources/mana/W-U.png
similarity index 100%
rename from cardvault/resources/mana/WU.png
rename to cardvault/resources/mana/W-U.png
diff --git a/cardvault/search_funct.py b/cardvault/search_funct.py
index 529ed00..5605f8f 100644
--- a/cardvault/search_funct.py
+++ b/cardvault/search_funct.py
@@ -1,8 +1,6 @@
import gi
-import util
-import config
-import cardlist
-import logger
+from cardvault import util
+from cardvault import cardlist
from gi.repository import Gtk, Gdk
from mtgsdk import Card
from urllib.error import URLError, HTTPError
@@ -13,9 +11,9 @@ def init_search_view(app):
# set mana icons on filter buttons
buttons = [x for x in app.ui.get_object("manaFilterGrid").get_children()
if isinstance(x, Gtk.ToggleButton)]
- _init_mana_buttons(buttons)
+ _init_mana_buttons(app, buttons)
# set auto completion for filter entry
- _init_set_entry(app.ui.get_object("setEntry"))
+ _init_set_entry(app, app.ui.get_object("setEntry"))
# Fill rarity box
_init_combo_box(app.ui.get_object("rarityCombo"), util.rarity_dict.keys())
# Fill type box
@@ -30,15 +28,34 @@ def reload_serach_view(app):
pass
-def add_to_library(card):
- util.add_card_to_lib(card)
+def get_filters(app):
+ output = {}
+ # Mana colors
+ color_list = []
+ # Go through mana color buttons an get the active filters
+ for button in app.ui.get_object("manaFilterGrid").get_children():
+ if isinstance(button, Gtk.ToggleButton):
+ if button.get_active():
+ color_list.append(button.get_name())
+ output["mana"] = ",".join(color_list)
+ # Rarity
+ combo = app.ui.get_object("rarityCombo")
+ output["rarity"] = _get_combo_value(combo)
+ # Type
+ combo = app.ui.get_object("typeCombo")
+ output["type"] = _get_combo_value(combo)
+ # Set
+ name = app.ui.get_object("setEntry").get_text()
+ output["set"] = ""
+ for set in app.sets.values():
+ if set.name == name:
+ output["set"] = set.code
+ return output
-def search_cards(term):
- logger.log("Starting online search for '" + term + "'", logger.LogLevel.Info)
- # Load filters from UI
- filters = _get_filters(util.app)
- logger.log("Used Filters: " + str(filters), logger.LogLevel.Info)
+def search_cards(term, filters):
+ util.log("Starting online search for '" + term + "'", util.LogLevel.Info)
+ util.log("Used Filters: " + str(filters), util.LogLevel.Info)
# Load card info from internet
try:
@@ -56,9 +73,9 @@ def search_cards(term):
if len(cards) == 0:
# TODO UI show no cards found
return
- logger.log("Found " + str(len(cards)) + " cards", logger.LogLevel.Info)
+ util.log("Found " + str(len(cards)) + " cards", util.LogLevel.Info)
# Remove duplicate entries
- if config.show_from_all_sets is False:
+ if util.SHOW_FROM_ALL_SETS is False:
cards = _remove_duplicates(cards)
# Pack results in a dictionary
@@ -70,7 +87,7 @@ def search_cards(term):
def _init_results_tree(app):
overlay = app.ui.get_object("searchResults")
- card_list = cardlist.CardList(False)
+ card_list = cardlist.CardList(False, app)
card_list.set_name("resultsScroller")
card_list.list.connect("row-activated", app.handlers.on_search_card_selected)
card_list.selection.connect("changed", app.handlers.on_search_selection_changed)
@@ -91,31 +108,6 @@ def _init_combo_box(combo, list):
combo.set_active(0)
-def _get_filters(app):
- output = {}
- # Mana colors
- color_list = []
- # Go through mana color buttons an get the active filters
- for button in app.ui.get_object("manaFilterGrid").get_children():
- if isinstance(button, Gtk.ToggleButton):
- if button.get_active():
- color_list.append(button.get_name())
- output["mana"] = ",".join(color_list)
- # Rarity
- combo = app.ui.get_object("rarityCombo")
- output["rarity"] = _get_combo_value(combo)
- # Type
- combo = app.ui.get_object("typeCombo")
- output["type"] = _get_combo_value(combo)
- # Set
- name = app.ui.get_object("setEntry").get_text()
- output["set"] = ""
- for set in util.set_list:
- if set.name == name:
- output["set"] = set.code
- return output
-
-
def _remove_duplicates(cards):
unique_cards = []
unique_names = []
@@ -133,15 +125,15 @@ def _get_combo_value(combo):
return value.replace("All", "")
-def _init_mana_buttons(button_list):
+def _init_mana_buttons(app, button_list):
for button in button_list:
- image = Gtk.Image.new_from_pixbuf(util.create_mana_icons("{" + button.get_name() + "}"))
+ image = Gtk.Image.new_from_pixbuf(app.get_mana_icons("{" + button.get_name() + "}"))
button.set_image(image)
-def _init_set_entry(entry):
+def _init_set_entry(app, entry):
set_store = Gtk.ListStore(str, str)
- for set in util.set_list:
+ for set in app.sets.values():
set_store.append([set.name, set.code])
completer = Gtk.EntryCompletion()
completer.set_model(set_store)
diff --git a/cardvault/util.py b/cardvault/util.py
index 2fd2a39..5dae635 100644
--- a/cardvault/util.py
+++ b/cardvault/util.py
@@ -1,34 +1,46 @@
import os
-import datetime
import gi
import re
-import config
-import logger
-import network
+import enum
import copy
+import json
from gi.repository import GdkPixbuf, Gtk
from PIL import Image as PImage
from urllib import request
import six.moves.cPickle as pickle
gi.require_version('Gtk', '3.0')
+from mtgsdk import Set
+from mtgsdk import MtgException
-# Locally stored images for faster loading times
-imagecache = {}
-manaicons = {}
-mana_icons_preconstructed = {}
+# Title of the Program Window
+APPLICATION_TITLE = "Card Vault"
-set_list = []
-set_dict = {}
+# Program version
+VERSION = "0.5.0"
-# Card library object
-library = {}
-# Dictionary for tagged cards
-tags = {}
+# Path of image cache
+CACHE_PATH = os.path.expanduser('~') + "/.cardvault/"
+IMAGE_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/images/"
+ICON_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/icons/"
-status_bar = None
-app = None
-unsaved_changes = False
+# When True Search view will list a card multiple times for each set they appear in
+SHOW_FROM_ALL_SETS = True
+
+START_PAGE = "search"
+
+LOG_LEVEL = 1
+
+default_config = {
+ "hide_duplicates_in_search": False,
+ "start_page": "search",
+ "log_level": 3,
+ "legality_colors": {
+ "Banned": "#C65642",
+ "Restricted": "#D39F30",
+ "Legal": "#62B62F"
+ }
+}
legality_colors ={
"Banned": "#C65642",
@@ -52,264 +64,176 @@ rarity_dict = {
card_types = ["Creature", "Artifact", "Instant", "Enchantment", "Sorcery", "Land", "Planeswalker"]
-def export_library():
- dialog = Gtk.FileChooserDialog("Export Library", app.ui.get_object("mainWindow"),
- Gtk.FileChooserAction.SAVE,
- (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
- dialog.set_current_name("mtg_export-" + datetime.datetime.now().strftime("%Y-%m-%d"))
- dialog.set_current_folder(os.path.expanduser("~"))
- response = dialog.run()
- if response == Gtk.ResponseType.OK:
- # prepare export file
- export = {"library": library, "tags": tags}
- try:
- pickle.dump(export, open(dialog.get_filename(), 'wb'))
-
- app.push_status("Library exported to \"" + dialog.get_filename() + "\"")
- logger.log("Library exported to \"" + dialog.get_filename() + "\"", logger.LogLevel.Info)
- except OSError as err:
- show_message("Error", err.strerror)
- logger.log(str(err), logger.LogLevel.Error)
-
- dialog.destroy()
+class LogLevel(enum.Enum):
+ Error = 1
+ Warning = 2
+ Info = 3
-def import_library():
- dialog = Gtk.FileChooserDialog("Import Library", app.ui.get_object("mainWindow"),
- Gtk.FileChooserAction.OPEN,
- (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
- dialog.set_current_folder(os.path.expanduser("~"))
- response = dialog.run()
- if response == Gtk.ResponseType.OK:
- override_question = show_question_dialog("Import Library",
- "Importing a library will override your current library. "
- "Proceed?")
- if override_question == Gtk.ResponseType.YES:
-
- try:
- imported = pickle.load(open(dialog.get_filename(), 'rb'))
- except pickle.UnpicklingError as err:
- show_message("Error", "Imported file is invalid")
- logger.log(str(err) + " while importing", logger.LogLevel.Error)
- dialog.destroy()
- return
-
- # Check imported file
- try:
- global library
- library = imported["library"]
- global tags
- tags = imported["tags"]
- except KeyError as err:
- logger.log("Invalid library format " + str(err), logger.LogLevel.Warning)
-
- # Try fallback method
- library.clear()
- for id, card in imported.items():
- library[id] = card
-
- save_library()
- app.push_status("Library imported")
- logger.log("Library imported", logger.LogLevel.Info)
- dialog.destroy()
+def log(message, log_level):
+ if log_level.value <= LOG_LEVEL:
+ level_string = "[" + log_level.name + "] "
+ print(level_string + message)
-def save_library():
- if not os.path.exists(config.cache_path):
- os.makedirs(config.cache_path)
- lib_path = config.cache_path + "library"
- tag_path = config.cache_path + "tags"
-
- # Serialize library object using pickle
+def parse_config(filename, default):
+ config = copy.copy(default)
try:
- pickle.dump(library, open(lib_path, 'wb'))
- pickle.dump(tags, open(tag_path, 'wb'))
- except OSError as err:
- show_message("Error", err.strerror)
- logger.log(str(err), logger.LogLevel.Error)
+ with open(filename) as configfile:
+ loaded_config = json.load(configfile)
+ if 'legality_colors' in config and 'legality_colors' in loaded_config:
+ # Need to prevent nested dict from being overwritten with an incomplete dict
+ config['legality_colors'].update(loaded_config['legality_colors'])
+ loaded_config['legality_colors'] = config['legality_colors']
+ config.update(loaded_config)
+ except IOError:
+ # Will just use the default config
+ # and create the file for manual editing
+ save_config(config, filename)
+ except ValueError:
+ # There's a syntax error in the config file
+ log("Syntax error wihle parsing config file", LogLevel.Error)
return
-
- global unsaved_changes
- unsaved_changes = False
- app.push_status("Library saved.")
- logger.log("library saved", logger.LogLevel.Info)
+ return config
-def load_library():
- lib_path = config.cache_path + "library"
- library.clear()
+def save_config(config_dict, filename):
+ path = os.path.dirname(filename)
+ if not os.path.isdir(path):
+ os.mkdir(path)
- if os.path.isfile(lib_path):
- # Deserialize using pickle
+ with open(filename, 'wb') as configfile:
+ configfile.write(json.dumps(config_dict, sort_keys=True,
+ indent=4, separators=(',', ': ')).encode('utf-8'))
+
+
+def get_root_filename(filename):
+ return os.path.expanduser(os.path.join('~', '.cardvault', filename))
+
+def get_ui_filename(filename):
+ return os.path.expanduser(os.path.join(os.path.dirname(__file__), 'gui', filename))
+
+
+def reload_image_cache(path):
+ cache = {}
+ if not os.path.isdir(path):
+ os.mkdir(path)
+ imagefiles = os.listdir(path)
+ for imagefile in imagefiles:
try:
- library_loaded = pickle.load(open(lib_path, 'rb'))
- for id, card in library_loaded.items():
- library[id] = card
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(path + imagefile)
+ # Strip filename extension
+ imagename = os.path.splitext(imagefile)[0]
+ cache[imagename] = pixbuf
except OSError as err:
- show_message("Error", err.strerror)
- logger.log(str(err), logger.LogLevel.Error)
- else:
- save_library()
- logger.log("No Library file found, creating new one", logger.LogLevel.Warning)
+ log("Error loading image: " + str(err), LogLevel.Error)
+ return cache
-def load_tags():
- tag_path = config.cache_path + "tags"
- tags.clear()
- if not os.path.isfile(tag_path):
- save_library()
- logger.log("No tags file found, creating new one", logger.LogLevel.Warning)
- try:
- tags_loaded = pickle.load(open(tag_path, 'rb'))
- for tag, ids in tags_loaded.items():
- tags[tag] = ids
- except OSError as err:
- show_message("Error", err.strerror)
- logger.log(str(err), logger.LogLevel.Error)
+def reload_preconstructed_icons(path):
+ cache = {}
+ if not os.path.exists(path):
+ os.makedirs(path)
-
-def load_sets():
- path = config.cache_path + "sets"
- if not os.path.isfile(path):
- # use mtgsdk api to retrieve al list of all sets
- new_sets = network.net_load_sets()
- if new_sets == "":
- show_message("API Error", "Could not retrieve Set infos")
- return
- # Serialize the loaded data to a file
- pickle.dump(new_sets, open(path, 'wb'))
- # Deserialize set data from local file
- sets = pickle.load(open(path, 'rb'))
- # Sort the loaded sets based on the sets name
- for set in sorted(sets, key=lambda x: x.name):
- set_list.append(set)
- set_dict[set.code] = set
-
-
-def reload_image_cache():
- if not os.path.exists(config.image_cache_path):
- os.makedirs(config.image_cache_path)
-
- # return array of images
- imageslist = os.listdir(config.image_cache_path)
- imagecache.clear()
- for image in imageslist:
- try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(config.image_cache_path + image)
- imagecache[image] = pixbuf
- except OSError as err:
- print("Error loading image: " + str(err))
-
-
-def reload_preconstructed_icons():
- if not os.path.exists(config.icon_cache_path):
- os.makedirs(config.icon_cache_path)
-
- icon_list = os.listdir(config.icon_cache_path)
- mana_icons_preconstructed.clear()
- for icon in icon_list:
- list = re.findall("{(.*?)}", str(icon))
+ iconfiles = os.listdir(path)
+ for file in iconfiles:
+ # Split filename into single icon names and remove extension
+ without_ext = file.split(".")[0]
+ list = without_ext.split("_")
+ # Compute size of the finished icon
pic_width = len(list) * 105
pic_height = 105
try:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(config.icon_cache_path + icon)
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file(ICON_CACHE_PATH + file)
pixbuf = pixbuf.scale_simple(pic_width / 5, pic_height / 5, GdkPixbuf.InterpType.HYPER)
- mana_icons_preconstructed[icon] = pixbuf
+ # Set name for icon
+ iconname = "_".join(list)
+ cache[iconname] = pixbuf
except OSError as err:
- print("Error loading icon: " + str(err))
+ log("Error loading image: " + str(err), LogLevel.Error)
+ return cache
-# endregion
-
-
-def get_library(tag=None):
- if tag is None or tag == "All":
- return library
- else:
- lib = {}
- for card_id in tags[tag]:
- lib[card_id] = library[card_id]
- return lib
-
-
-def get_untagged_cards():
- lib = copy.copy(library)
- for ids in tags.values():
- for card_id in ids:
- try:
- del lib[card_id]
- except KeyError:
- pass
- return lib
-
-
-def tag_card(card, tag):
- list = tags[tag]
- list.append(card.multiverse_id)
- global unsaved_changes
- unsaved_changes = True
-
-
-def add_tag(tag):
- tags[tag] = []
- app.push_status("Added Tag \"" + tag + "\"")
- global unsaved_changes
- unsaved_changes = True
-
-
-def remove_tag(tag):
- del tags[tag]
- app.push_status("Removed Tag \"" + tag + "\"")
- global unsaved_changes
- unsaved_changes = True
-
-
-def add_card_to_lib(card, tag=None):
- if tag is not None:
- tag_card(card, tag)
- library[card.multiverse_id] = card
- app.push_status(card.name + " added to library")
- global unsaved_changes
- unsaved_changes = True
-
-
-def remove_card_from_lib(card):
- del library[card.multiverse_id]
- app.push_status(card.name + " removed from library")
- global unsaved_changes
- unsaved_changes = True
-
-
-def show_question_dialog(title, message):
- dialog = Gtk.MessageDialog(app.ui.get_object("mainWindow"), 0, Gtk.MessageType.WARNING,
- Gtk.ButtonsType.YES_NO, title)
- dialog.format_secondary_text(message)
- response = dialog.run()
- dialog.destroy()
- return response
-
-
-def show_message(title, message):
- dialog = Gtk.MessageDialog(app.ui.get_object("mainWindow"), 0, Gtk.MessageType.INFO,
- Gtk.ButtonsType.OK, title)
- dialog.format_secondary_text(message)
- dialog.run()
- dialog.destroy()
-
-
-def load_mana_icons():
- path = os.path.dirname(__file__) + "/resources/mana/"
+def load_mana_icons(path):
if not os.path.exists(path):
- print("ERROR: Directory for mana icons not found")
+ log("Directory for mana icons not found " + path, LogLevel.Error)
return
- # return array of icons
- imagelist = os.listdir(path)
- manaicons.clear()
- for image in imagelist:
- img = PImage.open(path + image)
- manaicons[os.path.splitext(image)[0]] = img
+ icons = {}
+ filenames = os.listdir(path)
+ for file in filenames:
+ img = PImage.open(path + file)
+ # Strip file extension
+ name = os.path.splitext(file)[0]
+ icons[name] = img
+ return icons
+
+
+def load_sets(filename):
+ if not os.path.isfile(filename):
+ # use mtgsdk api to retrieve al list of all sets
+ try:
+ sets = Set.all()
+ except MtgException as err:
+ log(str(err), LogLevel.Error)
+ return
+ # Serialize the loaded data to a file
+ pickle.dump(sets, open(filename, 'wb'))
+ # Deserialize set data from local file
+ sets = pickle.load(open(filename, 'rb'))
+ # Sort the loaded sets based on the sets name
+ output = {}
+ for set in sorted(sets, key=lambda x: x.name):
+ output[set.code] = set
+ return output
+
+
+def export_library(path, file):
+ try:
+ pickle.dump(file, open(path, 'wb'))
+ log("Library exported to \"" + path + "\"", LogLevel.Info)
+ except OSError as err:
+ log(str(err), LogLevel.Error)
+
+
+def import_library(path):
+ try:
+ imported = pickle.load(open(path, 'rb'))
+ except pickle.UnpicklingError as err:
+ log(str(err) + " while importing", LogLevel.Error)
+ return
+ # Parse imported file
+ try:
+ library = imported["library"]
+ tags = imported["tags"]
+ except KeyError as err:
+ log("Invalid library format " + str(err), LogLevel.Error)
+ return
+
+ log("Library imported", LogLevel.Info)
+ return (library, tags)
+
+
+def save_file(path, file):
+ if not os.path.exists(path):
+ os.makedirs(path)
+ # Serialize using cPickle
+ try:
+ pickle.dump(file, open(path, 'wb'))
+ except OSError as err:
+ log(str(err), LogLevel.Error)
+ return
+ log("Saved file " + path, LogLevel.Info)
+
+
+def load_file(path):
+ if not os.path.isfile(path):
+ log(path + " does not exist", LogLevel.Error)
+ try:
+ loaded = pickle.load(open(path, 'rb'))
+ except OSError as err:
+ log(str(err), LogLevel.Error)
+ return
+ return loaded
def load_dummy_image(sizex, sizey):
@@ -320,38 +244,17 @@ def load_dummy_image(sizex, sizey):
def load_card_image_online(card, sizex, sizey):
url = card.image_url
if url is None:
- print("No Image URL provided")
+ log("No Image URL for " + card.name, LogLevel.Warning)
return load_dummy_image(sizex, sizey)
- filename = config.image_cache_path + card.multiverse_id.__str__() + ".PNG"
+ filename = IMAGE_CACHE_PATH + str(card.multiverse_id) + ".png"
request.urlretrieve(url, filename)
- reload_image_cache()
return GdkPixbuf.Pixbuf.new_from_file_at_size(filename, sizex, sizey)
-def load_card_image(card, sizex, sizey):
- # Try loading from disk, if file exists
- filename = str(card.multiverse_id) + ".PNG"
- if imagecache.__contains__(filename):
- pixbuf = imagecache[filename]
- return pixbuf.scale_simple(sizex, sizey, GdkPixbuf.InterpType.BILINEAR)
- else:
- return load_card_image_online(card, sizex, sizey)
-
-
-def get_mana_icons(mana_string):
- if not mana_string:
- return
- try:
- icon = mana_icons_preconstructed[mana_string.replace("/", "") + ".png"]
- except KeyError:
- icon = create_mana_icons(mana_string)
- mana_icons_preconstructed[mana_string] = icon
- return icon
-
-
-def create_mana_icons(mana_string):
+def create_mana_icons(icon_dict, mana_string):
# Convert the string to a List
- list = re.findall("{(.*?)}", str(mana_string))
+ safe_string = mana_string.replace("/", "-")
+ list = re.findall("{(.*?)}", safe_string)
if len(list) == 0:
return
# Compute horizontal size for the final image
@@ -362,18 +265,22 @@ def create_mana_icons(mana_string):
# Go through all entries an add the correspondent icon to the final image
for icon in list:
xpos = poscounter * 105
- loaded = manaicons.get(icon.replace("/", ""))
- if loaded is None:
- print("ERROR: No icon file named \"" + icon + "\" found.")
- else:
- image.paste(loaded, (xpos, 0))
+ try:
+ loaded = icon_dict[icon]
+ except KeyError as err:
+ log("No icon file named '" + icon + "' found.", LogLevel.Warning)
+ return
+ image.paste(loaded, (xpos, 0))
poscounter += 1
- path = config.icon_cache_path + mana_string.replace("/", "") + ".png"
+ # Save Icon file
+ path = ICON_CACHE_PATH + "_".join(list) + ".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:
return
- mana_icons_preconstructed[mana_string.replace("/", "") + ".png"] = pixbuf
return pixbuf
+
+# endregion
+
diff --git a/cardvault/window.py b/cardvault/window.py
deleted file mode 100644
index 74f1df2..0000000
--- a/cardvault/window.py
+++ /dev/null
@@ -1,140 +0,0 @@
-import config
-import handlers
-import util
-import search_funct
-import lib_funct
-import gi
-from gi.repository import Gtk, Pango
-gi.require_version('Gtk', '3.0')
-
-
-class MainWindow:
- def __init__(self):
- self.ui = Gtk.Builder()
- self.ui.add_from_file("gui/mainwindow.glade")
- self.ui.add_from_file("gui/overlays.glade")
- self.ui.add_from_file("gui/search.glade")
- self.ui.add_from_file("gui/library.glade")
- window = self.ui.get_object("mainWindow")
- self.current_page = None
- util.app = self
- not_found = self.ui.get_object("pageNotFound")
-
- self.pages = {
- "search": self.ui.get_object("searchView"),
- "library": self.ui.get_object("libraryView"),
- "decks": not_found
- }
-
- # Load local image Data
- util.reload_image_cache()
- util.reload_preconstructed_icons()
- util.load_mana_icons()
-
- util.load_sets()
- util.load_library()
- util.load_tags()
-
- self.handlers = handlers.Handlers(self)
- self.ui.connect_signals(self.handlers)
-
- search_funct.init_search_view(self)
-
- lib_funct.init_library_view(self)
-
- window.connect('delete-event', Gtk.main_quit)
- window.show_all()
- self.push_status("Card Vault ready.")
-
- view_menu = self.ui.get_object("viewMenu")
- start_page = [page for page in view_menu.get_children() if page.get_name() == config.start_page]
- start_page[0].activate()
-
- def push_status(self, msg):
- status_bar = self.ui.get_object("statusBar")
- status_bar.pop(0)
- status_bar.push(0, msg)
-
- def show_card_details(self, card):
- builder = Gtk.Builder()
- builder.add_from_file("gui/detailswindow.glade")
- builder.add_from_file("gui/overlays.glade")
- window = builder.get_object("cardDetails")
- window.set_title(card.name)
- # Card Image
- container = builder.get_object("imageContainer")
- pixbuf = util.load_card_image(card, 63 * 5, 88 * 5)
- image = Gtk.Image().new_from_pixbuf(pixbuf)
- container.add(image)
- # Name
- builder.get_object("cardName").set_text(card.name)
- # Types
- supertypes = ""
- if card.subtypes is not None:
- supertypes = " - " + " ".join(card.subtypes)
- types = " ".join(card.types) + supertypes
- builder.get_object("cardTypes").set_text(types)
- # Rarity
- builder.get_object("cardRarity").set_text(card.rarity if card.rarity else "")
- # Release
- builder.get_object("cardReleaseDate").set_text(card.release_date if card.release_date else "")
- # Set
- builder.get_object("cardSet").set_text(card.set_name)
- # Printings
- prints = []
- for set in card.printings:
- prints.append(util.set_dict[set].name)
- builder.get_object("cardPrintings").set_text(", ".join(prints))
- # Legalities
- grid = builder.get_object("legalitiesGrid")
- rows = 1
- for legality in card.legalities if card.legalities else {}:
- date_label = Gtk.Label()
- date_label.set_halign(Gtk.Align.END)
- text_label = Gtk.Label()
- text_label.set_line_wrap_mode(Pango.WrapMode.WORD)
- text_label.set_line_wrap(True)
- text_label.set_halign(Gtk.Align.END)
- color = util.legality_colors[legality["legality"]]
- date_label.set_markup("" + legality["format"] + ":" + "")
- text_label.set_markup("" + legality["legality"] + "")
- grid.attach(date_label, 0, rows + 2, 1, 1)
- grid.attach(text_label, 1, rows + 2, 1, 1)
-
- rows += 1
- grid.show_all()
-
- # Rulings
- if card.rulings:
- grid = builder.get_object("rulesGrid")
- rows = 1
- for rule in card.rulings:
- date_label = Gtk.Label(rule["date"])
- text_label = Gtk.Label(rule["text"])
- text_label.set_line_wrap_mode(Pango.WrapMode.WORD)
- text_label.set_line_wrap(True)
- text_label.set_justify(Gtk.Justification.LEFT)
- text_label.set_halign(Gtk.Align.START)
-
- grid.attach(date_label, 0, rows+2, 1, 1)
- grid.attach(text_label, 1, rows+2, 1, 1)
-
- rows += 1
- grid.show_all()
- else:
- builder.get_object("ruleBox").set_visible(False)
-
- window.show_all()
-
- def eval_key_pressed(widget,event):
- key, modifier = Gtk.accelerator_parse('Escape')
- keyval = event.keyval
- if keyval == key:
- window.destroy()
-
- window.connect("key-press-event", eval_key_pressed)
-
-
-
-win = MainWindow()
-Gtk.main()
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..0cd4231
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,38 @@
+from setuptools import setup, find_packages
+from cardvault import util
+
+try:
+ LONG_DESCRIPTION = open("README.rst").read()
+except IOError:
+ LONG_DESCRIPTION = __doc__
+
+
+setup(
+ name=util.APPLICATION_TITLE,
+ version=util.VERSION,
+ packages=find_packages(),
+
+ # install_requires=['pygobject'],
+ package_data={'cardvault': ['resources/images/*', 'resources/mana/*', 'gui/*']},
+
+ author='luxick',
+ author_email='cardvoult@luxick.de',
+ description='Managing MTG card libraries and decks',
+ long_description=LONG_DESCRIPTION,
+ url='https://github.com/luxick/cardvault',
+ keywords='card manager, gtk, MTG, Magic the Gathering',
+ license="MIT",
+ entry_points={
+ 'gui_scripts': [
+ 'cardvault = cardvault.application:main',
+ ]
+ },
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Intended Audience :: End Users/Desktop',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'License :: OSI Approved :: MIT License',
+ ]
+ )