4735: Check for updates in a separate thread

svn: r22926
This commit is contained in:
Nick Hall 2013-08-28 23:34:51 +00:00
parent f9540a469f
commit 94d0a21d08
6 changed files with 351 additions and 266 deletions

View File

@ -31,11 +31,20 @@ General utility functions useful for the generic plugin system
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
import sys import sys
import os import os
import datetime
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
from StringIO import StringIO from StringIO import StringIO
else: else:
from io import StringIO, BytesIO from io import StringIO, BytesIO
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
LOG = logging.getLogger(".gen.plug")
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Gramps modules # Gramps modules
@ -44,7 +53,10 @@ else:
from ._pluginreg import make_environment from ._pluginreg import make_environment
from ..const import USER_PLUGINS from ..const import USER_PLUGINS
from ...version import VERSION_TUPLE from ...version import VERSION_TUPLE
from . import BasePluginManager
from ..utils.file import get_unicode_path_from_file_chooser from ..utils.file import get_unicode_path_from_file_chooser
from ..utils.configmanager import safe_eval
from ..config import config
from ..const import GRAMPS_LOCALE as glocale from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext _ = glocale.translation.gettext
@ -166,6 +178,90 @@ class Zipfile(object):
""" """
return os.path.split(name)[1] return os.path.split(name)[1]
def available_updates():
whattypes = config.get('behavior.check-for-update-types')
if sys.version_info[0] < 3:
from urllib2 import urlopen
else:
from urllib.request import urlopen
LOG.debug("Checking for updated addons...")
langs = glocale.get_language_list()
langs.append("en")
# now we have a list of languages to try:
fp = None
for lang in langs:
URL = ("%s/listings/addons-%s.txt" %
(config.get("behavior.addons-url"), lang))
LOG.debug(" trying: %s" % URL)
try:
fp = urlopen(URL, timeout=10) # abort after 10 seconds
except:
try:
URL = ("%s/listings/addons-%s.txt" %
(config.get("behavior.addons-url"), lang[:2]))
fp = urlopen(URL, timeout=10)
except Exception as err: # some error
LOG.warn("Failed to open %s: %s" % (lang, err))
fp = None
if fp and fp.getcode() == 200: # ok
break
pmgr = BasePluginManager.get_instance()
addon_update_list = []
if fp and fp.getcode() == 200:
lines = list(fp.readlines())
count = 0
for line in lines:
line = line.decode('utf-8')
try:
plugin_dict = safe_eval(line)
if type(plugin_dict) != type({}):
raise TypeError("Line with addon metadata is not "
"a dictionary")
except:
LOG.warning("Skipped a line in the addon listing: " +
str(line))
continue
id = plugin_dict["i"]
plugin = pmgr.get_plugin(id)
if plugin:
LOG.debug("Comparing %s > %s" %
(version_str_to_tup(plugin_dict["v"], 3),
version_str_to_tup(plugin.version, 3)))
if (version_str_to_tup(plugin_dict["v"], 3) >
version_str_to_tup(plugin.version, 3)):
LOG.debug(" Downloading '%s'..." % plugin_dict["z"])
if "update" in whattypes:
if (not config.get('behavior.do-not-show-previously-seen-updates') or
plugin_dict["i"] not in config.get('behavior.previously-seen-updates')):
addon_update_list.append((_("Updated"),
"%s/download/%s" %
(config.get("behavior.addons-url"),
plugin_dict["z"]),
plugin_dict))
else:
LOG.debug(" '%s' is ok" % plugin_dict["n"])
else:
LOG.debug(" '%s' is not installed" % plugin_dict["n"])
if "new" in whattypes:
if (not config.get('behavior.do-not-show-previously-seen-updates') or
plugin_dict["i"] not in config.get('behavior.previously-seen-updates')):
addon_update_list.append((_("New"),
"%s/download/%s" %
(config.get("behavior.addons-url"),
plugin_dict["z"]),
plugin_dict))
config.set("behavior.last-check-for-updates",
datetime.date.today().strftime("%Y/%m/%d"))
count += 1
if fp:
fp.close()
else:
LOG.debug("Checking Addons Failed")
LOG.debug("Done checking!")
return addon_update_list
def load_addon_file(path, callback=None): def load_addon_file(path, callback=None):
""" """
Load an addon from a particular path (from URL or file system). Load an addon from a particular path (from URL or file system).

View File

@ -68,6 +68,9 @@ from .managedwindow import ManagedWindow
from .widgets import MarkupLabel, BasicLabel from .widgets import MarkupLabel, BasicLabel
from .dialog import ErrorDialog, QuestionDialog2, OkDialog from .dialog import ErrorDialog, QuestionDialog2, OkDialog
from .glade import Glade from .glade import Glade
from gramps.gen.plug.utils import available_updates
from .plug import PluginWindows
from gramps.gen.errors import WindowActiveError
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -1308,12 +1311,39 @@ class GrampsPreferences(ConfigureDialog):
table.attach(checkbutton, 0, 3, current_line, current_line+1, yoptions=0) table.attach(checkbutton, 0, 3, current_line, current_line+1, yoptions=0)
button = Gtk.Button(_("Check now")) button = Gtk.Button(_("Check now"))
button.connect("clicked", lambda obj: \ button.connect("clicked", self.check_for_updates)
self.uistate.viewmanager.check_for_updates(force=True))
table.attach(button, 3, 4, current_line, current_line+1, yoptions=0) table.attach(button, 3, 4, current_line, current_line+1, yoptions=0)
return _('General'), table return _('General'), table
def check_for_updates(self, button):
try:
addon_update_list = available_updates()
except:
OkDialog(_("Checking Addons Failed"),
_("The addon repository appears to be unavailable. "
"Please try again later."),
self.window)
return
if len(addon_update_list) > 0:
try:
PluginWindows.UpdateAddons(self.uistate, [], addon_update_list)
except WindowActiveError:
pass
else:
check_types = config.get('behavior.check-for-update-types')
OkDialog(_("There are no available addons of this type"),
_("Checked for '%s'") %
_("' and '").join([_(t) for t in check_types]),
self.window)
# List of translated strings used here
# Dead code for l10n
_('new'), _('update')
self.uistate.viewmanager.do_reg_plugins(self.dbstate, self.uistate)
def add_famtree_panel(self, configdialog): def add_famtree_panel(self, configdialog):
table = Gtk.Table(2, 2) table = Gtk.Table(2, 2)
table.set_border_width(12) table.set_border_width(12)

View File

@ -373,6 +373,7 @@ class DisplayState(Callback):
'filter-name-changed' : (str, UNITYPE, UNITYPE), 'filter-name-changed' : (str, UNITYPE, UNITYPE),
'nameformat-changed' : None, 'nameformat-changed' : None,
'grampletbar-close-changed' : None, 'grampletbar-close-changed' : None,
'update-available' : (list, ),
} }
#nav_type to message #nav_type to message

View File

@ -34,6 +34,14 @@ import traceback
import os import os
import sys import sys
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
LOG = logging.getLogger(".gui.plug")
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# GTK modules # GTK modules
@ -58,11 +66,15 @@ from ..utils import open_file_with_default_application
from ..pluginmanager import GuiPluginManager from ..pluginmanager import GuiPluginManager
from . import tool from . import tool
from ._guioptions import add_gui_options from ._guioptions import add_gui_options
from ..dialog import InfoDialog from ..dialog import InfoDialog, OkDialog
from ..editors import EditPerson from ..editors import EditPerson
from ..glade import Glade
from ..listmodel import ListModel, NOSORT, TOGGLE
from gramps.gen.utils.file import get_unicode_path_from_file_chooser from gramps.gen.utils.file import get_unicode_path_from_file_chooser
from gramps.gen.const import URL_WIKISTRING, USER_HOME, WIKI_EXTRAPLUGINS_RAWDATA from gramps.gen.const import URL_WIKISTRING, USER_HOME, WIKI_EXTRAPLUGINS_RAWDATA
from gramps.gen.config import config from gramps.gen.config import config
from ..widgets.progressdialog import (LongOpStatus, ProgressMonitor,
GtkProgressDialog)
def display_message(message): def display_message(message):
""" """
@ -1050,3 +1062,175 @@ class ToolManagedWindow(tool.Tool, ToolManagedWindowBase):
tool.Tool.__init__(self, dbstate, options_class, name) tool.Tool.__init__(self, dbstate, options_class, name)
ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class, ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class,
name, callback) name, callback)
#-------------------------------------------------------------------------
#
# UpdateAddons
#
#-------------------------------------------------------------------------
class UpdateAddons(ManagedWindow):
def __init__(self, uistate, track, addon_update_list):
self.title = _('Available Gramps Updates for Addons')
ManagedWindow.__init__(self, uistate, track, self.__class__)
glade = Glade("updateaddons.glade")
self.update_dialog = glade.toplevel
self.set_window(self.update_dialog, glade.get_object('title'),
self.title)
self.window.set_size_request(750, 400)
apply_button = glade.get_object('apply')
cancel_button = glade.get_object('cancel')
select_all = glade.get_object('select_all')
select_all.connect("clicked", self.select_all_clicked)
select_none = glade.get_object('select_none')
select_none.connect("clicked", self.select_none_clicked)
apply_button.connect("clicked", self.install_addons)
cancel_button.connect("clicked", self.close)
self.list = ListModel(glade.get_object("list"), [
# name, click?, width, toggle
{"name": _('Select'),
"width": 60,
"type": TOGGLE,
"visible_col": 6,
"editable": True}, # 0 selected?
(_('Type'), 1, 180), # 1 new gramplet
(_('Name'), 2, 200), # 2 name (version)
(_('Description'), 3, 200), # 3 description
('', NOSORT, 0), # 4 url
('', NOSORT, 0), # 5 id
{"name": '', "type": TOGGLE}, # 6 visible? bool
], list_mode="tree")
pos = None
addon_update_list.sort(key=lambda x: "%s %s" % (x[0], x[2]["t"]))
last_category = None
for (status,plugin_url,plugin_dict) in addon_update_list:
count = get_count(addon_update_list, plugin_dict["t"])
category = _("%(adjective)s: %(addon)s") % {
"adjective": status,
"addon": _(plugin_dict["t"])}
if last_category != category:
last_category = category
node = self.list.add([False, # initially selected?
category,
"",
"",
"",
"",
False]) # checkbox visible?
iter = self.list.add([False, # initially selected?
"%s %s" % (status, _(plugin_dict["t"])),
"%s (%s)" % (plugin_dict["n"],
plugin_dict["v"]),
plugin_dict["d"],
plugin_url,
plugin_dict["i"],
True], node=node)
if pos is None:
pos = iter
if pos:
self.list.selection.select_iter(pos)
self.update_dialog.run()
def build_menu_names(self, obj):
return (self.title, "")
def select_all_clicked(self, widget):
"""
Select all of the addons for download.
"""
self.list.model.foreach(update_rows, True)
self.list.tree.expand_all()
def select_none_clicked(self, widget):
"""
Select none of the addons for download.
"""
self.list.model.foreach(update_rows, False)
self.list.tree.expand_all()
def install_addons(self, obj):
"""
Process all of the selected addons.
"""
self.update_dialog.hide()
model = self.list.model
iter = model.get_iter_first()
length = 0
while iter:
iter = model.iter_next(iter)
if iter:
length += model.iter_n_children(iter)
longop = LongOpStatus(
_("Downloading and installing selected addons..."),
length, 1, # total, increment-by
can_cancel=True)
pm = ProgressMonitor(GtkProgressDialog,
("Title", self.window, Gtk.DialogFlags.MODAL))
pm.add_op(longop)
count = 0
if not config.get('behavior.do-not-show-previously-seen-updates'):
# reset list
config.get('behavior.previously-seen-updates')[:] = []
iter = model.get_iter_first()
while iter:
for rowcnt in range(model.iter_n_children(iter)):
child = model.iter_nth_child(iter, rowcnt)
row = [model.get_value(child, n) for n in range(6)]
if longop.should_cancel():
break
elif row[0]: # toggle on
load_addon_file(row[4], callback=LOG.debug)
count += 1
else: # add to list of previously seen, but not installed
if row[5] not in config.get('behavior.previously-seen-updates'):
config.get('behavior.previously-seen-updates').append(row[5])
longop.heartbeat()
pm._get_dlg()._process_events()
iter = model.iter_next(iter)
if not longop.was_cancelled():
longop.end()
if count:
OkDialog(_("Done downloading and installing addons"),
"%s %s" % (glocale.translation.ngettext("%d addon was installed.",
"%d addons were installed.",
count) % count,
_("You need to restart Gramps to see new views.")),
self.window)
else:
OkDialog(_("Done downloading and installing addons"),
_("No addons were installed."),
self.window)
self.close()
#-------------------------------------------------------------------------
#
# Local Functions
#
#-------------------------------------------------------------------------
def update_rows(model, path, iter, user_data):
"""
Update the rows of a model.
"""
#path: (8,) iter: <GtkTreeIter at 0xbfa89fa0>
#path: (8, 0) iter: <GtkTreeIter at 0xbfa89f60>
if len(path.get_indices()) == 2:
row = model[path]
row[0] = user_data
model.row_changed(path, iter)
def get_count(addon_update_list, category):
"""
Get the count of matching category items.
"""
count = 0
for (status,plugin_url,plugin_dict) in addon_update_list:
if plugin_dict["t"] == category and plugin_url:
count += 1
return count

View File

@ -34,6 +34,7 @@ from __future__ import print_function, division
import os import os
import sys import sys
import subprocess import subprocess
import threading
from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext _ = glocale.translation.gettext
# gtk is not included here, because this file is currently imported # gtk is not included here, because this file is currently imported
@ -46,6 +47,7 @@ _ = glocale.translation.gettext
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gi.repository import PangoCairo from gi.repository import PangoCairo
from gi.repository import GLib
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -55,6 +57,7 @@ from gi.repository import PangoCairo
from gramps.gen.lib.person import Person from gramps.gen.lib.person import Person
from gramps.gen.constfunc import has_display, is_quartz, mac, win from gramps.gen.constfunc import has_display, is_quartz, mac, win
from gramps.gen.config import config from gramps.gen.config import config
from gramps.gen.plug.utils import available_updates
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -506,3 +509,22 @@ def hex_to_color(hex):
from gi.repository import Gdk from gi.repository import Gdk
color = Gdk.color_parse(hex) color = Gdk.color_parse(hex)
return color return color
#-------------------------------------------------------------------------
#
# AvailableUpdates
#
#-------------------------------------------------------------------------
class AvailableUpdates(threading.Thread):
def __init__(self, uistate):
threading.Thread.__init__(self)
self.uistate = uistate
self.addon_update_list = []
def emit_update_available(self):
self.uistate.emit('update-available', (self.addon_update_list, ))
def run(self):
self.addon_update_list = available_updates()
if len(self.addon_update_list) > 0:
GLib.idle_add(self.emit_update_available)

View File

@ -78,7 +78,7 @@ from gramps.gen.plug import REPORT
from gramps.gen.plug.report._constants import standalone_categories from gramps.gen.plug.report._constants import standalone_categories
from .plug import (PluginWindows, ReportPluginDialog, ToolPluginDialog) from .plug import (PluginWindows, ReportPluginDialog, ToolPluginDialog)
from .plug.report import report, BookSelector from .plug.report import report, BookSelector
from gramps.gen.plug.utils import version_str_to_tup, load_addon_file from .utils import AvailableUpdates
from .pluginmanager import GuiPluginManager from .pluginmanager import GuiPluginManager
from gramps.gen.relationship import get_relationship_calculator from gramps.gen.relationship import get_relationship_calculator
from .displaystate import DisplayState, RecentDocsMenu from .displaystate import DisplayState, RecentDocsMenu
@ -88,22 +88,19 @@ from gramps.gen.const import (HOME_DIR, ICON, URL_BUGTRACKER, URL_HOMEPAGE,
from gramps.gen.constfunc import is_quartz from gramps.gen.constfunc import is_quartz
from gramps.gen.config import config from gramps.gen.config import config
from gramps.gen.errors import WindowActiveError from gramps.gen.errors import WindowActiveError
from .dialog import (ErrorDialog, WarningDialog, QuestionDialog2, from .dialog import ErrorDialog, WarningDialog, QuestionDialog2, InfoDialog
InfoDialog)
from .widgets import Statusbar from .widgets import Statusbar
from .undohistory import UndoHistory from .undohistory import UndoHistory
from gramps.gen.utils.file import (media_path_full, get_unicode_path_from_env_var, from gramps.gen.utils.file import (media_path_full, get_unicode_path_from_env_var,
get_unicode_path_from_file_chooser) get_unicode_path_from_file_chooser)
from .dbloader import DbLoader from .dbloader import DbLoader
from .display import display_help, display_url from .display import display_help, display_url
from .widgets.progressdialog import ProgressMonitor, GtkProgressDialog
from .configure import GrampsPreferences from .configure import GrampsPreferences
from gramps.gen.db.backup import backup from gramps.gen.db.backup import backup
from gramps.gen.db.exceptions import DbException from gramps.gen.db.exceptions import DbException
from .aboutdialog import GrampsAboutDialog from .aboutdialog import GrampsAboutDialog
from .navigator import Navigator from .navigator import Navigator
from .views.tags import Tags from .views.tags import Tags
from gramps.gen.utils.configmanager import safe_eval
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -239,32 +236,6 @@ WIKI_HELP_PAGE_FAQ = '%s_-_FAQ' % URL_MANUAL_PAGE
WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % URL_MANUAL_PAGE WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % URL_MANUAL_PAGE
WIKI_HELP_PAGE_MAN = '%s' % URL_MANUAL_PAGE WIKI_HELP_PAGE_MAN = '%s' % URL_MANUAL_PAGE
#-------------------------------------------------------------------------
#
# Local Functions
#
#-------------------------------------------------------------------------
def update_rows(model, path, iter, user_data):
"""
Update the rows of a model.
"""
#path: (8,) iter: <GtkTreeIter at 0xbfa89fa0>
#path: (8, 0) iter: <GtkTreeIter at 0xbfa89f60>
if len(path.get_indices()) == 2:
row = model[path]
row[0] = user_data
model.row_changed(path, iter)
def get_count(addon_update_list, category):
"""
Get the count of matching category items.
"""
count = 0
for (status,plugin_url,plugin_dict) in addon_update_list:
if plugin_dict["t"] == category and plugin_url:
count += 1
return count
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# ViewManager # ViewManager
@ -343,17 +314,16 @@ class ViewManager(CLIManager):
self.rel_class = get_relationship_calculator() self.rel_class = get_relationship_calculator()
self.uistate.set_relationship_class() self.uistate.set_relationship_class()
# Need to call after plugins have been registered # Need to call after plugins have been registered
self.uistate.connect('update-available', self.process_updates)
self.check_for_updates() self.check_for_updates()
def check_for_updates(self, force=False): def check_for_updates(self):
""" """
Check for add-on updates.
""" """
howoften = config.get("behavior.check-for-updates") howoften = config.get("behavior.check-for-updates")
whattypes = config.get('behavior.check-for-update-types')
update = False update = False
if force: if howoften != 0: # update never if zero
update = True
elif howoften != 0: # update never if zero
y,m,d = list(map(int, y,m,d = list(map(int,
config.get("behavior.last-check-for-updates").split("/"))) config.get("behavior.last-check-for-updates").split("/")))
days = (datetime.date.today() - datetime.date(y, m, d)).days days = (datetime.date.today() - datetime.date(y, m, d)).days
@ -365,238 +335,20 @@ class ViewManager(CLIManager):
update = True update = True
elif howoften == 4: # always elif howoften == 4: # always
update = True update = True
if update: if update:
if sys.version_info[0] < 3: AvailableUpdates(self.uistate).start()
from urllib2 import urlopen
else:
from urllib.request import urlopen
LOG.debug("Checking for updated addons...")
langs = glocale.get_language_list()
langs.append("en")
# now we have a list of languages to try:
fp = None
for lang in langs:
URL = ("%s/listings/addons-%s.txt" %
(config.get("behavior.addons-url"), lang))
LOG.debug(" trying: %s" % URL)
try:
fp = urlopen(URL, timeout=10) # abort after 10 seconds
except:
try:
URL = ("%s/listings/addons-%s.txt" %
(config.get("behavior.addons-url"), lang[:2]))
fp = urlopen(URL, timeout=10)
except Exception as err: # some error
LOG.warn("Failed to open %s: %s" % (lang, err))
fp = None
if fp and fp.getcode() == 200: # ok
break
addon_update_list = []
if fp and fp.getcode() == 200:
lines = list(fp.readlines())
count = 0
for line in lines:
line = line.decode('utf-8')
try:
plugin_dict = safe_eval(line)
if type(plugin_dict) != type({}):
raise TypeError("Line with addon metadata is not "
"a dictionary")
except:
LOG.warning("Skipped a line in the addon listing: " +
str(line))
continue
id = plugin_dict["i"]
plugin = self._pmgr.get_plugin(id)
if plugin:
LOG.debug("Comparing %s > %s" %
(version_str_to_tup(plugin_dict["v"], 3),
version_str_to_tup(plugin.version, 3)))
if (version_str_to_tup(plugin_dict["v"], 3) >
version_str_to_tup(plugin.version, 3)):
LOG.debug(" Downloading '%s'..." % plugin_dict["z"])
if "update" in whattypes:
if (not config.get('behavior.do-not-show-previously-seen-updates') or
plugin_dict["i"] not in config.get('behavior.previously-seen-updates')):
addon_update_list.append((_("Updated"),
"%s/download/%s" %
(config.get("behavior.addons-url"),
plugin_dict["z"]),
plugin_dict))
else:
LOG.debug(" '%s' is ok" % plugin_dict["n"])
else:
LOG.debug(" '%s' is not installed" % plugin_dict["n"])
if "new" in whattypes:
if (not config.get('behavior.do-not-show-previously-seen-updates') or
plugin_dict["i"] not in config.get('behavior.previously-seen-updates')):
addon_update_list.append((_("New"),
"%s/download/%s" %
(config.get("behavior.addons-url"),
plugin_dict["z"]),
plugin_dict))
config.set("behavior.last-check-for-updates",
datetime.date.today().strftime("%Y/%m/%d"))
count += 1
if fp:
fp.close()
else:
from .dialog import OkDialog
OkDialog(_("Checking Addons Failed"),
_("The addon repository appears to be unavailable. Please try again later."),
self.window)
if fp:
fp.close()
return
LOG.debug("Done checking!")
# List of translated strings used here
# Dead code for l10n
_('new'), _('update')
if addon_update_list:
self.update_addons(addon_update_list)
elif force:
from .dialog import OkDialog
OkDialog(_("There are no available addons of this type"),
_("Checked for '%s'") %
_("' and '").join([_(t) for t in config.get('behavior.check-for-update-types')]),
self.window)
def update_addons(self, addon_update_list): def process_updates(self, addon_update_list):
from .glade import Glade
from .managedwindow import set_titles
from .listmodel import ListModel, NOSORT, TOGGLE
glade = Glade("updateaddons.glade")
self.update_dialog = glade.toplevel
set_titles(self.update_dialog, glade.get_object('title'),
_('Available Gramps Updates for Addons'))
apply_button = glade.get_object('apply')
cancel_button = glade.get_object('cancel')
select_all = glade.get_object('select_all')
select_all.connect("clicked", self.select_all_clicked)
select_none = glade.get_object('select_none')
select_none.connect("clicked", self.select_none_clicked)
apply_button.connect("clicked", self.install_addons)
cancel_button.connect("clicked",
lambda obj: self.update_dialog.destroy())
self.list = ListModel(glade.get_object("list"), [
# name, click?, width, toggle
{"name": _('Select'),
"width": 60,
"type": TOGGLE,
"visible_col": 6,
"editable": True}, # 0 selected?
(_('Type'), 1, 180), # 1 new gramplet
(_('Name'), 2, 200), # 2 name (version)
(_('Description'), 3, 200), # 3 description
('', NOSORT, 0), # 4 url
('', NOSORT, 0), # 5 id
{"name": '', "type": TOGGLE}, # 6 visible? bool
], list_mode="tree")
pos = None
addon_update_list.sort(key=lambda x: "%s %s" % (x[0], x[2]["t"]))
last_category = None
for (status,plugin_url,plugin_dict) in addon_update_list:
count = get_count(addon_update_list, plugin_dict["t"])
category = _("%(adjective)s: %(addon)s") % {
"adjective": status,
"addon": _(plugin_dict["t"])}
if last_category != category:
last_category = category
node = self.list.add([False, # initially selected?
category,
"",
"",
"",
"",
False]) # checkbox visible?
iter = self.list.add([False, # initially selected?
"%s %s" % (status, _(plugin_dict["t"])),
"%s (%s)" % (plugin_dict["n"],
plugin_dict["v"]),
plugin_dict["d"],
plugin_url,
plugin_dict["i"],
True], node=node)
if pos is None:
pos = iter
if pos:
self.list.selection.select_iter(pos)
self.update_dialog.run()
def select_all_clicked(self, widget):
""" """
Select all of the addons for download. Called when add-on updates are available.
""" """
self.list.model.foreach(update_rows, True) try:
self.list.tree.expand_all() PluginWindows.UpdateAddons(self.uistate, [], addon_update_list)
except WindowActiveError:
pass
def select_none_clicked(self, widget): self.do_reg_plugins(self.dbstate, self.uistate)
"""
Select none of the addons for download.
"""
self.list.model.foreach(update_rows, False)
self.list.tree.expand_all()
def install_addons(self, obj):
"""
Process all of the selected addons.
"""
from .dialog import OkDialog
from .widgets.progressdialog import LongOpStatus
self.update_dialog.hide()
model = self.list.model
iter = model.get_iter_first()
length = 0
while iter:
iter = model.iter_next(iter)
if iter:
length += model.iter_n_children(iter)
longop = LongOpStatus(
_("Downloading and installing selected addons..."),
length, 1, # total, increment-by
can_cancel=True)
pm = ProgressMonitor(GtkProgressDialog,
("Title", self.window, Gtk.DialogFlags.MODAL))
pm.add_op(longop)
count = 0
if not config.get('behavior.do-not-show-previously-seen-updates'):
# reset list
config.get('behavior.previously-seen-updates')[:] = []
iter = model.get_iter_first()
while iter:
for rowcnt in range(model.iter_n_children(iter)):
child = model.iter_nth_child(iter, rowcnt)
row = [model.get_value(child, n) for n in range(6)]
if longop.should_cancel():
break
elif row[0]: # toggle on
load_addon_file(row[4], callback=LOG.debug)
count += 1
else: # add to list of previously seen, but not installed
if row[5] not in config.get('behavior.previously-seen-updates'):
config.get('behavior.previously-seen-updates').append(row[5])
longop.heartbeat()
pm._get_dlg()._process_events()
iter = model.iter_next(iter)
if not longop.was_cancelled():
longop.end()
if count:
self.do_reg_plugins(self.dbstate, self.uistate)
OkDialog(_("Done downloading and installing addons"),
"%s %s" % (glocale.translation.ngettext("%d addon was installed.",
"%d addons were installed.",
count) % count,
_("You need to restart Gramps to see new views.")),
self.window)
else:
OkDialog(_("Done downloading and installing addons"),
_("No addons were installed."),
self.window)
self.update_dialog.destroy()
def _errordialog(self, title, errormessage): def _errordialog(self, title, errormessage):
""" """