Add preferences dialog.

This commit is contained in:
luxick
2017-08-11 23:01:15 +02:00
parent 81675725d2
commit ecc2045282
7 changed files with 315 additions and 58 deletions

View File

@@ -1,9 +1,12 @@
from collections import OrderedDict
import gi
import os
import copy
import re
import mtgsdk
import time
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, Pango
from typing import Type, Dict, List
@@ -15,12 +18,10 @@ from cardvault import database
class Application:
# ---------------------------------Initialize the Application----------------------------------------------
def __init__(self):
# Load configuration file
self.configfile = util.get_root_filename("config.json")
self.config = util.parse_config(self.configfile, util.default_config)
self.config = self.load_config()
util.LOG_LEVEL = self.config["log_level"]
util.log("Start using config file: '{}'".format(self.configfile), util.LogLevel.Info)
util.log("Start using config file: '{}'".format(util.get_root_filename("config.json")), util.LogLevel.Info)
self.ui = Gtk.Builder()
self.ui.add_from_file(util.get_ui_filename("mainwindow.glade"))
@@ -68,7 +69,8 @@ class Application:
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]
view = self.config["start_page"] if not self.config["start_page"] == "dynamic" else self.config["last_viewed"]
start_page = [page for page in view_menu.get_children() if page.get_name() == view]
start_page[0].activate()
util.log("Launching Card Vault version {}".format(util.VERSION), util.LogLevel.Info)
@@ -129,7 +131,7 @@ class Application:
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"]]
color = util.LEGALITY_COLORS[legality["legality"]]
date_label.set_markup("<span fgcolor=\"" + color + "\">" + legality["format"] + ":" + "</span>")
text_label.set_markup("<span fgcolor=\"" + color + "\">" + legality["legality"] + "</span>")
grid.attach(date_label, 0, rows + 2, 1, 1)
@@ -199,6 +201,40 @@ class Application:
else:
return value
def show_preferences_dialog(self):
"""Show a dialog to adjust user preferences"""
dialog = self.ui.get_object("pref_dialog") # type: Gtk.Dialog
dialog.set_transient_for(self.ui.get_object("mainWindow"))
store = Gtk.ListStore(str, str)
for page in self.pages.keys():
store.append([page.title(), page])
store.append(["Continue where you left", "dynamic"])
page_map = {"search": 0,
"library": 1,
"decks": 2,
"wants": 3,
"dynamic": 4}
self.ui.get_object("pref_start_view_combo").set_model(store)
self.ui.get_object("pref_start_view_combo").set_active(page_map[self.config["start_page"]])
self.ui.get_object("pref_show_all_check").set_active(self.config["show_all_in_search"])
result = dialog.run()
dialog.hide()
if not result == Gtk.ResponseType.OK:
return
tree_iter = self.ui.get_object("pref_start_view_combo").get_active_iter()
value = self.ui.get_object("pref_start_view_combo").get_model().get_value(tree_iter, 1)
self.config["start_page"] = value
self.config["show_all_in_search"] = self.ui.get_object("pref_show_all_check").get_active()
self.save_config()
self.config = self.load_config()
def unsaved_changes(self) -> bool:
"""Check if database is in transaction"""
return self.db.db_unsaved_changes()
@@ -206,7 +242,11 @@ class Application:
def save_config(self):
cf = util.get_root_filename("config.json")
util.save_config(self.config, cf)
util.log("Config saved to '{}'".format(cf), util.LogLevel.Info)
@staticmethod
def load_config() -> dict:
configfile = util.get_root_filename("config.json")
return util.parse_config(configfile, util.DEFAULT_CONFIG)
def save_data(self):
util.log("Saving Data to database", util.LogLevel.Info)

View File

@@ -105,8 +105,17 @@ class CardVaultDB:
def db_clear_data_card(self):
"""Delete all resource data from database"""
self.db_operation("DELETE FROM cards")
self.db_operation("DELETE FROM sets")
con = sqlite3.connect(self.db_file)
try:
with con:
con.execute("DELETE FROM cards")
con.execute("DELETE FROM sets")
except sqlite3.OperationalError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
except sqlite3.IntegrityError as err:
util.log("Database Error", util.LogLevel.Error)
util.log(str(err), util.LogLevel.Error)
def db_clear_data_user(self):
"""Delete all user data from database"""
@@ -236,7 +245,7 @@ class CardVaultDB:
# Query operations #################################################################################################
def search_by_name_filtered(self, term: str, filters: dict, list_size: int) -> dict:
def search_by_name_filtered(self, term: str, filters: dict, list_size: int) -> list:
"""Search for cards based on the cards name with filter constrains"""
filter_rarity = filters["rarity"]
filer_type = filters["type"]
@@ -268,10 +277,10 @@ class CardVaultDB:
rows = cur.fetchall()
con.close()
output = {}
output = []
for row in rows:
card = self.table_to_card_mapping(row)
output[card.multiverse_id] = card
output.append(card)
return output
def search_by_name(self, term: str) -> dict:

View File

@@ -201,6 +201,200 @@
<placeholder/>
</child>
</object>
<object class="GtkListStore" id="views_store">
<columns>
<!-- column-name view -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Last used view</col>
</row>
</data>
</object>
<object class="GtkDialog" id="pref_dialog">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Preferences</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<property name="column_spacing">5</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">General</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Start View</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="pref_start_view_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">views_store</property>
<child>
<object class="GtkCellRendererText"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Search</property>
<attributes>
<attribute name="weight" value="semibold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="pref_show_all_check">
<property name="label" translatable="yes">Show results from all sets</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Display mutliple cards with the same name from diffrents sets in search results.</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-5">button1</action-widget>
<action-widget response="-6">button2</action-widget>
</action-widgets>
<child>
<placeholder/>
</child>
</object>
<object class="GtkDialog" id="yn_dialog">
<property name="can_focus">False</property>
<property name="resizable">False</property>

View File

@@ -205,6 +205,30 @@
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="Options">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Options</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="prefs_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Preferences</property>
<property name="use_underline">True</property>
<signal name="activate" handler="prefs_open" swapped="no"/>
<accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>

View File

@@ -86,7 +86,6 @@ class Handlers(SearchHandlers, LibraryHandlers, WantsHandlers):
dialog.set_current_folder(os.path.expanduser("~"))
response = dialog.run()
if response == Gtk.ResponseType.OK:
# Show confirmation message
override_question = self.app.show_dialog_yn(
"Import Library", "Importing a library will override your current library.\nProceed?")
if override_question == Gtk.ResponseType.YES:
@@ -94,9 +93,7 @@ class Handlers(SearchHandlers, LibraryHandlers, WantsHandlers):
self.app.library = imports[0]
self.app.tags = imports[1]
self.app.wants = imports[2]
# Save imported data to database
self.app.db_override_user_data()
# Cause current page to reload with imported data
self.app.current_page.emit('show')
dialog.destroy()
@@ -123,6 +120,13 @@ class Handlers(SearchHandlers, LibraryHandlers, WantsHandlers):
util.log("Done", util.LogLevel.Info)
self.app.push_status("Local card data deleted. Switching to online mode.")
def prefs_open(self, item):
"""
Handler for open preferences menu item
Called By: prefs_item menu item
"""
self.app.show_preferences_dialog()
def on_view_changed(self, item):
if item.get_active():
container = self.app.ui.get_object("contentPage")
@@ -137,6 +141,9 @@ class Handlers(SearchHandlers, LibraryHandlers, WantsHandlers):
app_title = new_page.get_name() + " - " + util.APPLICATION_TITLE
self.app.ui.get_object("mainWindow").set_title(app_title)
self.app.config["last_viewed"] = new_page.get_name().lower()
self.app.save_config()
def do_delete_event(self, arg1, arg2):
if self.app.unsaved_changes():
response = self.app.show_dialog_ync("Unsaved Changes",

View File

@@ -197,7 +197,7 @@ class SearchHandlers:
def search_cards(self, term: str, filters: dict) -> dict:
"""Return a dict of cards based on a search term and filters"""
cards = {}
# Check if a local database can be used for searching
if self.app.config["local_db"]:
util.log("Starting local search for '" + term + "'", util.LogLevel.Info)
@@ -207,7 +207,6 @@ class SearchHandlers:
end = time.time()
util.log("Card info fetched in {}s".format(round(end - start, 3)), util.LogLevel.Info)
else:
util.log("Starting online search for '" + term + "'", util.LogLevel.Info)
util.log("Used Filters: " + str(filters), util.LogLevel.Info)
@@ -216,7 +215,7 @@ class SearchHandlers:
try:
util.log("Fetching card info ...", util.LogLevel.Info)
start = time.time()
data = Card.where(name=term) \
cards = Card.where(name=term) \
.where(colorIdentity=",".join(filters["mana"])) \
.where(types=filters["type"]) \
.where(set=filters["set"]) \
@@ -229,21 +228,19 @@ class SearchHandlers:
util.log(err, util.LogLevel.Error)
return {}
# Remove duplicate entries
if util.SHOW_FROM_ALL_SETS is False:
data = self._remove_duplicates(data)
# Pack results in a dictionary
cards = {}
for card in data:
cards[card.multiverse_id] = card
if not self.app.config["show_all_in_search"]:
cards = self._remove_duplicates(cards)
# Check if results were found
if len(cards) == 0:
# TODO UI show no cards found
util.log("No Cards found", util.LogLevel.Info)
return {}
util.log("Found " + str(len(cards)) + " cards", util.LogLevel.Info)
return cards
output = {}
for card in cards:
output[card.multiverse_id] = card
return output
# ---------------------------------Search Tree----------------------------------------------

View File

@@ -13,10 +13,10 @@ from urllib import request
from mtgsdk import Set, Card, MtgException
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GdkPixbuf, GLib
# Title of the Program Window
APPLICATION_TITLE = "Card Vault"
@@ -28,12 +28,6 @@ CACHE_PATH = os.path.expanduser('~') + "/.cardvault/"
IMAGE_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/images/"
ICON_CACHE_PATH = os.path.expanduser('~') + "/.cardvault/icons/"
# When True Search view will list a card multiple times for each set they appear in
SHOW_FROM_ALL_SETS = True
# First page to show after startup
START_PAGE = "search"
# Log level of the application
# 1 Info
# 2 Warning
@@ -67,23 +61,19 @@ GENERIC_TREE_COLORS = {
"owned": "black"
}
default_config = {
"hide_duplicates_in_search": False,
LEGALITY_COLORS = {
"Banned": "#C65642",
"Restricted": "#D39F30",
"Legal": "#62B62F"
}
DEFAULT_CONFIG = {
"last_viewed": "",
"show_all_in_search": True,
"start_page": "search",
"local_db": False,
"first_run": True,
"log_level": 3,
"legality_colors": {
"Banned": "#C65642",
"Restricted": "#D39F30",
"Legal": "#62B62F"
}
}
legality_colors = {
"Banned": "#C65642",
"Restricted": "#D39F30",
"Legal": "#62B62F"
"log_level": 3
}
card_colors = {
@@ -92,7 +82,7 @@ card_colors = {
'Black': 'B',
'Red': 'R',
'Green': 'G'
}
}
color_sort_order = {
'W': 0,
@@ -157,10 +147,6 @@ def parse_config(filename: str, default: dict):
try:
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