2
from gi.repository import Gtk
3
from gi.repository import GdkPixbuf
4
from gi.repository import GObject
8
from softwarecenter.utils import get_icon_from_theme, size_to_str
9
from softwarecenter.backend import get_install_backend
10
from softwarecenter.backend.transactionswatcher import get_transactions_watcher
12
from gettext import gettext as _
15
class PendingStore(Gtk.ListStore):
24
COL_CANCEL) = range(7)
27
column_types = (str, # COL_TID
28
GdkPixbuf.Pixbuf, # COL_ICON
36
PENDING_STORE_ICON_CANCEL = Gtk.STOCK_CANCEL
37
PENDING_STORE_ICON_NO_CANCEL = "" # Gtk.STOCK_YES
41
def __init__(self, icons):
42
# icon, status, progress
43
Gtk.ListStore.__init__(self)
44
self.set_column_types(self.column_types)
46
self._transactions_watcher = get_transactions_watcher()
47
self._transactions_watcher.connect("lowlevel-transactions-changed",
48
self._on_lowlevel_transactions_changed)
51
# the apt-daemon stuff
52
self.backend = get_install_backend()
54
# let the pulse helper run
55
GObject.timeout_add(500, self._pulse_purchase_helper)
58
super(PendingStore, self).clear()
59
for sig in self._signals:
60
GObject.source_remove(sig)
64
def _on_lowlevel_transactions_changed(self, watcher, current_tid, pending_tids):
65
logging.debug("on_transaction_changed %s (%s)" % (current_tid, len(pending_tids)))
67
for tid in [current_tid] + pending_tids:
70
# we do this synchronous (it used to be a reply_handler)
71
# otherwise we run into a race that
72
# when we get two on_transaction_changed closely after each
73
# other clear() is run before the "_append_transaction" handler
74
# is run and we end up with two (or more) _append_transactions
75
trans = self._transactions_watcher.get_transaction(tid)
77
self._append_transaction(trans)
78
# add pending purchases as pseudo transactions
79
for pkgname in self.backend.pending_purchases:
80
iconname = self.backend.pending_purchases[pkgname].iconname
81
icon = get_icon_from_theme(self.icons, iconname=iconname, iconsize=self.ICON_SIZE)
82
appname = self.backend.pending_purchases[pkgname].appname
83
status_text = self._render_status_text(
84
appname or pkgname, _(u'Installing purchase\u2026'))
85
self.append([pkgname, icon, pkgname, status_text, float(0), 1, None])
87
def _pulse_purchase_helper(self):
89
if item[self.COL_PULSE] > 0:
90
self[-1][self.COL_PULSE] += 1
93
def _append_transaction(self, trans):
94
"""Extract information about the transaction and append it to the
97
logging.debug("_append_transaction %s (%s)" % (trans.tid, trans))
100
"progress-details-changed", self._on_progress_details_changed))
101
self._signals.append(
102
trans.connect("progress-changed", self._on_progress_changed))
103
self._signals.append(
104
trans.connect("status-changed", self._on_status_changed))
105
self._signals.append(
107
"cancellable-changed",self._on_cancellable_changed))
109
if "sc_appname" in trans.meta_data:
110
appname = trans.meta_data["sc_appname"]
111
elif "sc_pkgname" in trans.meta_data:
112
appname = trans.meta_data["sc_pkgname"]
114
#FIXME: Extract information from packages property
115
appname = trans.get_role_description()
116
self._signals.append(
117
trans.connect("role-changed", self._on_role_changed))
119
iconname = trans.meta_data["sc_iconname"]
121
icon = get_icon_from_theme(self.icons, iconsize=self.ICON_SIZE)
123
icon = get_icon_from_theme(self.icons, iconname=iconname, iconsize=self.ICON_SIZE)
124
if trans.is_waiting():
125
status = trans.status_details
127
status = trans.get_status_description()
128
status_text = self._render_status_text(appname, status)
129
cancel_icon = self._get_cancel_icon(trans.cancellable)
130
self.append([trans.tid, icon, appname, status_text, float(trans.progress),
133
def _on_cancellable_changed(self, trans, cancellable):
134
#print "_on_allow_cancel: ", trans, allow_cancel
136
if row[self.COL_TID] == trans.tid:
137
row[self.COL_CANCEL] = self._get_cancel_icon(cancellable)
139
def _get_cancel_icon(self, cancellable):
141
return self.PENDING_STORE_ICON_CANCEL
143
return self.PENDING_STORE_ICON_NO_CANCEL
145
def _on_role_changed(self, trans, role):
146
#print "_on_progress_changed: ", trans, role
148
if row[self.COL_TID] == trans.tid:
149
row[self.COL_NAME] = trans.get_role_description(role) or ""
151
def _on_progress_details_changed(self, trans, current_items, total_items,
152
current_bytes, total_bytes, current_cps,
154
#print "_on_progress_details_changed: ", trans, progress
156
if row[self.COL_TID] == trans.tid:
157
if trans.is_downloading():
158
name = row[self.COL_NAME]
159
current_bytes_str = size_to_str(current_bytes)
160
total_bytes_str = size_to_str(total_bytes)
161
status = _("Downloaded %sB of %sB") % \
162
(current_bytes_str, total_bytes_str)
163
row[self.COL_STATUS] = self._render_status_text(name, status)
165
def _on_progress_changed(self, trans, progress):
166
# print "_on_progress_changed: ", trans, progress
168
if row[self.COL_TID] == trans.tid:
170
row[self.COL_PROGRESS] = float(progress)
172
def _on_status_changed(self, trans, status):
173
#print "_on_progress_changed: ", trans, status
175
if row[self.COL_TID] == trans.tid:
176
# FIXME: the spaces around %s are poor mans padding because
177
# setting xpad on the cell-renderer seems to not work
178
name = row[self.COL_NAME]
179
if trans.is_waiting():
180
st = trans.status_details
182
st = trans.get_status_description(status)
183
row[self.COL_STATUS] = self._render_status_text(name, st)
185
def _render_status_text(self, name, status):
188
if type(name) == str:
191
name = name.encode('utf8')
192
return "%s\n<small>%s</small>" % (name, status)