diff --git a/app/ui/picons_dialog.glade b/app/ui/picons_dialog.glade index ed136d1f..77da4ce2 100644 --- a/app/ui/picons_dialog.glade +++ b/app/ui/picons_dialog.glade @@ -3,7 +3,7 @@ The MIT License (MIT) -Copyright (c) 2018 Dmitriy Yefremov +Copyright (c) 2018-2019 Dmitriy Yefremov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -31,28 +31,8 @@ Author: Dmitriy Yefremov - + - - True - False - gtk-goto-bottom - - - True - False - network-server-symbolic - - - True - False - gtk-goto-top - - - True - False - gtk-execute - @@ -119,157 +99,76 @@ Author: Dmitriy Yefremov True emblem-photos dialog - + True False + Picons download tool 2 True - - - True - False - True - vertical + + + True + True + Cancel + True + - + True False - 5 - Picons download tool - 0.52999997138977051 + gtk-cancel + + + + + + + + True + False + 2 + + + True + False + True + False + Load providers + center + True + + + + True + False + network-server-symbolic + + - True + False True 0 - + True - False - 10 - 10 - 5 - 2 + False + True + False + Receive picons + center + True + - - gtk-cancel - True - True - True - Cancel - True - True - - - - False - True - 0 - - - - + True False - - - + gtk-goto-bottom - - True - True - 1 - - - - - Receive picons - True - False - False - False - Receive picons for providers - center - image1 - True - - - - False - True - 2 - - - - - Convert - False - False - center - image4 - True - - - False - True - end - 3 - - - - - Load providers - True - False - False - False - Load satellite providers. - center - image2 - True - - - - False - True - 4 - - - - - Send - True - True - True - Transfer to receiver - image3 - True - - - - False - True - end - 4 - - - - - True - False - - - - - - True - True - 6 - @@ -278,7 +177,64 @@ Author: Dmitriy Yefremov 1 + + + True + False + + + False + True + 2 + + + + + True + True + True + Transfer to receiver + True + + + + True + False + gtk-goto-top + + + + + False + True + 3 + + + + 6 + + + + + True + False + Convert + center + True + + + + True + False + gtk-execute + + + + + end + 3 + @@ -286,15 +242,11 @@ Author: Dmitriy Yefremov 640 False - 2 - 2 vertical 2 False - 2 - 2 True spread @@ -304,17 +256,6 @@ Author: Dmitriy Yefremov 0 - - - True - False - - - False - True - 4 - - True @@ -326,8 +267,8 @@ Author: Dmitriy Yefremov False 5 5 - 2 - 2 + 5 + 5 vertical 2 @@ -404,10 +345,47 @@ Author: Dmitriy Yefremov - + True False - Satellite + 2 + + + False + Satellite + + + False + True + 0 + + + + + True + False + 2 + Loading data... + + + False + True + 1 + + + + + True + False + 2 + True + + + False + True + 2 + + @@ -658,7 +636,7 @@ Author: Dmitriy Yefremov 5 5 5 - folder-open-symbolic + folder-open False @@ -988,7 +966,7 @@ Author: Dmitriy Yefremov True True - 8 + 0 @@ -1025,7 +1003,7 @@ Author: Dmitriy Yefremov False True - 13 + 1 @@ -1079,7 +1057,7 @@ Author: Dmitriy Yefremov False True - 14 + 2 diff --git a/app/ui/picons_downloader.py b/app/ui/picons_downloader.py index 6c14b5ea..374093a0 100644 --- a/app/ui/picons_downloader.py +++ b/app/ui/picons_downloader.py @@ -3,7 +3,6 @@ import re import shutil import subprocess import tempfile -import time from gi.repository import GLib, GdkPixbuf @@ -23,8 +22,8 @@ class PiconsDialog: self._sat_positions = sat_positions self._TMP_DIR = tempfile.gettempdir() + "/" self._BASE_URL = "www.lyngsat.com/packages/" - self._PATTERN = re.compile("^https://www\.lyngsat\.com/[\w-]+\.html$") - self._POS_PATTERN = re.compile("^\d+\.\d+[EW]?$") + self._PATTERN = re.compile(r"^https://www\.lyngsat\.com/[\w-]+\.html$") + self._POS_PATTERN = re.compile(r"^\d+\.\d+[EW]?$") self._current_process = None self._terminate = False @@ -71,11 +70,16 @@ class PiconsDialog: self._enigma2_path_button = builder.get_object("enigma2_path_button") self._save_to_button = builder.get_object("save_to_button") self._send_button = builder.get_object("send_button") + self._cancel_button = builder.get_object("cancel_button") self._enigma2_radio_button = builder.get_object("enigma2_radio_button") self._neutrino_mp_radio_button = builder.get_object("neutrino_mp_radio_button") self._resize_no_radio_button = builder.get_object("resize_no_radio_button") self._resize_220_132_radio_button = builder.get_object("resize_220_132_radio_button") self._resize_100_60_radio_button = builder.get_object("resize_100_60_radio_button") + self._satellite_label = builder.get_object("satellite_label") + self._satellite_label.bind_property("visible", builder.get_object("loading_data_label"), "visible", 4) + self._satellite_label.bind_property("visible", builder.get_object("loading_data_spinner"), "visible", 4) + self._cancel_button.bind_property("visible", builder.get_object("header_download_box"), "visible", 4) # style self._style_provider = Gtk.CssProvider() self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css") @@ -88,16 +92,17 @@ class PiconsDialog: self._picons_path = self._properties.get("picons_dir_path", "") self._picons_dir_entry.set_text(self._picons_path) self._enigma2_picons_path = self._picons_path + if profile is Profile.NEUTRINO_MP: self._enigma2_picons_path = options.get(Profile.ENIGMA_2.value).get("picons_dir_path", "") if not len(self._picon_ids) and self._profile is Profile.ENIGMA_2: message = get_message("To automatically set the identifiers for picons,\n" "first load the required services list into the main application window.") self.show_info_message(message, Gtk.MessageType.WARNING) + self._satellite_label.show() def show(self): self._dialog.run() - self._dialog.destroy() def on_satellites_view_realize(self, view): self.get_satellites(view) @@ -105,18 +110,22 @@ class PiconsDialog: @run_task def get_satellites(self, view): sats = SatellitesParser().get_satellites_list(SatelliteSource.LYNGSAT) + if not sats: + self.show_info_message("Getting satellites list error!", Gtk.MessageType.ERROR) gen = self.append_satellites(view.get_model(), sats) GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) def append_satellites(self, model, sats): - for sat in sats: - pos = sat[1] - name = "{} ({})".format(sat[0], pos) - pos = "{}{}".format("-" if pos[-1] == "W" else "", pos[:-1]) - if not self._terminate and model: - if pos in self._sat_positions: - model.append((name, sat[3], pos)) - yield True + try: + for sat in sats: + pos = sat[1] + name, pos = "{} ({})".format(sat[0], pos), "{}{}".format("-" if pos[-1] == "W" else "", pos[:-1]) + + if not self._terminate and model: + if pos in self._sat_positions: + yield model.append((name, sat[3], pos)) + finally: + self._satellite_label.show() def on_satellite_selection(self, view, path, column): model = view.get_model() @@ -125,7 +134,10 @@ class PiconsDialog: @run_idle def on_load_providers(self, item): self._expander.set_expanded(True) + self.on_info_bar_close() + self._cancel_button.show() url = self._url_entry.get_text() + self._current_process = subprocess.Popen(["wget", "-pkP", self._TMP_DIR, url], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -133,24 +145,33 @@ class PiconsDialog: GLib.io_add_watch(self._current_process.stderr, GLib.IO_IN, self.write_to_buffer) model = self._providers_tree_view.get_model() model.clear() - self.update_receive_button_state() self.append_providers(url, model) @run_task def append_providers(self, url, model): self._current_process.wait() - providers = parse_providers(self._TMP_DIR + url[url.find("w"):]) - if providers: - for p in providers: - model.append((self.get_pixbuf(p[0]) if p[0] else TV_ICON, *p[1:])) - self.update_receive_button_state() + try: + self._terminate = False + providers = parse_providers(self._TMP_DIR + url[url.find("w"):]) + except FileNotFoundError: + pass # NOP + else: + if providers: + for p in providers: + if self._terminate: + return + model.append((self.get_pixbuf(p[0]) if p[0] else TV_ICON, *p[1:])) + self.update_receive_button_state() + finally: + GLib.idle_add(self._cancel_button.hide) + self._terminate = False def get_pixbuf(self, img_url): return GdkPixbuf.Pixbuf.new_from_file_at_scale(filename=self._TMP_DIR + "www.lyngsat.com/" + img_url, width=48, height=48, preserve_aspect_ratio=True) - @run_idle def on_receive(self, item): + self._cancel_button.show() self.start_download() @run_task @@ -170,13 +191,19 @@ class PiconsDialog: scroll_to(prv.path, self._providers_tree_view) return - for prv in providers: - if self._terminate: - break - self.process_provider(Provider(*prv)) - self.resize(self._picons_path) - if not self._terminate: + try: + for prv in providers: + if self._terminate: + return + self.process_provider(Provider(*prv)) + + if self._resize_no_radio_button.get_active(): + self.resize(self._picons_path) + self.show_info_message(get_message("Done!"), Gtk.MessageType.INFO) + finally: + GLib.idle_add(self._cancel_button.hide) + self._terminate = False def process_provider(self, prv): url = prv.url @@ -195,17 +222,13 @@ class PiconsDialog: char = fd.read(1) self.append_output(char) return True - else: - return False + return False @run_idle def append_output(self, char): append_text_to_tview(char, self._text_view) def resize(self, path): - if self._resize_no_radio_button.get_active(): - return - self.show_info_message(get_message("Resizing..."), Gtk.MessageType.INFO) command = "mogrify -resize {}! *.png".format( "320x240" if self._resize_220_132_radio_button.get_active() else "100x60").split() @@ -214,18 +237,30 @@ class PiconsDialog: self._current_process.wait() except FileNotFoundError as e: self.show_info_message("Conversion error. " + str(e), Gtk.MessageType.ERROR) - self.on_cancel() + + def on_cancel(self, item=None): + if self.is_task_running() and show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL: + return True + + self.terminate_task() @run_task - def on_cancel(self, item=None): - if self._current_process: - self._terminate = True - self._current_process.terminate() - time.sleep(1) + def terminate_task(self): + self._terminate = True - @run_idle - def on_close(self, item): - self.on_cancel(item) + if self._current_process: + self._current_process.terminate() + self.show_info_message(get_message("The task is canceled!"), Gtk.MessageType.WARNING) + + def on_close(self, window, event): + if self.on_cancel(): + return True + + self.clean_data() + GLib.idle_add(self._dialog.destroy) + + @run_task + def clean_data(self): path = self._TMP_DIR + "www.lyngsat.com" if os.path.exists(path): shutil.rmtree(path) @@ -239,7 +274,7 @@ class PiconsDialog: @run_task def upload_picons(self): - if self._current_process is not None and self._current_process.poll() is None: + if self.is_task_running(): self.show_dialog("The task is already running!", DialogType.ERROR) return @@ -276,7 +311,8 @@ class PiconsDialog: self.update_selection(view, False) def update_selection(self, view, select): - view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 7, select)) + view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 7, select)) + self.update_receive_button_state() def on_url_changed(self, entry): suit = self._PATTERN.search(entry.get_text()) @@ -317,7 +353,10 @@ class PiconsDialog: @run_idle def update_receive_button_state(self): - self._receive_button.set_sensitive(len(self.get_selected_providers()) > 0) + try: + return self._receive_button.set_sensitive(len(self.get_selected_providers()) > 0) + except TypeError: + return False def get_selected_providers(self): """ returns selected providers """ @@ -335,6 +374,9 @@ class PiconsDialog: return picon_format + def is_task_running(self): + return self._current_process and self._current_process.poll() is None + if __name__ == "__main__": pass