676 lines
21 KiB
Python
676 lines
21 KiB
Python
#
|
|
# Epour - A bittorrent client using EFL and libtorrent
|
|
#
|
|
# Copyright 2012-2017 Kai Huuhko <kai.huuhko@gmail.com>
|
|
#
|
|
# 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; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# 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.
|
|
#
|
|
|
|
if "long" not in dir(__builtins__):
|
|
long = int
|
|
|
|
import html
|
|
import sys
|
|
import os
|
|
import pipes
|
|
from datetime import datetime, timedelta
|
|
import logging
|
|
log = logging.getLogger("epour.gui")
|
|
|
|
import libtorrent as lt
|
|
|
|
from efl import ecore
|
|
from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL
|
|
from efl.elementary.genlist import Genlist, GenlistItemClass, \
|
|
ELM_GENLIST_ITEM_NONE, ELM_GENLIST_ITEM_TREE, ELM_GENLIST_ITEM_FIELD_TEXT
|
|
from efl.elementary.window import DialogWindow
|
|
from efl.elementary.button import Button
|
|
from efl.elementary.box import Box
|
|
from efl.elementary.check import Check
|
|
from efl.elementary.label import Label, ELM_WRAP_CHAR
|
|
from efl.elementary.entry import Entry
|
|
from efl.elementary.frame import Frame
|
|
from efl.elementary.object import ELM_SEL_FORMAT_TEXT, \
|
|
ELM_SEL_TYPE_CLIPBOARD
|
|
from efl.elementary.table import Table
|
|
from efl.elementary.progressbar import Progressbar
|
|
from efl.elementary.configuration import Configuration
|
|
elm_conf = Configuration()
|
|
SCALE = elm_conf.scale
|
|
from efl.elementary.scroller import Scroller
|
|
from efl.elementary.spinner import Spinner
|
|
from efl.elementary.fileselector import Fileselector
|
|
from efl.elementary.fileselector_entry import FileselectorEntry
|
|
|
|
from .intrepr import intrepr
|
|
from .Widgets import Information, UnitSpinner
|
|
|
|
EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
|
|
EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
|
|
FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
|
|
FILL_HORIZ = EVAS_HINT_FILL, 0.5
|
|
ALIGN_LEFT = 0.0, 0.5
|
|
ALIGN_RIGHT = 1.0, 0.5
|
|
|
|
|
|
from collections import defaultdict
|
|
FILE_MARKER = '<files>'
|
|
|
|
|
|
class FileClass(GenlistItemClass):
|
|
|
|
def text_get(self, obj, part, data):
|
|
fn, file_entry, n = data
|
|
progress = obj.data["progress"][n]
|
|
return "{} - {}/{} ({:.0%})".format(
|
|
fn,
|
|
intrepr(progress),
|
|
intrepr(file_entry.size),
|
|
float(progress)/float(file_entry.size)
|
|
)
|
|
|
|
def content_get(self, obj, part, data):
|
|
fn, file_entry, n = data
|
|
h = obj.data["handle"]
|
|
if part == "elm.swallow.icon":
|
|
check = Check(obj)
|
|
check.tooltip_text_set(_("Enable/disable file download"))
|
|
check.state = h.file_priority(n)
|
|
check.callback_changed_add(lambda x: h.file_priority(n, x.state))
|
|
return check
|
|
|
|
|
|
class DirectoryClass(GenlistItemClass):
|
|
def text_get(self, obj, part, data):
|
|
dir_name, d = data
|
|
return "{}".format(
|
|
dir_name
|
|
)
|
|
|
|
|
|
ITEM_CLASS_FILES = FileClass(item_style="indent")
|
|
ITEM_CLASS_DIRS = DirectoryClass()
|
|
|
|
|
|
def attach(branch, file_entry, n, trunk):
|
|
"""Insert a branch of directories on its trunk."""
|
|
parts = branch.split('/', 1)
|
|
if len(parts) == 1: # branch is a file
|
|
trunk[FILE_MARKER].append((parts[0], file_entry, n))
|
|
else:
|
|
node, others = parts
|
|
if node not in trunk:
|
|
trunk[node] = defaultdict(dict, ((FILE_MARKER, []),))
|
|
attach(others, file_entry, n, trunk[node])
|
|
|
|
|
|
class TorrentProps(DialogWindow):
|
|
|
|
def __init__(self, parent_win, h):
|
|
if not h.is_valid():
|
|
Information(self, _("Invalid torrent handle."))
|
|
return
|
|
|
|
DialogWindow.__init__(
|
|
self, parent_win, "epour", "Epour - %s" % (h.name()),
|
|
size=(SCALE * 480, SCALE * 320),
|
|
autodel=True
|
|
)
|
|
|
|
scroller = Scroller(self)
|
|
self.resize_object_add(scroller)
|
|
|
|
box = Box(self, size_hint_weight=EXPAND_BOTH)
|
|
|
|
scroller.content = box
|
|
|
|
tname = Label(
|
|
self, size_hint_align=FILL_HORIZ, line_wrap=ELM_WRAP_CHAR,
|
|
ellipsis=True, scale=2.0
|
|
)
|
|
tname.text = "{}".format(html.escape(h.name()))
|
|
tname.show()
|
|
box.pack_end(tname)
|
|
|
|
if h.has_metadata():
|
|
f = Frame(self, text=_("Torrent info"), size_hint_align=FILL_HORIZ)
|
|
ti = TorrentInfo(
|
|
f, h, size_hint_align=FILL_HORIZ)
|
|
f.content = ti
|
|
box.pack_end(f)
|
|
ti.show()
|
|
f.show()
|
|
|
|
f = Frame(self, text=_("Torrent settings"), size_hint_align=FILL_HORIZ)
|
|
ts = TorrentSettings(
|
|
f, h, size_hint_align=FILL_HORIZ)
|
|
f.content = ts
|
|
box.pack_end(f)
|
|
ts.show()
|
|
f.show()
|
|
|
|
f = Frame(self, text=_("Torrent status"), size_hint_align=FILL_HORIZ)
|
|
ts = TorrentStatus(f, h, size_hint_align=FILL_HORIZ)
|
|
f.content = ts
|
|
box.pack_end(f)
|
|
ts.show()
|
|
f.show()
|
|
|
|
magnet_uri = lt.make_magnet_uri(h)
|
|
|
|
f = Frame(self, size_hint_align=FILL_HORIZ, text=_("Magnet URI"))
|
|
me_box = Box(f, horizontal=True)
|
|
me = Entry(
|
|
me_box, size_hint_align=FILL_HORIZ, size_hint_weight=EXPAND_HORIZ,
|
|
editable=False, entry=magnet_uri, line_wrap=ELM_WRAP_CHAR
|
|
)
|
|
me_box.pack_end(me)
|
|
me.show()
|
|
me_btn = Button(me_box, text=_("Copy"))
|
|
me_btn.callback_clicked_add(
|
|
lambda x: me.top_widget.cnp_selection_set(
|
|
ELM_SEL_TYPE_CLIPBOARD, ELM_SEL_FORMAT_TEXT, me.text
|
|
)
|
|
)
|
|
me_btn.show()
|
|
me_box.pack_end(me_btn)
|
|
me_box.show()
|
|
f.content = me_box
|
|
f.show()
|
|
box.pack_end(f)
|
|
|
|
# xbtn = Button(box, text="Close")
|
|
# xbtn.callback_clicked_add(lambda x: self.delete())
|
|
# box.pack_end(xbtn)
|
|
# xbtn.show()
|
|
|
|
box.show()
|
|
scroller.show()
|
|
|
|
|
|
class TorrentFiles(DialogWindow):
|
|
def __init__(self, parent_win, h):
|
|
DialogWindow.__init__(
|
|
self,
|
|
parent_win,
|
|
"epour",
|
|
_("Epour - Files for torrent: %s") % (h.name()),
|
|
size=(SCALE * 480, SCALE * 320),
|
|
autodel=True
|
|
)
|
|
|
|
box = Box(self, size_hint_weight=EXPAND_BOTH)
|
|
self.resize_object_add(box)
|
|
|
|
file_dict = defaultdict(dict, ((FILE_MARKER, []),))
|
|
|
|
filelist = Genlist(
|
|
box, size_hint_align=FILL_BOTH, size_hint_weight=EXPAND_BOTH,
|
|
homogeneous=True, tree_effect_enabled=True
|
|
)
|
|
filelist.data["handle"] = h
|
|
filelist.data["file_dict"] = file_dict
|
|
filelist.data["progress"] = h.file_progress()
|
|
|
|
def update_progress(h):
|
|
#log.debug("File progress TICK")
|
|
filelist.data["progress"] = h.file_progress()
|
|
for it in filelist.realized_items:
|
|
it.fields_update("*", ELM_GENLIST_ITEM_FIELD_TEXT)
|
|
return True
|
|
|
|
timer = ecore.Timer(5.0, update_progress, h)
|
|
self.on_del_add(lambda x: timer.delete())
|
|
self.callback_withdrawn_add(lambda x: timer.freeze())
|
|
self.callback_iconified_add(lambda x: timer.freeze())
|
|
self.callback_normal_add(lambda x: timer.thaw())
|
|
|
|
filelist.callback_expand_request_add(self.expand_request_cb)
|
|
filelist.callback_contract_request_add(self.contract_request_cb)
|
|
|
|
filelist.callback_expanded_add(self.item_expanded_cb)
|
|
filelist.callback_contracted_add(self.item_contracted_cb)
|
|
filelist.callback_activated_add(self.item_activated_cb)
|
|
|
|
self.populate(filelist)
|
|
|
|
filelist.show()
|
|
|
|
sel_all = Button(self, text=_("Select all"))
|
|
sel_all.callback_clicked_add(self.select_all_cb, filelist, h, True)
|
|
sel_all.show()
|
|
|
|
sel_none = Button(self, text=_("Select none"))
|
|
sel_none.callback_clicked_add(self.select_all_cb, filelist, h, False)
|
|
sel_none.show()
|
|
|
|
# xbtn = Button(self)
|
|
# xbtn.text = "Close"
|
|
# xbtn.callback_clicked_add(lambda x: parent.item_pop())
|
|
# xbtn.show()
|
|
|
|
btn_box = Box(self, horizontal=True)
|
|
btn_box.pack_end(sel_all)
|
|
btn_box.pack_end(sel_none)
|
|
#btn_box.pack_end(xbtn)
|
|
btn_box.show()
|
|
|
|
box.pack_end(filelist)
|
|
box.pack_end(btn_box)
|
|
box.show()
|
|
self.show()
|
|
|
|
def select_all_cb(self, btn, filelist, h, all_selected=True):
|
|
priorities = h.file_priorities()
|
|
for n, p in enumerate(priorities):
|
|
priorities[n] = all_selected
|
|
h.prioritize_files(priorities)
|
|
filelist.realized_items_update()
|
|
|
|
def populate(self, filelist):
|
|
h = filelist.data["handle"]
|
|
file_dict = filelist.data["file_dict"]
|
|
filelist.clear()
|
|
|
|
i = h.get_torrent_info()
|
|
entries = i.files()
|
|
|
|
for n, f in enumerate(entries):
|
|
attach(f.path, f, n, file_dict)
|
|
|
|
def construct(d, parent=None):
|
|
"""Construct a genlist tree."""
|
|
for key in sorted(d.keys()):
|
|
value = d[key]
|
|
if key == FILE_MARKER:
|
|
if value:
|
|
for fn, fe, n in value:
|
|
filelist.item_append(
|
|
ITEM_CLASS_FILES,
|
|
(fn, fe, n),
|
|
parent,
|
|
ELM_GENLIST_ITEM_NONE)
|
|
else:
|
|
it = filelist.item_append(
|
|
ITEM_CLASS_DIRS,
|
|
(key, value),
|
|
parent,
|
|
ELM_GENLIST_ITEM_TREE)
|
|
it.expanded = True
|
|
|
|
construct(file_dict)
|
|
|
|
def expand_request_cb(self, gl, item):
|
|
item.expanded = True
|
|
|
|
def contract_request_cb(self, gl, item):
|
|
item.expanded = False
|
|
|
|
def item_expanded_cb(self, gl, item):
|
|
dir_node, d = item.data
|
|
|
|
for key in sorted(d.keys()):
|
|
value = d[key]
|
|
if key == FILE_MARKER:
|
|
if value:
|
|
for fn, fe, n in value:
|
|
gl.item_append(
|
|
ITEM_CLASS_FILES,
|
|
(fn, fe, n),
|
|
item,
|
|
ELM_GENLIST_ITEM_NONE)
|
|
else:
|
|
gl.item_append(
|
|
ITEM_CLASS_DIRS,
|
|
(key, value),
|
|
item,
|
|
ELM_GENLIST_ITEM_TREE)
|
|
|
|
def item_contracted_cb(self, gl, item):
|
|
item.subitems_clear()
|
|
|
|
def item_activated_cb(self, gl, item):
|
|
if item.type != ELM_GENLIST_ITEM_NONE:
|
|
return
|
|
fn, fe, n = item.data
|
|
h = gl.data["handle"]
|
|
progress = h.file_progress()[n]
|
|
|
|
if progress == 0:
|
|
log.error("Tried to open a file with size 0")
|
|
return
|
|
|
|
if progress < fe.size:
|
|
log.warn("Opening an incomplete file")
|
|
|
|
path = os.path.join(h.save_path().rstrip("\0"), fe.path)
|
|
|
|
if sys.platform == 'linux2':
|
|
ecore.Exe('xdg-open %s' % pipes.quote(path))
|
|
else:
|
|
os.startfile(path)
|
|
|
|
|
|
class TorrentInfo(Box):
|
|
def __init__(self, parent, h, *args, **kwargs):
|
|
Box.__init__(self, parent, *args, **kwargs)
|
|
|
|
info = h.get_torrent_info()
|
|
|
|
table = Table(self, homogeneous=True, size_hint_align=FILL_HORIZ)
|
|
|
|
i = 0
|
|
for func in info.comment, info.creation_date, info.creator:
|
|
try:
|
|
w = func()
|
|
except Exception as e:
|
|
log.debug(e)
|
|
else:
|
|
if w:
|
|
l = Label(table, size_hint_align=ALIGN_LEFT)
|
|
l.text = func.__name__.replace("_", " ").capitalize()
|
|
table.pack(l, 0, i, 1, 1)
|
|
l.show()
|
|
v = Label(table, ellipsis=True, size_hint_align=ALIGN_LEFT)
|
|
v.text = html.escape(str(w))
|
|
table.pack(v, 1, i, 1, 1)
|
|
v.show()
|
|
i += 1
|
|
|
|
tpriv = Check(self)
|
|
tpriv.size_hint_align = 0.0, 0.0
|
|
tpriv.text = _("Private")
|
|
tpriv.tooltip_text_set(
|
|
"Whether this torrent is private,<br>"
|
|
"i.e. it should not be distributed on the<br>"
|
|
"trackerless network (the kademlia DHT)."
|
|
)
|
|
tpriv.disabled = True
|
|
tpriv.state = info.priv()
|
|
|
|
fl_btn = Button(self, size_hint_align=ALIGN_LEFT)
|
|
fl_btn.text = "Files ->"
|
|
fl_btn.callback_clicked_add(lambda x: TorrentFiles(self.top_widget, h))
|
|
|
|
self.pack_end(table)
|
|
self.pack_end(tpriv)
|
|
self.pack_end(fl_btn)
|
|
|
|
table.show()
|
|
tpriv.show()
|
|
fl_btn.show()
|
|
|
|
|
|
class TorrentSettings(Box):
|
|
|
|
status_checks = {
|
|
"super_seeding": "super_seeding",
|
|
"sequential_download": "set_sequential_download",
|
|
"upload_mode": "set_upload_mode",
|
|
"share_mode": "set_share_mode",
|
|
"ip_filter_applies": "apply_ip_filter"
|
|
}
|
|
|
|
handle_unit_spinners = {
|
|
"upload_limit": "set_upload_limit",
|
|
"download_limit": "set_download_limit"
|
|
}
|
|
|
|
handle_spinners = {
|
|
"max_uploads": "set_max_uploads",
|
|
"max_connections": "set_max_connections"
|
|
}
|
|
|
|
def __init__(self, parent, handle, *args, **kwargs):
|
|
super(self.__class__, self).__init__(parent, *args, **kwargs)
|
|
|
|
s = handle.status()
|
|
|
|
t = Table(self, size_hint_align=FILL_HORIZ, homogeneous=True)
|
|
|
|
l = Label(t)
|
|
l.size_hint_align = ALIGN_LEFT
|
|
l.text = _("Storage path")
|
|
t.pack(l, 0, 0, 1, 1)
|
|
l.show()
|
|
|
|
fs = FsEntry(t)
|
|
fs.size_hint_align = FILL_HORIZ
|
|
fs.text = _("Select")
|
|
fs.folder_only = True
|
|
fs.expandable = False
|
|
fs.path = handle.save_path()
|
|
|
|
def fs_cb(fs, path):
|
|
if not path:
|
|
return
|
|
|
|
if os.path.isdir(path):
|
|
handle.move_storage(path)
|
|
|
|
fs.callback_file_chosen_add(fs_cb)
|
|
t.pack(fs, 1, 0, 1, 1)
|
|
fs.show()
|
|
|
|
self.pack_end(t)
|
|
t.show()
|
|
|
|
t = Table(self, size_hint_align=FILL_HORIZ, homogeneous=True)
|
|
|
|
#Checks (bool)
|
|
for i, (getter, setter) in enumerate(self.status_checks.items()):
|
|
l = Label(t)
|
|
l.size_hint_align = ALIGN_LEFT
|
|
l.text = getter.replace("_", " ").capitalize()
|
|
t.pack(l, 0, i, 1, 1)
|
|
l.show()
|
|
w = Check(t)
|
|
w.size_hint_align = ALIGN_RIGHT
|
|
w.state = getattr(s, getter)
|
|
setter = getattr(handle, self.status_checks[getter])
|
|
w.callback_changed_add(lambda x, z=setter: z(x.state))
|
|
t.pack(w, 1, i, 1, 1)
|
|
w.show()
|
|
|
|
self.pack_end(t)
|
|
t.show()
|
|
|
|
t = Table(self, size_hint_align=FILL_HORIZ, homogeneous=True)
|
|
|
|
#Unit Spinners (int)
|
|
for i, (getter, setter) in enumerate(
|
|
self.handle_unit_spinners.items()
|
|
):
|
|
l = Label(t)
|
|
l.size_hint_align = ALIGN_LEFT
|
|
l.text = getter.replace("_", " ").capitalize()
|
|
t.pack(l, 0, i, 1, 1)
|
|
l.show()
|
|
w = UnitSpinner(self, "B/s", 1024, UnitSpinner.binary_prefixes)
|
|
w.size_hint_align = FILL_HORIZ
|
|
w.spinner.special_value_add(0, _("disabled"))
|
|
w.set_value(getattr(handle, getter)())
|
|
setter = getattr(handle, self.handle_unit_spinners[getter])
|
|
w.callback_changed_add(lambda x, y, z=setter: z(y))
|
|
t.pack(w, 1, i, 1, 1)
|
|
w.show()
|
|
|
|
self.pack_end(t)
|
|
t.show()
|
|
|
|
t = Table(self, size_hint_align=FILL_HORIZ, homogeneous=True)
|
|
|
|
#Spinners (int)
|
|
for i, (getter, setter) in enumerate(self.handle_spinners.items()):
|
|
l = Label(t)
|
|
l.size_hint_align = ALIGN_LEFT
|
|
l.text = getter.replace("_", " ").capitalize()
|
|
t.pack(l, 0, i, 1, 1)
|
|
l.show()
|
|
w = Spinner(t)
|
|
w.size_hint_align = FILL_HORIZ
|
|
w.min_max = -1.0, 16777215.0
|
|
w.special_value_add(-1.0, "disabled")
|
|
w.value = getattr(handle, getter)()
|
|
setter = getattr(handle, self.handle_spinners[getter])
|
|
w.callback_delay_changed_add(lambda x, z=setter: z(int(x.value)))
|
|
t.pack(w, 1, i, 1, 1)
|
|
w.show()
|
|
|
|
self.pack_end(t)
|
|
t.show()
|
|
|
|
|
|
class TorrentStatus(Table):
|
|
|
|
state_str = (
|
|
_('Queued'), _('Checking'), _('Downloading metadata'), _('Downloading'),
|
|
_('Finished'), _('Seeding'), _('Allocating'), _('Checking resume data')
|
|
)
|
|
|
|
ignored_keys = (
|
|
"progress_ppm", "distributed_copies", "states", "auto_managed"
|
|
)
|
|
|
|
byte_values = (
|
|
"all_time_download", "all_time_upload", "block_size", "total_done",
|
|
"total_wanted", "total_wanted_done", "total_failed_bytes",
|
|
"total_payload_download", "total_payload_upload", "total_download",
|
|
"total_upload", "total_redundant_bytes"
|
|
)
|
|
|
|
byte_transfer_values = (
|
|
"download_rate", "download_payload_rate", "upload_rate",
|
|
"upload_payload_rate"
|
|
)
|
|
|
|
timedelta_values = (
|
|
"active_time", "seeding_time", "time_since_download",
|
|
"time_since_upload", "finished_time"
|
|
)
|
|
|
|
datetime_values = (
|
|
"added_time", "last_seen_complete", "completed_time"
|
|
)
|
|
|
|
def __init__(self, parent, h, *args, **kwargs):
|
|
Table.__init__(self, parent, *args, **kwargs)
|
|
|
|
s = h.status()
|
|
|
|
self.widgets = []
|
|
|
|
i = 0
|
|
|
|
for k in dir(s):
|
|
if k.startswith("__") or k in self.ignored_keys:
|
|
continue
|
|
try:
|
|
v = getattr(s, k)
|
|
except Exception as e:
|
|
log.debug(e)
|
|
continue
|
|
|
|
v = self.convert_value(k, v)
|
|
|
|
w = None
|
|
|
|
if k == "state":
|
|
pass
|
|
elif isinstance(v, lt.torrent_status.states):
|
|
continue
|
|
elif isinstance(v, list):
|
|
continue # TODO
|
|
elif k == "progress":
|
|
w = Progressbar(self)
|
|
w.size_hint_align = FILL_HORIZ
|
|
elif isinstance(v, bool):
|
|
w = Check(self)
|
|
w.size_hint_align = ALIGN_RIGHT
|
|
w.disabled = True
|
|
if not w:
|
|
try:
|
|
w = Label(self)
|
|
w.size_hint_align = ALIGN_RIGHT
|
|
except Exception as e:
|
|
log.debug(e)
|
|
continue
|
|
|
|
w.data["key"] = k
|
|
self.populate(w, v)
|
|
self.widgets.append(w)
|
|
|
|
l = Label(self)
|
|
l.size_hint_align = ALIGN_LEFT
|
|
l.text = str(k).replace("_", " ").capitalize()
|
|
self.pack(l, 0, i, 1, 1)
|
|
l.show()
|
|
|
|
self.pack(w, 1, i, 1, 1)
|
|
w.show()
|
|
|
|
i += 1
|
|
|
|
def update():
|
|
#log.debug("Torrent status TICK")
|
|
s = h.status()
|
|
for w in self.widgets:
|
|
key = w.data["key"]
|
|
v = self.convert_value(key, getattr(s, key))
|
|
self.populate(w, v)
|
|
|
|
return True
|
|
|
|
timer = ecore.Timer(5.0, update)
|
|
self.on_del_add(lambda x: timer.delete())
|
|
self.top_widget.callback_withdrawn_add(lambda x: timer.freeze())
|
|
self.top_widget.callback_iconified_add(lambda x: timer.freeze())
|
|
self.top_widget.callback_normal_add(lambda x: timer.thaw())
|
|
|
|
@staticmethod
|
|
def populate(w, v):
|
|
if isinstance(w, Label):
|
|
w.text = str(v)
|
|
elif isinstance(w, Check):
|
|
w.state = v
|
|
elif isinstance(w, Progressbar):
|
|
w.value = v
|
|
|
|
def convert_value(self, k, v):
|
|
if k == "state":
|
|
v = str(v).replace("_", " ").capitalize()
|
|
elif k in self.datetime_values:
|
|
v = datetime.fromtimestamp(v)
|
|
elif k in self.timedelta_values:
|
|
v = timedelta(seconds=v)
|
|
elif isinstance(v, list):
|
|
pass
|
|
elif isinstance(v, lt.storage_mode_t):
|
|
v = str(v).replace("_", " ").capitalize()
|
|
elif isinstance(v, (int, long, bytes, str)):
|
|
if k in self.byte_values:
|
|
v = intrepr(v)
|
|
if k in self.byte_transfer_values:
|
|
v = intrepr(v) + "/s"
|
|
|
|
return v
|
|
|
|
|
|
class FsEntry(Fileselector, FileselectorEntry):
|
|
|
|
def __init__(self, parent, *args, **kwargs):
|
|
FileselectorEntry.__init__(self, parent, *args, **kwargs)
|