lbry-sdk/lbrynet/lbrynet_downloader_gui/StreamFrame.py
Jimmy Kiselak 192ac6959a more specific exceptions, and change when blobs are deleted
More specific exceptions are raised when problems are encountered
looking up metadata and validating stream descriptor files, and
on the GUI those more specific exceptions are used to prevent
errors from being presented to the user.

If the user has selected the option to re-upload data for some
stream, blobs downloaded for that purpose will not be deleted
when they are finished being output. Instead, by default, for
the GUI they will be deleted when the stream is removed from
the GUI. That can be changed so they are not deleted at all,
using the lbry.conf file.
2015-09-01 17:49:26 -04:00

398 lines
No EOL
16 KiB
Python

import Tkinter as tk
import sys
import tkFont
import ttk
import locale
import os
class StreamFrame(object):
def __init__(self, app, uri):
self.app = app
self.uri = uri
self.stream_hash = None
self.cancel_func = None
self.stream_frame = ttk.Frame(self.app.streams_frame, style="B.TFrame")
self.stream_frame.pack(fill=tk.X, side=tk.BOTTOM, pady=(30, 0))
self.stream_frame_header = ttk.Frame(self.stream_frame, style="C.TFrame")
self.stream_frame_header.grid(sticky=tk.E + tk.W)
self.uri_font = tkFont.Font(size=8)
self.uri_label = ttk.Label(
self.stream_frame_header, text=self.uri, font=self.uri_font, foreground="#666666"
)
self.uri_label.grid(row=0, column=0, sticky=tk.W)
if os.name == "nt":
self.button_cursor = ""
else:
self.button_cursor = "hand1"
close_file_name = "close2.gif"
if os.name == "nt":
close_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "lbrynet",
"lbrynet_downloader_gui", close_file_name)
else:
close_file = os.path.join(os.path.dirname(__file__), close_file_name)
self.close_picture = tk.PhotoImage(
file=close_file
)
self.close_button = ttk.Button(
self.stream_frame_header, command=self.cancel, style="Stop.TButton", cursor=self.button_cursor
)
self.close_button.config(image=self.close_picture)
self.close_button.grid(row=0, column=1, sticky=tk.E + tk.N)
self.stream_frame_header.grid_columnconfigure(0, weight=1)
self.stream_frame.grid_columnconfigure(0, weight=1)
self.stream_frame_body = ttk.Frame(self.stream_frame, style="C.TFrame")
self.stream_frame_body.grid(row=1, column=0, sticky=tk.E + tk.W)
self.name_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame")
self.name_frame.grid(sticky=tk.W + tk.E)
self.name_frame.grid_columnconfigure(0, weight=1)
self.stream_frame_body.grid_columnconfigure(0, weight=1)
self.metadata_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame")
self.metadata_frame.grid(sticky=tk.W + tk.E, row=1)
self.metadata_frame.grid_columnconfigure(0, weight=1)
self.options_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame")
self.outer_button_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame")
self.outer_button_frame.grid(sticky=tk.W + tk.E, row=4)
show_options_picture_file_name = "show_options.gif"
if os.name == "nt":
show_options_picture_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
"lbrynet", "lbrynet_downloader_gui",
show_options_picture_file_name)
else:
show_options_picture_file = os.path.join(os.path.dirname(__file__),
show_options_picture_file_name)
self.show_options_picture = tk.PhotoImage(
file=show_options_picture_file
)
hide_options_picture_file_name = "hide_options.gif"
if os.name == "nt":
hide_options_picture_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),
"lbrynet", "lbrynet_downloader_gui",
hide_options_picture_file_name)
else:
hide_options_picture_file = os.path.join(os.path.dirname(__file__),
hide_options_picture_file_name)
self.hide_options_picture = tk.PhotoImage(
file=hide_options_picture_file
)
self.show_options_button = None
self.status_label = None
self.name_label = None
self.bytes_downloaded_label = None
self.button_frame = None
self.download_buttons = []
self.option_frames = []
self.name_font = None
self.description_label = None
self.file_name_frame = None
self.cost_frame = None
self.cost_description = None
self.remaining_cost_description = None
self.cost_label = None
self.remaining_cost_label = None
self.progress_frame = None
def cancel(self):
if self.cancel_func is not None:
self.cancel_func()
self.stream_frame.destroy()
self.app.stream_removed()
def show_name(self, name):
self.name_font = tkFont.Font(size=16)
self.name_label = ttk.Label(
self.name_frame, text=name, font=self.name_font
)
self.name_label.grid(row=0, column=0, sticky=tk.W)
def show_description(self, description):
if os.name == "nt":
wraplength = 580
else:
wraplength = 600
self.description_label = ttk.Label(
self.name_frame, text=description, wraplength=wraplength
)
self.description_label.grid(row=1, column=0, sticky=tk.W)
def show_metadata_status(self, value):
if self.status_label is None:
self.status_label = ttk.Label(
self.metadata_frame, text=value
)
self.status_label.grid()
self.metadata_frame.grid_columnconfigure(0, weight=1)
else:
self.status_label.config(text=value)
@staticmethod
def get_formatted_stream_size(stream_size):
if isinstance(stream_size, (int, long)):
if stream_size >= 2**40:
units = "TB"
factor = 2**40
elif stream_size >= 2**30:
units = "GB"
factor = 2**30
elif stream_size >= 2**20:
units = "MB"
factor = 2**20
elif stream_size >= 2**10:
units = "KB"
factor = 2**10
else:
return str(stream_size) + " B"
return "%.1f %s" % (round((stream_size * 1.0 / factor), 1), units)
return stream_size
def show_stream_metadata(self, stream_name, stream_size):
if self.status_label is not None:
self.status_label.destroy()
self.file_name_frame = ttk.Frame(self.metadata_frame, style="F.TFrame")
self.file_name_frame.grid(row=0, column=0, sticky=tk.W)
self.metadata_frame.grid_columnconfigure(0, weight=1, uniform="metadata")
file_size_label = ttk.Label(
self.file_name_frame,
text=self.get_formatted_stream_size(stream_size)
)
file_size_label.grid(row=0, column=2)
file_name_label = ttk.Label(
self.file_name_frame,
text=" - " + stream_name,
)
file_name_label.grid(row=0, column=3)
self.button_frame = ttk.Frame(self.outer_button_frame, style="E.TFrame")
self.button_frame.grid(row=0, column=1)
self.outer_button_frame.grid_columnconfigure(0, weight=1, uniform="buttons")
self.outer_button_frame.grid_columnconfigure(1, weight=2, uniform="buttons1")
self.outer_button_frame.grid_columnconfigure(2, weight=1, uniform="buttons")
def add_download_factory(self, factory, download_func):
download_button = ttk.Button(
self.button_frame, text=factory.get_description(), command=download_func,
style='LBRY.TButton', cursor=self.button_cursor
)
self.download_buttons.append(download_button)
download_button.grid(row=0, column=len(self.download_buttons) - 1, padx=5, pady=(1, 2))
def disable_download_buttons(self):
for download_button in self.download_buttons:
download_button.config(state=tk.DISABLED)
def remove_download_buttons(self):
for download_button in self.download_buttons:
download_button.destroy()
self.download_buttons = []
def get_option_widget(self, option_type, option_frame):
if option_type.value == float:
entry_frame = ttk.Frame(
option_frame,
style="H.TFrame"
)
entry_frame.grid()
col = 0
if option_type.short_description is not None:
entry_label = ttk.Label(
entry_frame,
#text=option_type.short_description
text=""
)
entry_label.grid(row=0, column=0, sticky=tk.W)
col = 1
entry = ttk.Entry(
entry_frame,
width=10,
style="Float.TEntry"
)
entry_frame.entry = entry
entry.grid(row=0, column=col, sticky=tk.W)
return entry_frame
if option_type.value == bool:
bool_frame = ttk.Frame(
option_frame,
style="H.TFrame"
)
bool_frame.chosen_value = tk.BooleanVar()
true_text = "True"
false_text = "False"
if option_type.bool_options_description is not None:
true_text, false_text = option_type.bool_options_description
true_radio_button = ttk.Radiobutton(
bool_frame, text=true_text, variable=bool_frame.chosen_value, value=True
)
true_radio_button.grid(row=0, sticky=tk.W)
false_radio_button = ttk.Radiobutton(
bool_frame, text=false_text, variable=bool_frame.chosen_value, value=False
)
false_radio_button.grid(row=1, sticky=tk.W)
return bool_frame
label = ttk.Label(
option_frame,
text=""
)
return label
def show_download_options(self, options):
left_padding = 20
for option in options:
f = ttk.Frame(
self.options_frame,
style="E.TFrame"
)
f.grid(sticky=tk.W + tk.E, padx=left_padding)
self.option_frames.append((option, f))
description_label = ttk.Label(
f,
text=option.long_description
)
description_label.grid(row=0, sticky=tk.W)
if len(option.option_types) > 1:
f.chosen_type = tk.IntVar()
choices_frame = ttk.Frame(
f,
style="F.TFrame"
)
f.choices_frame = choices_frame
choices_frame.grid(row=1, sticky=tk.W, padx=left_padding)
choices_frame.choices = []
for i, option_type in enumerate(option.option_types):
choice_frame = ttk.Frame(
choices_frame,
style="G.TFrame"
)
choice_frame.grid(sticky=tk.W)
option_text = ""
if option_type.short_description is not None:
option_text = option_type.short_description
option_radio_button = ttk.Radiobutton(
choice_frame, text=option_text, variable=f.chosen_type, value=i
)
option_radio_button.grid(row=0, column=0, sticky=tk.W)
option_widget = self.get_option_widget(option_type, choice_frame)
option_widget.grid(row=0, column=1, sticky=tk.W)
choices_frame.choices.append(option_widget)
if i == 0:
option_radio_button.invoke()
else:
choice_frame = ttk.Frame(
f,
style="F.TFrame"
)
choice_frame.grid(sticky=tk.W, padx=left_padding)
option_widget = self.get_option_widget(option.option_types[0], choice_frame)
option_widget.grid(row=0, column=0, sticky=tk.W)
f.option_widget = option_widget
self.show_options_button = ttk.Button(
self.stream_frame_body, command=self._toggle_show_options, style="Stop.TButton",
cursor=self.button_cursor
)
self.show_options_button.config(image=self.show_options_picture)
self.show_options_button.grid(sticky=tk.W, row=2, column=0)
def _get_chosen_option(self, option_type, option_widget):
if option_type.value == float:
return float(option_widget.entry.get())
if option_type.value == bool:
return option_widget.chosen_value.get()
return option_type.value
def get_chosen_options(self):
chosen_options = []
for o, f in self.option_frames:
if len(o.option_types) > 1:
chosen_index = f.chosen_type.get()
option_type = o.option_types[chosen_index]
option_widget = f.choices_frame.choices[chosen_index]
chosen_options.append(self._get_chosen_option(option_type, option_widget))
else:
option_type = o.option_types[0]
option_widget = f.option_widget
chosen_options.append(self._get_chosen_option(option_type, option_widget))
return chosen_options
def _toggle_show_options(self):
if self.options_frame.winfo_ismapped():
self.show_options_button.config(image=self.show_options_picture)
self.options_frame.grid_forget()
else:
self.show_options_button.config(image=self.hide_options_picture)
self.options_frame.grid(sticky=tk.W + tk.E, row=3)
def show_progress(self, total_bytes, bytes_left_to_download, points_paid,
points_remaining):
if self.bytes_downloaded_label is None:
self.remove_download_buttons()
self.button_frame.destroy()
for option, frame in self.option_frames:
frame.destroy()
self.options_frame.destroy()
self.show_options_button.destroy()
self.progress_frame = ttk.Frame(self.outer_button_frame, style="F.TFrame")
self.progress_frame.grid(row=0, column=0, sticky=tk.W, pady=(0, 8))
self.bytes_downloaded_label = ttk.Label(
self.progress_frame,
text=""
)
self.bytes_downloaded_label.grid(row=0, column=0)
self.cost_frame = ttk.Frame(self.outer_button_frame, style="F.TFrame")
self.cost_frame.grid(row=1, column=0, sticky=tk.W, pady=(0, 7))
self.cost_label = ttk.Label(
self.cost_frame,
text="",
foreground="red"
)
self.cost_label.grid(row=0, column=1, padx=(1, 0))
self.outer_button_frame.grid_columnconfigure(2, weight=0, uniform="")
if self.bytes_downloaded_label.winfo_exists():
percent_done = 0
if total_bytes > 0:
percent_done = 100.0 * (total_bytes - bytes_left_to_download) / total_bytes
percent_done_string = locale.format_string(" (%.2f%%)", percent_done)
self.bytes_downloaded_label.config(
text=self.get_formatted_stream_size(total_bytes - bytes_left_to_download) + percent_done_string
)
if self.cost_label.winfo_exists():
total_points = points_remaining + points_paid
self.cost_label.config(text=locale.format_string("%.2f/%.2f LBC",
(round(points_paid, 2), round(total_points, 2)),
grouping=True))
def show_download_done(self, total_points_paid):
if self.bytes_downloaded_label is not None and self.bytes_downloaded_label.winfo_exists():
self.bytes_downloaded_label.destroy()
if self.cost_label is not None and self.cost_label.winfo_exists():
self.cost_label.config(text=locale.format_string("%.2f LBC",
(round(total_points_paid, 2),),
grouping=True))