~mvo/software-center/qml

« back to all changes in this revision

Viewing changes to softwarecenter/ui/gtk3/models/pendingstore.py

  • Committer: Michael Vogt
  • Date: 2011-10-05 13:08:09 UTC
  • mfrom: (1887.1.603 software-center)
  • Revision ID: michael.vogt@ubuntu.com-20111005130809-0tin9nr00f0uw65b
mergedĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
from gi.repository import Gtk
 
3
from gi.repository import GdkPixbuf
 
4
from gi.repository import GObject
 
5
 
 
6
import logging
 
7
 
 
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
 
11
 
 
12
from gettext import gettext as _
 
13
 
 
14
 
 
15
class PendingStore(Gtk.ListStore):
 
16
 
 
17
    # column names
 
18
    (COL_TID,
 
19
     COL_ICON, 
 
20
     COL_NAME, 
 
21
     COL_STATUS, 
 
22
     COL_PROGRESS,
 
23
     COL_PULSE,
 
24
     COL_CANCEL) = range(7)
 
25
 
 
26
    # column types
 
27
    column_types = (str,             # COL_TID
 
28
                    GdkPixbuf.Pixbuf,  # COL_ICON
 
29
                    str,             # COL_NAME
 
30
                    str,             # COL_STATUS
 
31
                    float,           # COL_PROGRESS
 
32
                    int,            # COL_PULSE
 
33
                    str)             # COL_CANCEL
 
34
 
 
35
    # icons
 
36
    PENDING_STORE_ICON_CANCEL = Gtk.STOCK_CANCEL
 
37
    PENDING_STORE_ICON_NO_CANCEL = "" # Gtk.STOCK_YES
 
38
 
 
39
    ICON_SIZE = 24
 
40
 
 
41
    def __init__(self, icons):
 
42
        # icon, status, progress
 
43
        Gtk.ListStore.__init__(self)
 
44
        self.set_column_types(self.column_types)
 
45
 
 
46
        self._transactions_watcher = get_transactions_watcher()
 
47
        self._transactions_watcher.connect("lowlevel-transactions-changed",
 
48
                                           self._on_lowlevel_transactions_changed)
 
49
        # data
 
50
        self.icons = icons
 
51
        # the apt-daemon stuff
 
52
        self.backend = get_install_backend()
 
53
        self._signals = []
 
54
        # let the pulse helper run
 
55
        GObject.timeout_add(500, self._pulse_purchase_helper)
 
56
 
 
57
    def clear(self):
 
58
        super(PendingStore, self).clear()
 
59
        for sig in self._signals:
 
60
            GObject.source_remove(sig)
 
61
            del sig
 
62
        self._signals = []
 
63
 
 
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)))
 
66
        self.clear()
 
67
        for tid in [current_tid] + pending_tids:
 
68
            if not tid:
 
69
                continue
 
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)
 
76
            if trans:
 
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])
 
86
 
 
87
    def _pulse_purchase_helper(self):
 
88
        for item in self:
 
89
            if item[self.COL_PULSE] > 0:
 
90
                self[-1][self.COL_PULSE] += 1
 
91
        return True
 
92
 
 
93
    def _append_transaction(self, trans):
 
94
        """Extract information about the transaction and append it to the
 
95
        store.
 
96
        """
 
97
        logging.debug("_append_transaction %s (%s)" % (trans.tid, trans))
 
98
        self._signals.append(
 
99
            trans.connect(
 
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(
 
106
            trans.connect(
 
107
                "cancellable-changed",self._on_cancellable_changed))
 
108
 
 
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"]
 
113
        else:
 
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))
 
118
        try:
 
119
            iconname = trans.meta_data["sc_iconname"]
 
120
        except KeyError:
 
121
            icon = get_icon_from_theme(self.icons, iconsize=self.ICON_SIZE)
 
122
        else:
 
123
            icon = get_icon_from_theme(self.icons, iconname=iconname, iconsize=self.ICON_SIZE)
 
124
        if trans.is_waiting():
 
125
            status = trans.status_details
 
126
        else:
 
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),
 
131
                     -1, cancel_icon])
 
132
 
 
133
    def _on_cancellable_changed(self, trans, cancellable):
 
134
        #print "_on_allow_cancel: ", trans, allow_cancel
 
135
        for row in self:
 
136
            if row[self.COL_TID] == trans.tid:
 
137
                row[self.COL_CANCEL] = self._get_cancel_icon(cancellable)
 
138
 
 
139
    def _get_cancel_icon(self, cancellable):
 
140
        if cancellable:
 
141
            return self.PENDING_STORE_ICON_CANCEL
 
142
        else:
 
143
            return self.PENDING_STORE_ICON_NO_CANCEL
 
144
 
 
145
    def _on_role_changed(self, trans, role):
 
146
        #print "_on_progress_changed: ", trans, role
 
147
        for row in self:
 
148
            if row[self.COL_TID] == trans.tid:
 
149
                row[self.COL_NAME] = trans.get_role_description(role) or ""
 
150
 
 
151
    def _on_progress_details_changed(self, trans, current_items, total_items,
 
152
                                     current_bytes, total_bytes, current_cps,
 
153
                                     eta):
 
154
        #print "_on_progress_details_changed: ", trans, progress
 
155
        for row in self:
 
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)
 
164
 
 
165
    def _on_progress_changed(self, trans, progress):
 
166
        # print "_on_progress_changed: ", trans, progress
 
167
        for row in self:
 
168
            if row[self.COL_TID] == trans.tid:
 
169
                if progress:
 
170
                    row[self.COL_PROGRESS] = float(progress)
 
171
 
 
172
    def _on_status_changed(self, trans, status):
 
173
        #print "_on_progress_changed: ", trans, status
 
174
        for row in self:
 
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
 
181
                else:
 
182
                    st = trans.get_status_description(status)
 
183
                row[self.COL_STATUS] = self._render_status_text(name, st)
 
184
 
 
185
    def _render_status_text(self, name, status):
 
186
        if not name:
 
187
            name = ""
 
188
        if type(name) == str:
 
189
            pass
 
190
        else:
 
191
            name = name.encode('utf8')
 
192
        return "%s\n<small>%s</small>" % (name, status)
 
193