[rigo] implement loading of Application Images
At the end of the App View, load images bound to App.
This commit is contained in:
@@ -116,3 +116,14 @@ GtkInfoBar #message-area-error GtkLabel {
|
||||
#comment-box-author {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
/* Application View Image Box */
|
||||
#image-box-title {
|
||||
color: #613056; /* purple */
|
||||
}
|
||||
#image-box-comment {
|
||||
color: #345f14;
|
||||
}
|
||||
#image-box-author {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
@@ -377,16 +377,7 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="appViewCommentMoreLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Want to see more?</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -409,6 +400,42 @@
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="appViewImagesAlign">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">25</property>
|
||||
<property name="right_padding">25</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="appViewImagesVbox">
|
||||
<property name="width_request">300</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="appViewCommentMoreLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Want to see more?</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -34,7 +34,8 @@ from entropy.const import const_debug_write, const_debug_enabled, \
|
||||
from entropy.i18n import _
|
||||
from entropy.misc import ParallelTask
|
||||
from entropy.services.client import WebService
|
||||
from entropy.client.services.interfaces import ClientWebService
|
||||
from entropy.client.services.interfaces import ClientWebService, \
|
||||
DocumentList
|
||||
|
||||
import entropy.tools
|
||||
|
||||
@@ -544,7 +545,7 @@ class ApplicationMetadata(object):
|
||||
offset, callback):
|
||||
"""
|
||||
Asynchronously download updated information regarding the
|
||||
comments of given package.
|
||||
comments of the given application.
|
||||
This request disables local cache usage and directly queries
|
||||
the remote Web Service.
|
||||
Once data is available, callback will be called passing the returned
|
||||
@@ -579,6 +580,71 @@ class ApplicationMetadata(object):
|
||||
task.daemon = True
|
||||
task.start()
|
||||
|
||||
@staticmethod
|
||||
def download_images_async(entropy_ws, package_key, repository_id,
|
||||
offset, callback, ignore_icons=True):
|
||||
"""
|
||||
Asynchronously download updated information regarding the images
|
||||
of the given application.
|
||||
This request disables local cache usage and directly queries
|
||||
the remote Web Service.
|
||||
Once data is available, callback will be called passing the returned
|
||||
payload as argument.
|
||||
For this method, the signature of callback is:
|
||||
callback(DocumentList)
|
||||
If the Web Service is not available for repository, None is passed as
|
||||
payload of callback.
|
||||
Please note that the callback is called from another thread.
|
||||
"""
|
||||
webserv = entropy_ws.get(repository_id)
|
||||
if webserv is None:
|
||||
task = ParallelTask(callback, None)
|
||||
task.name = "DownloadImagesAsync::None"
|
||||
task.daemon = True
|
||||
task.start()
|
||||
return None
|
||||
|
||||
def _getter():
|
||||
outcome = None
|
||||
try:
|
||||
images = webserv.get_images(
|
||||
[package_key], cache=False,
|
||||
latest=True, offset=offset)[package_key]
|
||||
|
||||
fetched_images = []
|
||||
for image in images:
|
||||
if image.is_icon() and ignore_icons:
|
||||
continue
|
||||
# check if we have the file on-disk, otherwise
|
||||
# spawn the fetch in parallel.
|
||||
image_path = image.local_document()
|
||||
if not os.path.isfile(image_path):
|
||||
local_path = ApplicationMetadata._download_document(
|
||||
webserv, image)
|
||||
if local_path:
|
||||
fetched_images.append(image)
|
||||
else:
|
||||
fetched_images.append(image)
|
||||
|
||||
# final DocumentList may contain less elements
|
||||
# than those advertised by total().
|
||||
_outcome = DocumentList(
|
||||
images.package_name(),
|
||||
images.total(),
|
||||
images.offset())
|
||||
_outcome.extend(fetched_images)
|
||||
outcome = _outcome
|
||||
|
||||
finally:
|
||||
# ignore exceptions, if any, and always
|
||||
# call callback.
|
||||
callback(outcome)
|
||||
|
||||
task = ParallelTask(_getter)
|
||||
task.name = "DownloadImagesAsync::Getter"
|
||||
task.daemon = True
|
||||
task.start()
|
||||
|
||||
|
||||
class SignalBoolean(object):
|
||||
|
||||
@@ -927,6 +993,31 @@ class Application(object):
|
||||
"Application{%s}.download_comments called" % (
|
||||
self._pkg_match,))
|
||||
|
||||
def download_images(self, callback, offset=0):
|
||||
"""
|
||||
Return Application Images Entropy Document object.
|
||||
In case of missing comments (locally), None is returned.
|
||||
The actual outcome of this method is a DocumentList object.
|
||||
"""
|
||||
repo = self._entropy.open_repository(self._repo_id)
|
||||
key_slot = repo.retrieveKeySlot(self._pkg_id)
|
||||
if key_slot is None:
|
||||
task = ParallelTask(callback, None)
|
||||
task.name = "DownloadImagesNoneCallback"
|
||||
task.daemon = True
|
||||
task.start()
|
||||
return
|
||||
|
||||
key, slot = key_slot
|
||||
ApplicationMetadata.download_images_async(
|
||||
self._entropy_ws, key, self._repo_id,
|
||||
offset, callback)
|
||||
|
||||
if const_debug_enabled():
|
||||
const_debug_write(__name__,
|
||||
"Application{%s}.download_images called" % (
|
||||
self._pkg_match,))
|
||||
|
||||
def is_webservice_available(self):
|
||||
"""
|
||||
Return whether the Entropy Web Service is available
|
||||
|
||||
@@ -46,7 +46,7 @@ class CommentBox(Gtk.VBox):
|
||||
self._comment[ts_id])
|
||||
time_str = escape_markup(time_str)
|
||||
label.set_markup(
|
||||
"<small><b>%s</b>" % (self._comment[user_id],) \
|
||||
"<small><b>%s</b>" % (escape_markup(self._comment[user_id]),) \
|
||||
+ ", <i>" + time_str + "</i>" \
|
||||
+ "</small>")
|
||||
label.set_line_wrap(True)
|
||||
@@ -63,32 +63,34 @@ class CommentBox(Gtk.VBox):
|
||||
if title:
|
||||
title_id = Document.DOCUMENT_TITLE_ID
|
||||
label = Gtk.Label()
|
||||
label.set_markup("<b>" + self._comment[title_id] + "</b>")
|
||||
label_align = Gtk.Alignment()
|
||||
label_align.set_padding(0, 3, 0, 0)
|
||||
label_align.add(label)
|
||||
label.set_markup(
|
||||
"<b>" + escape_markup(self._comment[title_id]) + "</b>")
|
||||
label.set_name("comment-box-title")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 0.0)
|
||||
label.set_selectable(True)
|
||||
label.show()
|
||||
vbox.pack_start(label, False, False, 0)
|
||||
vbox.pack_start(label_align, False, False, 0)
|
||||
|
||||
data_id = Document.DOCUMENT_DATA_ID
|
||||
label = Gtk.Label()
|
||||
label.set_markup("<small>" + self._comment[data_id] + "</small>")
|
||||
label_align = Gtk.Alignment()
|
||||
label_align.set_padding(0, 15, 0, 0)
|
||||
label_align.add(label)
|
||||
label.set_markup(
|
||||
"<small>" + \
|
||||
escape_markup(self._comment[data_id]) + "</small>")
|
||||
label.set_name("comment-box-comment")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 0.0)
|
||||
label.set_selectable(True)
|
||||
label.show()
|
||||
vbox.pack_start(label, False, False, 0)
|
||||
vbox.pack_start(label_align, False, False, 0)
|
||||
|
||||
if self._is_last:
|
||||
self.pack_start(vbox, False, False, 0)
|
||||
vbox.show_all()
|
||||
else:
|
||||
align = Gtk.Alignment()
|
||||
align.set_padding(0, 10, 0, 0)
|
||||
align.add(vbox)
|
||||
self.pack_start(align, False, False, 0)
|
||||
align.show_all()
|
||||
self.pack_start(vbox, False, False, 0)
|
||||
vbox.show_all()
|
||||
|
||||
220
rigo/rigo/ui/gtk3/widgets/images.py
Normal file
220
rigo/rigo/ui/gtk3/widgets/images.py
Normal file
@@ -0,0 +1,220 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2012 Fabio Erculiani
|
||||
|
||||
Authors:
|
||||
Fabio Erculiani
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
"""
|
||||
import os
|
||||
from threading import Lock
|
||||
|
||||
from gi.repository import Gtk, Gdk, Pango, GObject, GdkPixbuf
|
||||
|
||||
from rigo.utils import escape_markup
|
||||
from rigo.ui.gtk3.utils import get_sc_icon_theme
|
||||
from rigo.paths import DATA_DIR
|
||||
from rigo.enums import Icons
|
||||
from rigo.utils import open_url
|
||||
|
||||
from entropy.client.services.interfaces import Document, DocumentFactory
|
||||
|
||||
import entropy.tools
|
||||
|
||||
|
||||
class ImageBox(Gtk.VBox):
|
||||
|
||||
IMAGE_SIZE = 160
|
||||
_MISSING_ICON = None
|
||||
_MISSING_ICON_MUTEX = Lock()
|
||||
_ICONS = None
|
||||
_ICONS_MUTEX = Lock()
|
||||
_hand = Gdk.Cursor.new(Gdk.CursorType.HAND2)
|
||||
|
||||
def __init__(self, image, is_last=False):
|
||||
Gtk.VBox.__init__(self)
|
||||
self._image = image
|
||||
self.set_name("image-box")
|
||||
self.set_spacing(2)
|
||||
self._is_last = is_last
|
||||
|
||||
@property
|
||||
def _icons(self):
|
||||
"""
|
||||
Get Icons Theme Object.
|
||||
"""
|
||||
if ImageBox._ICONS is not None:
|
||||
return ImageBox._ICONS
|
||||
with ImageBox._ICONS_MUTEX:
|
||||
if ImageBox._ICONS is not None:
|
||||
return ImageBox._ICONS
|
||||
_icons = get_sc_icon_theme(DATA_DIR)
|
||||
AppListStore._ICONS = _icons
|
||||
return _icons
|
||||
|
||||
@property
|
||||
def _missing_icon(self):
|
||||
"""
|
||||
Return the missing icon Gtk.Image() if needed.
|
||||
"""
|
||||
if ImageBox._MISSING_ICON is not None:
|
||||
return ImageBox._MISSING_ICON
|
||||
with ImageBox._MISSING_ICON_MUTEX:
|
||||
if ImageBox._MISSING_ICON is not None:
|
||||
return ImageBox._MISSING_ICON
|
||||
_missing_icon = self._icons.load_icon(
|
||||
Icons.MISSING_APP, ImageBox.IMAGE_SIZE, 0)
|
||||
ImageBox._MISSING_ICON = _missing_icon
|
||||
return _missing_icon
|
||||
|
||||
def _on_image_clicked(self, widget, event):
|
||||
"""
|
||||
Image clicked event, load image in browser.
|
||||
"""
|
||||
url = self._image.document_url()
|
||||
if url is not None:
|
||||
open_url(url)
|
||||
|
||||
def _on_image_enter(self, widget, event):
|
||||
"""
|
||||
Cursor over image, switch cursor.
|
||||
"""
|
||||
widget.get_window().set_cursor(ImageBox._hand)
|
||||
|
||||
def _on_image_leave(self, widget, event):
|
||||
"""
|
||||
Cursor leaving image, switch cursor.
|
||||
"""
|
||||
widget.get_window().set_cursor(None)
|
||||
|
||||
def render(self):
|
||||
|
||||
vbox = Gtk.VBox()
|
||||
hbox = Gtk.HBox()
|
||||
vbox.pack_start(hbox, False, False, 0)
|
||||
|
||||
use_missing = False
|
||||
image_path = self._image.local_document()
|
||||
if not os.path.isfile(image_path):
|
||||
img_buf = self._missing_icon
|
||||
use_missing = True
|
||||
else:
|
||||
img = Gtk.Image.new_from_file(image_path)
|
||||
img_buf = img.get_pixbuf()
|
||||
if img_buf is None:
|
||||
use_missing = True
|
||||
img_buf = self._missing_icon
|
||||
del img
|
||||
|
||||
w, h = img_buf.get_width(), img_buf.get_height()
|
||||
del img_buf
|
||||
if w < 1:
|
||||
# not legit
|
||||
use_missing = True
|
||||
img_buf = self._missing_icon
|
||||
|
||||
width = ImageBox.IMAGE_SIZE
|
||||
height = width * h / w
|
||||
|
||||
if not use_missing:
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
|
||||
image_path, width, height)
|
||||
except GObject.GError:
|
||||
try:
|
||||
os.remove(icon_path)
|
||||
except OSError:
|
||||
pass
|
||||
pixbuf = self._missing_icon
|
||||
use_missing = True
|
||||
else:
|
||||
pixbuf = self._missing_icon
|
||||
|
||||
event_image = Gtk.EventBox()
|
||||
image = Gtk.Image.new_from_pixbuf(pixbuf)
|
||||
event_image.add(image)
|
||||
event_image.connect("button-press-event", self._on_image_clicked)
|
||||
event_image.connect("leave-notify-event", self._on_image_leave)
|
||||
event_image.connect("enter-notify-event", self._on_image_enter)
|
||||
hbox.pack_start(event_image, False, False, 2)
|
||||
|
||||
right_vbox = Gtk.VBox()
|
||||
right_align = Gtk.Alignment()
|
||||
right_align.set_padding(0, 0, 8, 0)
|
||||
right_align.add(right_vbox)
|
||||
|
||||
ts_id = Document.DOCUMENT_TIMESTAMP_ID
|
||||
user_id = DocumentFactory.DOCUMENT_USERNAME_ID
|
||||
label = Gtk.Label()
|
||||
time_str = entropy.tools.convert_unix_time_to_human_time(
|
||||
self._image[ts_id])
|
||||
time_str = escape_markup(time_str)
|
||||
label.set_markup(
|
||||
"<small><b>%s</b>" % (escape_markup(self._image[user_id]),) \
|
||||
+ ", <i>" + time_str + "</i>" \
|
||||
+ "</small>")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 1.0)
|
||||
label.set_selectable(True)
|
||||
label.set_name("image-box-author")
|
||||
right_vbox.pack_start(label, False, False, 0)
|
||||
|
||||
# title, keywords, ddata, document_id
|
||||
title = self._image['title'].strip()
|
||||
|
||||
if title:
|
||||
title_id = Document.DOCUMENT_TITLE_ID
|
||||
label = Gtk.Label()
|
||||
label.set_markup(
|
||||
"<b>" + escape_markup(self._image[title_id]) + "</b>")
|
||||
label.set_name("image-box-title")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 0.0)
|
||||
label.set_selectable(True)
|
||||
right_vbox.pack_start(label, False, False, 0)
|
||||
|
||||
desc_id = Document.DOCUMENT_DESCRIPTION_ID
|
||||
label = Gtk.Label()
|
||||
label_align = Gtk.Alignment()
|
||||
label_align.set_padding(0, 5, 0, 0)
|
||||
label_align.add(label)
|
||||
label.set_markup(
|
||||
"<small>" + escape_markup(self._image[desc_id]) + "</small>")
|
||||
label.set_name("image-box-description")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 0.0)
|
||||
label.set_selectable(True)
|
||||
right_vbox.pack_start(label_align, False, False, 0)
|
||||
|
||||
keywords_id = Document.DOCUMENT_KEYWORDS_ID
|
||||
label = Gtk.Label()
|
||||
keywords_txt = "<b>%s:</b> " % (escape_markup(_("Keywords")),)
|
||||
label.set_markup(
|
||||
"<small>" + keywords_txt + \
|
||||
escape_markup(self._image[keywords_id]) + "</small>")
|
||||
label.set_name("image-box-keywords")
|
||||
label.set_line_wrap(True)
|
||||
label.set_line_wrap_mode(Pango.WrapMode.WORD)
|
||||
label.set_alignment(0.0, 0.0)
|
||||
label.set_selectable(True)
|
||||
right_vbox.pack_start(label, False, False, 0)
|
||||
|
||||
hbox.pack_start(right_align, True, True, 0)
|
||||
|
||||
self.pack_start(vbox, False, False, 0)
|
||||
vbox.show_all()
|
||||
105
rigo/rigo_app.py
105
rigo/rigo_app.py
@@ -47,6 +47,7 @@ from rigo.ui.gtk3.widgets.notifications import NotificationBox, \
|
||||
from rigo.ui.gtk3.widgets.welcome import WelcomeBox
|
||||
from rigo.ui.gtk3.widgets.stars import ReactiveStar
|
||||
from rigo.ui.gtk3.widgets.comments import CommentBox
|
||||
from rigo.ui.gtk3.widgets.images import ImageBox
|
||||
from rigo.ui.gtk3.models.appliststore import AppListStore
|
||||
from rigo.ui.gtk3.utils import init_sc_css_provider, get_sc_icon_theme
|
||||
from rigo.utils import build_application_store_url, build_register_url, \
|
||||
@@ -425,6 +426,9 @@ class ApplicationViewController(GObject.Object):
|
||||
|
||||
self._stars_container.pack_start(self._stars_alignment, False, False, 0)
|
||||
|
||||
self._app_images_box = self._builder.get_object(
|
||||
"appViewImagesVbox")
|
||||
|
||||
def set_notification_controller(self, nc):
|
||||
"""
|
||||
Bind NotificationController object to this class.
|
||||
@@ -521,7 +525,7 @@ class ApplicationViewController(GObject.Object):
|
||||
self._on_stars_clicked(self._stars, app=app)
|
||||
box.add_button(_("_Vote now"), _send_vote)
|
||||
|
||||
box.add_destroy_button(_("_Abort"))
|
||||
box.add_destroy_button(_("_Later"))
|
||||
self._nc.append(box)
|
||||
|
||||
def _on_stars_login_failed(self, widget, app):
|
||||
@@ -556,7 +560,7 @@ class ApplicationViewController(GObject.Object):
|
||||
self._logout_webservice(app, _send_vote)
|
||||
box.add_button(_("_No, logout!"), _logout_webservice)
|
||||
|
||||
box.add_destroy_button(_("_Abort"))
|
||||
box.add_destroy_button(_("_Later"))
|
||||
self._nc.append(box)
|
||||
|
||||
def _vote_submit(self, app, username, vote):
|
||||
@@ -640,6 +644,8 @@ class ApplicationViewController(GObject.Object):
|
||||
going to hide.
|
||||
"""
|
||||
self._last_app = None
|
||||
for child in self._app_my_comments_box.get_children():
|
||||
child.destroy()
|
||||
self.emit("application-hide", self)
|
||||
|
||||
def _on_send_comment(self, widget, app=None):
|
||||
@@ -703,16 +709,16 @@ class ApplicationViewController(GObject.Object):
|
||||
context_id=self.COMMENT_NOTIFICATION_CONTEXT_ID)
|
||||
|
||||
def _comment_submit(widget):
|
||||
#box.destroy()
|
||||
self._comment_submit(app, username, text)
|
||||
box.add_button(_("_Ok, cool!"), _comment_submit)
|
||||
|
||||
def _send_comment():
|
||||
self._on_send_comment(None, app=app)
|
||||
def _logout_webservice(widget):
|
||||
#box.destroy()
|
||||
self._logout_webservice(app)
|
||||
self._logout_webservice(app, _send_comment)
|
||||
box.add_button(_("_No, logout!"), _logout_webservice)
|
||||
|
||||
box.add_destroy_button(_("_Abort"))
|
||||
box.add_destroy_button(_("_Later"))
|
||||
self._nc.append(box)
|
||||
|
||||
def _comment_submit(self, app, username, text):
|
||||
@@ -811,7 +817,7 @@ class ApplicationViewController(GObject.Object):
|
||||
def _send_comment(widget):
|
||||
self._on_send_comment(widget, app=app)
|
||||
box.add_button(_("_Send now"), _send_comment)
|
||||
box.add_destroy_button(_("_Abort"))
|
||||
box.add_destroy_button(_("_Later"))
|
||||
self._nc.append(box)
|
||||
|
||||
def _on_comment_login_failed(self, widget, app):
|
||||
@@ -879,6 +885,60 @@ class ApplicationViewController(GObject.Object):
|
||||
GLib.idle_add(self._append_comments, downloader, app,
|
||||
comments, has_more)
|
||||
|
||||
def _append_images(self, downloader, app, images, has_more):
|
||||
"""
|
||||
Append given Entropy WebService Document objects to
|
||||
the images area.
|
||||
"""
|
||||
# remove spinner if there, ugly O(n)
|
||||
for child in self._app_images_box.get_children():
|
||||
if not isinstance(child, ImageBox):
|
||||
child.destroy()
|
||||
|
||||
if not images:
|
||||
label = Gtk.Label()
|
||||
label.set_markup(
|
||||
_("<i>No <b>images</b> for this Application, yet!</i>"))
|
||||
self._app_images_box.pack_start(label, False, False, 1)
|
||||
label.show()
|
||||
return
|
||||
|
||||
if has_more:
|
||||
button_box = Gtk.HButtonBox()
|
||||
button = Gtk.Button()
|
||||
button.set_label(_("Older images"))
|
||||
button.set_alignment(0.5, 0.5)
|
||||
def _enqueue_download(widget):
|
||||
widget.get_parent().destroy()
|
||||
spinner = Gtk.Spinner()
|
||||
spinner.set_size_request(24, 24)
|
||||
spinner.set_tooltip_text(_("Loading older images..."))
|
||||
spinner.set_name("image-box-spinner")
|
||||
self._app_images_box.pack_end(spinner, False, False, 3)
|
||||
spinner.show()
|
||||
downloader.enqueue_download()
|
||||
button.connect("clicked", _enqueue_download)
|
||||
|
||||
button_box.pack_start(button, False, False, 0)
|
||||
self._app_images_box.pack_start(button_box, False, False, 1)
|
||||
button_box.show_all()
|
||||
|
||||
idx = 0
|
||||
length = len(images)
|
||||
for doc in images:
|
||||
idx += 1
|
||||
box = ImageBox(doc, is_last=(not has_more and (idx == length)))
|
||||
box.render()
|
||||
self._app_images_box.pack_end(box, False, False, 2)
|
||||
box.show()
|
||||
|
||||
def _append_images_safe(self, downloader, app, comments, has_more):
|
||||
"""
|
||||
Same as _append_images() but thread-safe.
|
||||
"""
|
||||
GLib.idle_add(self._append_images, downloader, app,
|
||||
comments, has_more)
|
||||
|
||||
def _setup_application_stats(self, stats, icon):
|
||||
"""
|
||||
Setup widgets related to Application statistics (and icon).
|
||||
@@ -933,23 +993,30 @@ class ApplicationViewController(GObject.Object):
|
||||
spinner.show()
|
||||
spinner.start()
|
||||
|
||||
downloader = ApplicationViewController.CommentsDownloader(
|
||||
app, self, self._append_comments_safe)
|
||||
downloader = ApplicationViewController.MetadataDownloader(
|
||||
app, self, self._append_comments_safe,
|
||||
app.download_comments)
|
||||
downloader.start()
|
||||
|
||||
downloader = ApplicationViewController.MetadataDownloader(
|
||||
app, self, self._append_images_safe,
|
||||
app.download_images)
|
||||
downloader.start()
|
||||
|
||||
self.emit("application-show", app)
|
||||
|
||||
class CommentsDownloader(GObject.Object):
|
||||
class MetadataDownloader(GObject.Object):
|
||||
"""
|
||||
Automated Application comments downloader.
|
||||
"""
|
||||
|
||||
def __init__(self, app, avc, callback):
|
||||
def __init__(self, app, avc, callback, app_downloader_method):
|
||||
self._app = app
|
||||
self._avc = avc
|
||||
self._offset = 0
|
||||
self._callback = callback
|
||||
self._task = ParallelTask(self._download)
|
||||
self._app_downloader = app_downloader_method
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
@@ -961,7 +1028,7 @@ class ApplicationViewController(GObject.Object):
|
||||
|
||||
def _download_callback(self, document_list):
|
||||
"""
|
||||
Callback called by download_comments() once data
|
||||
Callback called by download_<something>() once data
|
||||
is arrived from web service.
|
||||
document_list can be None!
|
||||
"""
|
||||
@@ -976,12 +1043,12 @@ class ApplicationViewController(GObject.Object):
|
||||
if const_debug_enabled():
|
||||
const_debug_write(
|
||||
__name__,
|
||||
"CommentsDownloader._download_callback: %s, more: %s" % (
|
||||
"MetadataDownloader._download_callback: %s, more: %s" % (
|
||||
document_list, has_more))
|
||||
if document_list is not None:
|
||||
const_debug_write(
|
||||
__name__,
|
||||
"CommentsDownloader._download_callback: "
|
||||
"MetadataDownloader._download_callback: "
|
||||
"total: %s, offset: %s" % (
|
||||
document_list.total(), document_list.offset()))
|
||||
|
||||
@@ -989,13 +1056,13 @@ class ApplicationViewController(GObject.Object):
|
||||
|
||||
def reset_offset(self):
|
||||
"""
|
||||
Reset Comments download offset to 0.
|
||||
Reset Metadata download offset to 0.
|
||||
"""
|
||||
self._offset = 0
|
||||
|
||||
def get_offset(self):
|
||||
"""
|
||||
Get current Comments download offset.
|
||||
Get current Metadata download offset.
|
||||
"""
|
||||
return self._offset
|
||||
|
||||
@@ -1008,10 +1075,10 @@ class ApplicationViewController(GObject.Object):
|
||||
|
||||
def _download(self):
|
||||
"""
|
||||
Thread body of the initial Comments downloader.
|
||||
Thread body of the initial Metadata downloader.
|
||||
"""
|
||||
self._app.download_comments(self._download_callback,
|
||||
offset=self._offset)
|
||||
self._app_downloader(self._download_callback,
|
||||
offset=self._offset)
|
||||
|
||||
|
||||
class Rigo(Gtk.Application):
|
||||
|
||||
Reference in New Issue
Block a user