Add custom theme and icons
Genlist items have a translucent background with simple progressbar on top of that.
|
@ -2,3 +2,4 @@
|
|||
build/
|
||||
dist/
|
||||
MANIFEST
|
||||
data/themes
|
3
AUTHORS
|
@ -1,4 +1,5 @@
|
|||
Kai Huuhko <kai.huuhko@gmail.com>
|
||||
Davide Andreoli <dave@gurumeditation.it>
|
||||
|
||||
Icons by swordplay <swordplay@gmail.com> from Bodhi Linux forums.
|
||||
Application icon by swordplay <swordplay@gmail.com> from Bodhi Linux forums.
|
||||
Other icons from IcoMoon - Free by Keyamoon (https://keyamoon.com) downloaded from https://icomoon.io
|
After Width: | Height: | Size: 577 B |
After Width: | Height: | Size: 1020 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 423 B |
After Width: | Height: | Size: 465 B |
After Width: | Height: | Size: 615 B |
After Width: | Height: | Size: 627 B |
|
@ -19,6 +19,7 @@
|
|||
# MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
import os
|
||||
import cgi
|
||||
import logging
|
||||
from datetime import timedelta, datetime
|
||||
|
@ -38,8 +39,8 @@ from efl.elementary import Genlist, GenlistItemClass, \
|
|||
ELM_GENLIST_ITEM_FIELD_TEXT, ELM_GENLIST_ITEM_FIELD_CONTENT, \
|
||||
ELM_OBJECT_SELECT_MODE_NONE, ELM_OBJECT_SELECT_MODE_DEFAULT, \
|
||||
ELM_LIST_COMPRESS
|
||||
from efl.elementary import StandardWindow
|
||||
from efl.elementary import Icon
|
||||
from efl.elementary import Window, Background, ELM_WIN_BASIC
|
||||
from efl.elementary import Icon, Image
|
||||
from efl.elementary import Box
|
||||
from efl.elementary import Label
|
||||
from efl.elementary import Panel, ELM_PANEL_ORIENT_BOTTOM
|
||||
|
@ -47,6 +48,10 @@ from efl.elementary import Table
|
|||
from efl.elementary import Menu
|
||||
from efl.elementary import Configuration
|
||||
from efl.elementary import Toolbar, ELM_TOOLBAR_SHRINK_NONE
|
||||
from efl.elementary import Theme
|
||||
from efl.elementary import Progressbar
|
||||
|
||||
from xdg.BaseDirectory import load_data_paths
|
||||
|
||||
from .Widgets import ConfirmExit, Error, Information, BlockGraph
|
||||
|
||||
|
@ -62,6 +67,14 @@ scale = elm_conf.scale
|
|||
|
||||
log = logging.getLogger("epour.gui")
|
||||
|
||||
theme_file = None
|
||||
|
||||
for data_path in load_data_paths("epour"):
|
||||
if os.path.exists(data_path):
|
||||
if os.path.exists(os.path.join(data_path, "themes", "default.edj")):
|
||||
theme_file = os.path.join(data_path, "themes", "default.edj")
|
||||
break
|
||||
|
||||
|
||||
class MainInterface(object):
|
||||
|
||||
|
@ -71,11 +84,20 @@ class MainInterface(object):
|
|||
|
||||
self.torrentitems = {}
|
||||
|
||||
win = self.win = StandardWindow(
|
||||
"epour", "Epour", size=(480 * scale, 400 * scale),
|
||||
theme = Theme.default_get()
|
||||
theme.overlay_add(theme_file)
|
||||
theme.extension_add(theme_file)
|
||||
|
||||
win = self.win = Window(
|
||||
"epour", ELM_WIN_BASIC, size=(480 * scale, 400 * scale),
|
||||
screen_constrain=True)
|
||||
win.title = "Epour"
|
||||
win.callback_delete_request_add(lambda x: self.quit())
|
||||
|
||||
bg = Background(win, size_hint_weight=EXPAND_BOTH, color=(0, 0, 0))
|
||||
win.resize_object_add(bg)
|
||||
bg.show()
|
||||
|
||||
mbox = Box(win, size_hint_weight=EXPAND_BOTH)
|
||||
win.resize_object_add(mbox)
|
||||
mbox.show()
|
||||
|
@ -88,7 +110,7 @@ class MainInterface(object):
|
|||
tb.menu_parent = win
|
||||
|
||||
item = tb.item_append(
|
||||
"document-new", _("Add torrent"),
|
||||
"toolbar-new", _("Add torrent"),
|
||||
lambda x, y: self.add_torrent())
|
||||
|
||||
def pause_session(it):
|
||||
|
@ -100,10 +122,10 @@ class MainInterface(object):
|
|||
del it.state
|
||||
|
||||
item = tb.item_append(
|
||||
"media-playback-pause", _("Pause Session"),
|
||||
"session-pause", _("Pause Session"),
|
||||
lambda tb, it: pause_session(it))
|
||||
item.state_add(
|
||||
"media-playback-start", _("Resume Session"),
|
||||
"session-resume", _("Resume Session"),
|
||||
lambda tb, it: resume_session(it))
|
||||
|
||||
def prefs_general_cb():
|
||||
|
@ -118,19 +140,19 @@ class MainInterface(object):
|
|||
from .Preferences import PreferencesSession
|
||||
PreferencesSession(self, self._session).show()
|
||||
|
||||
item = tb.item_append("preferences-system", _("Preferences"))
|
||||
item = tb.item_append("toolbar-settings", _("Preferences"))
|
||||
item.menu = True
|
||||
item.menu.item_add(
|
||||
None, _("General"), "preferences-system",
|
||||
None, _("General"), "toolbar-settings",
|
||||
lambda o, i: prefs_general_cb())
|
||||
item.menu.item_add(
|
||||
None, _("Proxy"), "preferences-system",
|
||||
None, _("Proxy"), "toolbar-settings",
|
||||
lambda o, i: prefs_proxy_cb())
|
||||
item.menu.item_add(
|
||||
None, _("Session"), "preferences-system",
|
||||
None, _("Session"), "toolbar-settings",
|
||||
lambda o, i: prefs_session_cb())
|
||||
|
||||
item = tb.item_append("application-exit", _("Exit"),
|
||||
item = tb.item_append("toolbar-quit", _("Exit"),
|
||||
lambda tb, it: self.quit())
|
||||
|
||||
mbox.pack_start(tb)
|
||||
|
@ -442,6 +464,12 @@ class TorrentClass(GenlistItemClass):
|
|||
_('Downloading'), _('Finished'), _('Seeding'), _('Allocating'),
|
||||
_('Checking resume data'))
|
||||
|
||||
state_ic = (
|
||||
"torrent-queued", "torrent-checking-files",
|
||||
"torrent-downloading-metadata", "torrent-downloading",
|
||||
"torrent-finished", "torrent-seeding", "torrent-allocating",
|
||||
"torrent-checking-resume-data")
|
||||
|
||||
log = logging.getLogger("epour.gui.torrent_list")
|
||||
|
||||
def __init__(self, session, *args, **kwargs):
|
||||
|
@ -460,62 +488,79 @@ class TorrentClass(GenlistItemClass):
|
|||
return '%s' % (torrent.status.name)
|
||||
elif part == "elm.text.sub":
|
||||
status = torrent.status
|
||||
qp = handle.queue_position()
|
||||
qp = status.queue_position
|
||||
if qp == -1:
|
||||
qp = "seeding"
|
||||
|
||||
return _(
|
||||
"{0:.0%} complete, ETA: {1} "
|
||||
"(Down: {2}/s Up: {3}/s Queue pos: {4})").format(
|
||||
status.progress,
|
||||
timedelta(seconds=self.get_eta(status)),
|
||||
intrepr(status.download_payload_rate, precision=0),
|
||||
intrepr(status.upload_payload_rate, precision=0),
|
||||
qp)
|
||||
string = ""
|
||||
|
||||
def content_get(self, obj, part, item_data):
|
||||
if part != "elm.swallow.icon":
|
||||
return
|
||||
eta = self.get_eta(status)
|
||||
|
||||
torrent = item_data
|
||||
handle = torrent.handle
|
||||
if eta:
|
||||
# NOTE: Estimated Time of Arrival (time until the process is finished)
|
||||
string += _("ETA: {0} ").format(timedelta(seconds=eta))
|
||||
|
||||
if not handle.is_valid():
|
||||
return
|
||||
if not status.is_seeding:
|
||||
string += _("Download: {0}/s ").format(intrepr(status.download_payload_rate, precision=0))
|
||||
|
||||
status = torrent.status
|
||||
state = torrent.state
|
||||
ic = Icon(obj)
|
||||
try:
|
||||
if status.paused:
|
||||
try:
|
||||
ic.standard = "player_pause"
|
||||
except Exception:
|
||||
try:
|
||||
ic.standard = "media-playback-pause"
|
||||
except Exception:
|
||||
pass
|
||||
elif status.is_seeding:
|
||||
try:
|
||||
ic.standard = "up"
|
||||
except Exception:
|
||||
try:
|
||||
ic.standard = "arrow-up"
|
||||
except Exception:
|
||||
pass
|
||||
string += _("Upload: {0}/s ").format(intrepr(status.upload_payload_rate, precision=0))
|
||||
|
||||
string += _("Queue position: {0}").format(qp)
|
||||
|
||||
return string
|
||||
|
||||
def reusable_content_get(self, obj, part, item_data, old_content):
|
||||
if part == "elm.swallow.icon":
|
||||
torrent = item_data
|
||||
handle = torrent.handle
|
||||
|
||||
if not handle.is_valid():
|
||||
return
|
||||
|
||||
state = torrent.state
|
||||
if old_content:
|
||||
ic = old_content
|
||||
else:
|
||||
try:
|
||||
ic.standard = "down"
|
||||
except Exception:
|
||||
try:
|
||||
ic.standard = "arrow-down"
|
||||
except Exception:
|
||||
pass
|
||||
except RuntimeError:
|
||||
log.debug("Setting torrent ic failed")
|
||||
ic.tooltip_text_set(self.state_str[state])
|
||||
ic.size_hint_aspect_set(EVAS_ASPECT_CONTROL_VERTICAL, 1, 1)
|
||||
return ic
|
||||
ic = Image(obj)
|
||||
group = "states/" + self.state_ic[state]
|
||||
ic.file_set(theme_file, group)
|
||||
ic.tooltip_text_set(self.state_str[state])
|
||||
ic.size_hint_aspect_set(EVAS_ASPECT_CONTROL_VERTICAL, 1, 1)
|
||||
return ic
|
||||
elif part == "elm.swallow.end":
|
||||
torrent = item_data
|
||||
handle = torrent.handle
|
||||
|
||||
if not handle.is_valid():
|
||||
return
|
||||
|
||||
status = torrent.status
|
||||
if old_content:
|
||||
ic = old_content
|
||||
else:
|
||||
ic = Image(obj)
|
||||
group = "states/"
|
||||
if status.paused:
|
||||
group += "torrent-paused"
|
||||
else:
|
||||
group += "torrent-active"
|
||||
ic.file_set(theme_file, group)
|
||||
return ic
|
||||
else:
|
||||
torrent = item_data
|
||||
handle = torrent.handle
|
||||
|
||||
if not handle.is_valid():
|
||||
return
|
||||
status = torrent.status
|
||||
if old_content:
|
||||
pb = old_content
|
||||
else:
|
||||
pb = Progressbar(obj)
|
||||
pb.style = "simple"
|
||||
pb.unit_format = None
|
||||
pb.value = status.progress
|
||||
return pb
|
||||
|
||||
def get_eta(self, s):
|
||||
# if s.is_seeding and self.options["stop_at_ratio"]:
|
||||
|
@ -528,9 +573,12 @@ class TorrentClass(GenlistItemClass):
|
|||
# s.all_time_upload
|
||||
# ) / s.upload_payload_rate
|
||||
|
||||
if s.download_payload_rate == 0:
|
||||
return 0
|
||||
|
||||
left = s.total_wanted - s.total_wanted_done
|
||||
|
||||
if left <= 0 or s.download_payload_rate == 0:
|
||||
if left <= 0:
|
||||
return 0
|
||||
|
||||
eta = int(round(left / s.download_payload_rate))
|
||||
|
|
76
setup.py
|
@ -1,5 +1,9 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import distutils
|
||||
from distutils.command.build import build
|
||||
from distutils.log import warn, info, error
|
||||
from DistUtilsExtra import auto
|
||||
|
||||
from epour import __version__
|
||||
|
@ -8,6 +12,73 @@ from epour import __version__
|
|||
class sdist_auto(auto.sdist_auto):
|
||||
filter_suffix = ['.pyc', '.mo', '~', '.swp', '-workspace', '-project']
|
||||
|
||||
|
||||
def _data_files_append(distribution, target, files):
|
||||
""" Tiny util to append to data_files, ensuring data_file is defined """
|
||||
if not isinstance(files, (list, tuple)):
|
||||
files = [files]
|
||||
if distribution.data_files is None:
|
||||
data_files = distribution.data_files = []
|
||||
else:
|
||||
data_files = distribution.data_files
|
||||
data_files.append((target, files))
|
||||
|
||||
|
||||
class build_edc(distutils.cmd.Command):
|
||||
description = 'compile all the edje themes using edje_cc'
|
||||
user_options = [('themes-dir=', 'd', 'directory that holds the themes '
|
||||
'(default: data/themes)'),
|
||||
('main-name=', 'n', 'main edc file name of the themes '
|
||||
'(default: main.edc)')]
|
||||
|
||||
def initialize_options(self):
|
||||
self.themes_dir = None
|
||||
self.main_name = None
|
||||
|
||||
def finalize_options(self):
|
||||
if self.themes_dir is None:
|
||||
self.themes_dir = 'themes'
|
||||
if self.main_name is None:
|
||||
self.main_name = 'main.edc'
|
||||
|
||||
def run(self):
|
||||
distutils.dir_util.mkpath('data/themes', verbose=False)
|
||||
for name in os.listdir(self.themes_dir):
|
||||
edc_file = os.path.join(self.themes_dir, name, self.main_name)
|
||||
if os.path.isfile(edc_file):
|
||||
self.compile_theme(name, edc_file)
|
||||
|
||||
def compile_theme(self, name, edc_file):
|
||||
"""
|
||||
Compile edc file to using edje_cc, and put the generated theme file
|
||||
in the data_files list so it got installed.
|
||||
"""
|
||||
theme_dir = os.path.dirname(edc_file)
|
||||
sources = []
|
||||
for root, dirs, files in os.walk(theme_dir):
|
||||
sources.extend( os.path.join(root, name) for name in files )
|
||||
|
||||
edj_file = os.path.join('data', 'themes', '%s.edj' % name)
|
||||
if distutils.dep_util.newer_group(sources, edj_file):
|
||||
info('compiling theme "%s" from edc file: "%s"' % (name, edc_file))
|
||||
cmd = ['edje_cc', '-v',
|
||||
'-id', theme_dir, '-id', os.path.join(theme_dir, 'images'),
|
||||
'-fd', theme_dir, '-fd', os.path.join(theme_dir, 'fonts'),
|
||||
'-sd', theme_dir, '-sd', os.path.join(theme_dir, 'sounds'),
|
||||
edc_file, edj_file]
|
||||
self.spawn(cmd)
|
||||
|
||||
info("changing mode of %s to 644", edj_file)
|
||||
os.chmod(edj_file, 0o0644) # stupid edje_cc create files as 0600 :/
|
||||
|
||||
target = os.path.join('share', self.distribution.get_name(), 'themes')
|
||||
_data_files_append(self.distribution, target, edj_file)
|
||||
|
||||
def has_themes(bar):
|
||||
return True
|
||||
|
||||
build.sub_commands.append(('build_edc', has_themes))
|
||||
|
||||
auto.setup(
|
||||
name='epour',
|
||||
version=__version__,
|
||||
|
@ -30,6 +101,7 @@ auto.setup(
|
|||
'dbus',
|
||||
],
|
||||
cmdclass={
|
||||
'sdist': sdist_auto
|
||||
}
|
||||
'sdist': sdist_auto,
|
||||
'build_edc': build_edc,
|
||||
},
|
||||
)
|
||||
|
|
After Width: | Height: | Size: 136 B |
After Width: | Height: | Size: 74 B |
After Width: | Height: | Size: 486 B |
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 1020 B |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 828 B |
After Width: | Height: | Size: 578 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 423 B |
After Width: | Height: | Size: 682 B |
After Width: | Height: | Size: 633 B |
After Width: | Height: | Size: 469 B |
After Width: | Height: | Size: 712 B |
After Width: | Height: | Size: 580 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 369 B |
After Width: | Height: | Size: 566 B |
After Width: | Height: | Size: 526 B |
After Width: | Height: | Size: 1.9 KiB |