base implementation of profiles support

This commit is contained in:
DYefremov
2019-12-27 23:05:37 +03:00
parent 5aec42548e
commit 614c87cbf3
9 changed files with 396 additions and 200 deletions

View File

@@ -11,7 +11,7 @@ from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import urlopen, HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, build_opener, install_opener
from app.commons import log
from app.commons import log, run_idle
from app.settings import SettingsType
_BQ_FILES_LIST = ("tv", "radio", # enigma 2
@@ -277,12 +277,15 @@ def telnet(host, port=23, user="", password="", timeout=5):
class HttpAPI:
def __init__(self, host, port, user, password):
self._base_url = "http://{}:{}/api/".format(host, port)
init_auth(user, password, self._base_url)
__MAX_WORKERS = 4
def __init__(self, settings):
self._settings = settings
self._base_url = None
self.init()
from concurrent.futures import ThreadPoolExecutor as PoolExecutor
self._executor = PoolExecutor(max_workers=2)
self._executor = PoolExecutor(max_workers=self.__MAX_WORKERS)
def send(self, req_type, ref, callback=print):
url = self._base_url + req_type.value
@@ -295,6 +298,10 @@ class HttpAPI:
future = self._executor.submit(get_json, req_type, url)
future.add_done_callback(lambda f: callback(f.result()))
def init(self):
self._base_url = "http://{}:{}/api/".format(self._settings.host, self._settings.http_port)
init_auth(self._settings.http_user, self._settings.http_password, self._base_url)
def close(self):
self._executor.shutdown(False)

View File

@@ -1,9 +1,10 @@
import copy
import json
import os
from pprint import pformat
from textwrap import dedent
from enum import Enum, IntEnum
from pathlib import Path
from pprint import pformat
from textwrap import dedent
CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/"
CONFIG_FILE = CONFIG_PATH + "config.json"
@@ -25,11 +26,14 @@ class Defaults(Enum):
FAV_CLICK_MODE = 0
def get_default_settings():
def get_default_settings(profile_name="default"):
def_settings = SettingsType.ENIGMA_2.get_default_settings()
set_local_paths(def_settings, profile_name)
return {
"version": 1,
"default_profile": Defaults.DEFAULT_PROFILE.value,
"profiles": {"default": SettingsType.ENIGMA_2.get_default_settings()},
"profiles": {profile_name: def_settings},
"v5_support": Defaults.V5_SUPPORT.value,
"http_api_support": Defaults.HTTP_API_SUPPORT.value,
"enable_yt_dl": Defaults.ENABLE_YT_DL.value,
@@ -41,6 +45,12 @@ def get_default_settings():
}
def set_local_paths(settings, profile_name):
settings["data_local_path"] = "{}{}/".format(settings["data_local_path"], profile_name)
settings["picons_local_path"] = "{}{}/".format(settings["picons_local_path"], profile_name)
settings["backup_local_path"] = "{}{}/".format(settings["backup_local_path"], profile_name)
class SettingsType(IntEnum):
""" Profiles for settings """
ENIGMA_2 = 0
@@ -49,13 +59,13 @@ class SettingsType(IntEnum):
def get_default_settings(self):
""" Returns default settings for current type """
if self is self.ENIGMA_2:
return {"setting_type": self,
return {"setting_type": self.value,
"host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "timeout": 5,
"http_user": "root", "http_password": "", "http_port": "80", "http_timeout": 5,
"telnet_user": "root", "telnet_password": "", "telnet_port": "23", "telnet_timeout": 5,
"services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/",
"satellites_xml_path": "/etc/tuxbox/", "data_local_path": DATA_PATH + "enigma2/",
"picons_path": "/usr/share/enigma2/picon",
"picons_path": "/usr/share/enigma2/picon/",
"picons_local_path": DATA_PATH + "enigma2/picons/",
"backup_local_path": DATA_PATH + "enigma2/backup/"}
elif self is self.NEUTRINO_MP:
@@ -78,8 +88,8 @@ class Settings:
__INSTANCE = None
__VERSION = 1
def __init__(self):
settings = get_settings()
def __init__(self, ext_settings=None):
settings = ext_settings or get_settings()
if self.__VERSION > settings.get("version", 0):
write_settings(get_default_settings())
@@ -112,6 +122,7 @@ class Settings:
def reset(self, force_write=False):
for k, v in self.setting_type.get_default_settings().items():
self._cp_settings[k] = v
set_local_paths(self._cp_settings, self._current_profile)
if force_write:
self.save()
@@ -128,6 +139,33 @@ class Settings:
""" Returns extra options or None """
return self._settings.get(name, None)
@property
def settings(self):
""" Returns copy of the current settings! """
return copy.deepcopy(self._settings)
@settings.setter
def settings(self, value):
""" Sets copy of the settings! """
self._settings = copy.deepcopy(value)
@property
def current_profile(self):
return self._current_profile
@current_profile.setter
def current_profile(self, value):
self._current_profile = value
self._cp_settings = self._profiles.get(self._current_profile)
@property
def default_profile(self):
return self._settings.get("default_profile", "default")
@default_profile.setter
def default_profile(self, value):
self._settings["default_profile"] = value
@property
def profiles(self):
return self._profiles

View File

@@ -192,6 +192,7 @@ def backup_data(path, backup_path, move=True):
"""
backup_path = "{}{}/".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
os.makedirs(os.path.dirname(backup_path), exist_ok=True)
os.makedirs(os.path.dirname(path), exist_ok=True)
# backup files in data dir(skipping dirs and satellites.xml)
for file in filter(lambda f: f != "satellites.xml" and os.path.isfile(os.path.join(path, f)), os.listdir(path)):
src, dst = os.path.join(path, file), backup_path + file

View File

@@ -26,7 +26,7 @@ THE SOFTWARE.
Author: Dmitriy Yefremov
-->
<interface>
<interface domain="demon-editor">
<requires lib="gtk+" version="3.16"/>
<!-- interface-license-type mit -->
<!-- interface-name DemonEditor -->
@@ -47,6 +47,7 @@ Author: Dmitriy Yefremov
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">FTP-transfer</property>
<property name="spacing">5</property>
<property name="show_close_button">True</property>
<child>
@@ -56,7 +57,6 @@ Author: Dmitriy Yefremov
<property name="spacing">2</property>
<child>
<object class="GtkButton" id="receive_button">
<property name="width_request">48</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
@@ -78,7 +78,6 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkButton" id="send_button">
<property name="width_request">48</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
@@ -100,120 +99,13 @@ Author: Dmitriy Yefremov
</child>
</object>
</child>
<child type="title">
<object class="GtkBox" id="header_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">2</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="header_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">FTP-transfer</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="header_data_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">5</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="all_radio_button">
<property name="label" translatable="yes">All</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">satellites_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="bouquets_radio_button">
<property name="label" translatable="yes">Bouquets</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">satellites_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="satellites_radio_button">
<property name="label" translatable="yes">Satellites</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">all_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="webtv_radio_button">
<property name="label" translatable="yes">WebTV</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">all_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="options_button">
<property name="width_request">48</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Options</property>
<signal name="clicked" handler="on_preferences" swapped="no"/>
<signal name="clicked" handler="on_settings" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
@@ -238,13 +130,156 @@ Author: Dmitriy Yefremov
<property name="margin_bottom">1</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkBox" id="selection_data_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="margin_top">10</property>
<property name="margin_bottom">5</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="all_radio_button">
<property name="label" translatable="yes">All</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">satellites_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="bouquets_radio_button">
<property name="label" translatable="yes">Bouquets</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">satellites_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="satellites_radio_button">
<property name="label" translatable="yes">Satellites</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">all_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="webtv_radio_button">
<property name="label" translatable="yes">WebTV</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<property name="group">all_radio_button</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Profile:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="profile_combo_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="focus_on_click">False</property>
<property name="halign">center</property>
<property name="margin_top">1</property>
<property name="margin_bottom">1</property>
<property name="active">0</property>
<property name="has_frame">False</property>
<property name="has_entry">True</property>
<signal name="changed" handler="on_profile_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry">
<property name="can_focus">True</property>
<property name="has_tooltip">True</property>
<property name="halign">center</property>
<property name="margin_top">1</property>
<property name="margin_bottom">1</property>
<property name="editable">False</property>
<property name="has_frame">False</property>
<property name="max_width_chars">9</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="main_settings_box_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="label_xalign">0.019999999552965164</property>
<property name="shadow_type">in</property>
<child>
@@ -253,6 +288,7 @@ Author: Dmitriy Yefremov
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="orientation">vertical</property>
<child>
@@ -319,7 +355,7 @@ Author: Dmitriy Yefremov
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="position">1</property>
</packing>
</child>
<child>
@@ -398,7 +434,7 @@ Author: Dmitriy Yefremov
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="position">2</property>
</packing>
</child>
<child>
@@ -590,7 +626,7 @@ Author: Dmitriy Yefremov
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">3</property>
</packing>
</child>
<child>
@@ -629,7 +665,7 @@ Author: Dmitriy Yefremov
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
<child>
@@ -685,9 +721,12 @@ Author: Dmitriy Yefremov
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
<property name="position">5</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>

View File

@@ -1,3 +1,5 @@
import os
from gi.repository import GLib
from app.commons import run_idle, run_task
@@ -6,8 +8,8 @@ from app.settings import SettingsType
from app.ui.backup import backup_data, restore_data
from app.ui.main_helper import append_text_to_tview
from app.ui.settings_dialog import show_settings_dialog
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
from .dialogs import show_dialog, DialogType, get_message
from .uicommons import Gtk, UI_RESOURCES_PATH
class DownloadDialog:
@@ -20,11 +22,11 @@ class DownloadDialog:
handlers = {"on_receive": self.on_receive,
"on_send": self.on_send,
"on_settings_button": self.on_settings_button,
"on_preferences": self.on_preferences,
"on_settings": self.on_settings,
"on_profile_changed": self.on_profile_changed,
"on_info_bar_close": self.on_info_bar_close}
builder = Gtk.Builder()
builder.set_translation_domain(TEXT_DOMAIN)
builder.add_from_file(UI_RESOURCES_PATH + "download_dialog.glade")
builder.connect_signals(handlers)
@@ -35,7 +37,6 @@ class DownloadDialog:
self._message_label = builder.get_object("info_bar_message_label")
self._text_view = builder.get_object("text_view")
self._expander = builder.get_object("expander")
self._host_entry = builder.get_object("host_entry")
self._data_path_entry = builder.get_object("data_path_entry")
self._remove_unused_check_button = builder.get_object("remove_unused_check_button")
@@ -52,12 +53,19 @@ class DownloadDialog:
self._use_http_switch = builder.get_object("use_http_switch")
self._http_radio_button = builder.get_object("http_radio_button")
self._use_http_box = builder.get_object("use_http_box")
self._profile_combo_box = builder.get_object("profile_combo_box")
self.init_settings()
def show(self):
self._dialog_window.show()
def init_settings(self):
self.update_profiles()
self.init_ui_settings()
@run_idle
def init_ui_settings(self):
self._host_entry.set_text(self._settings.host)
self._data_path_entry.set_text(self._settings.data_local_path)
is_enigma = self._s_type is SettingsType.ENIGMA_2
@@ -66,6 +74,12 @@ class DownloadDialog:
self._use_http_box.set_visible(is_enigma)
self._use_http_switch.set_active(is_enigma)
def update_profiles(self):
self._profile_combo_box.remove_all()
for p in self._settings.profiles:
self._profile_combo_box.append(p, p)
self._profile_combo_box.set_active_id(self._settings.current_profile)
@run_idle
def on_receive(self, item):
self.download(True, self.get_download_type())
@@ -108,11 +122,11 @@ class DownloadDialog:
self._timeout_entry.set_text("")
self._current_property = label
def on_preferences(self, item):
def on_settings(self, item):
response = show_settings_dialog(self._dialog_window, self._settings)
if response != Gtk.ResponseType.CANCEL:
self._s_type = self._settings.setting_type
self.init_settings()
self.update_profiles()
gen = self._update_settings_callback()
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
@@ -121,6 +135,13 @@ class DownloadDialog:
self.on_settings_button(button)
break
def on_profile_changed(self, box):
active = box.get_active_text()
if active in self._settings.profiles:
self._settings.current_profile = active
self._profile_combo_box.set_active_id(active)
self.init_ui_settings()
def on_info_bar_close(self, bar=None, resp=None):
self._info_bar.set_visible(False)
@@ -135,8 +156,10 @@ class DownloadDialog:
if download:
if backup and d_type is not DownloadType.SATELLITES:
data_path = self._settings.data_local_path or self._data_path_entry.get_text()
os.makedirs(os.path.dirname(data_path), exist_ok=True)
backup_path = self._settings.backup_local_path or data_path + "backup/"
backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL)
download_data(settings=self._settings, download_type=d_type, callback=self.append_output)
else:
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)

View File

@@ -1,6 +1,5 @@
import os
import sys
from contextlib import suppress
from functools import lru_cache
from itertools import chain
@@ -21,21 +20,21 @@ from app.tools.media import Player
from app.ui.epg_dialog import EpgDialog
from app.ui.transmitter import LinksTransmitter
from .backup import BackupDialog, backup_data, clear_data_path
from .imports import ImportDialog, import_bouquet
from .download_dialog import DownloadDialog
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog
from .search import SearchProvider
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \
FavClickMode
from .dialogs import show_dialog, DialogType, get_chooser_dialog, WaitDialog, get_message
from .download_dialog import DownloadDialog
from .imports import ImportDialog, import_bouquet
from .iptv import IptvDialog, SearchUnavailableDialog, IptvListConfigurationDialog, YtListImportDialog
from .main_helper import insert_marker, move_items, rename, ViewTarget, set_flags, locate_in_services, \
scroll_to, get_base_model, update_picons_data, copy_picon_reference, assign_picon, remove_picon, \
is_only_one_item_selected, gen_bouquets, BqGenType, get_iptv_url, append_picons, get_selection, get_model_data, \
remove_all_unused_picons
from .picons_downloader import PiconsDialog
from .satellites_dialog import show_satellites_dialog
from .settings_dialog import show_settings_dialog
from .search import SearchProvider
from .service_details_dialog import ServiceDetailsDialog, Action
from .settings_dialog import show_settings_dialog
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column, \
FavClickMode
class Application(Gtk.Application):
@@ -75,7 +74,8 @@ class Application(Gtk.Application):
handlers = {"on_close_app": self.on_close_app,
"on_resize": self.on_resize,
"on_about_app": self.on_about_app,
"on_preferences": self.on_preferences,
"on_settings": self.on_settings,
"on_profile_changed": self.on_profile_changed,
"on_download": self.on_download,
"on_data_open": self.on_data_open,
"on_data_save": self.on_data_save,
@@ -215,7 +215,6 @@ class Application(Gtk.Application):
self._app_info_box.bind_property("visible", builder.get_object("left_header_box"), "sensitive", 4)
# Status bar
self._profile_combo_box = builder.get_object("profile_combo_box")
self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_text() + self._settings.host)
self._receiver_info_box = builder.get_object("receiver_info_box")
self._receiver_info_label = builder.get_object("receiver_info_label")
self._signal_box = builder.get_object("signal_box")
@@ -288,10 +287,11 @@ class Application(Gtk.Application):
def do_startup(self):
Gtk.Application.do_startup(self)
self.update_profile_label()
self.init_profiles()
self.init_drag_and_drop()
self.init_colors()
self.init_http_api()
gen = self.init_http_api()
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
def do_activate(self):
self._main_window.set_application(self)
@@ -315,6 +315,11 @@ class Application(Gtk.Application):
self.activate()
return 0
@run_idle
def init_profiles(self):
self.update_profiles()
self._profile_combo_box.set_active_id(self._settings.default_profile)
def init_drag_and_drop(self):
""" Enable drag-and-drop """
target = []
@@ -807,7 +812,7 @@ class Application(Gtk.Application):
DownloadDialog(transient=self._main_window,
settings=self._settings,
open_data_callback=self.open_data,
update_settings_callback=self.update_options).show()
update_settings_callback=self.update_settings).show()
@run_task
def on_download_data(self):
@@ -1167,25 +1172,46 @@ class Application(Gtk.Application):
for v in [view, *args]:
v.get_selection().unselect_all()
def on_preferences(self, item):
def on_settings(self, item):
response = show_settings_dialog(self._main_window, self._settings)
if response != Gtk.ResponseType.CANCEL:
gen = self.update_options()
gen = self.update_settings()
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
def update_options(self):
profile = self._settings.setting_type
def update_settings(self):
s_type = self._settings.setting_type
if profile != self._s_type:
if s_type != self._s_type:
yield from self.show_app_info(True)
self._s_type = profile
self._s_type = s_type
c_gen = self.clear_current_data()
yield from c_gen
self.update_profile_label()
self.init_colors(True)
self.init_profiles()
yield True
self.init_http_api()
yield True
gen = self.init_http_api()
yield from gen
def on_profile_changed(self, box):
if self._app_info_box.get_visible():
self.update_profile_label()
return
active = box.get_active_text()
if active in self._settings.profiles:
self._settings.current_profile = active
self._s_type = self._settings.setting_type
self._profile_combo_box.set_tooltip_text(self._profile_combo_box.get_tooltip_text() + self._settings.host)
self.update_profile_label()
if self._http_api and self._settings.http_api_support:
self._http_api.init()
def update_profiles(self):
self._profile_combo_box.remove_all()
for p in self._settings.profiles:
self._profile_combo_box.append(p, p)
def on_tree_view_key_press(self, view, event):
""" Handling keystrokes on press """
@@ -1654,7 +1680,7 @@ class Application(Gtk.Application):
self._links_transmitter.hide()
# ************************ HTTP API ****************************#
@run_task
def init_http_api(self):
self._fav_click_mode = FavClickMode(self._settings.fav_click_mode)
http_api_enable = self._settings.http_api_support
@@ -1663,20 +1689,20 @@ class Application(Gtk.Application):
GLib.idle_add(self._http_status_image.set_visible, status)
if self._s_type is SettingsType.NEUTRINO_MP or not http_api_enable:
self.update_info_boxes_visible(False)
GLib.idle_add(self.update_info_boxes_visible, False)
if self._http_api:
self._http_api.close()
yield True
self._http_api = None
self.init_send_to(False)
return
if not self._http_api:
self._http_api = HttpAPI(self._settings.host, self._settings.http_port,
self._settings.http_user, self._settings.http_password)
self._http_api = HttpAPI(self._settings)
GLib.timeout_add_seconds(3, self.update_info, priority=GLib.PRIORITY_LOW)
self.init_send_to(http_api_enable and self._settings.enable_send_to)
yield True
@run_idle
def init_send_to(self, enable):
@@ -2066,6 +2092,7 @@ class Application(Gtk.Application):
# ***************** Profile label *********************#
@run_idle
def update_profile_label(self):
label, sep, ip = self._profile_combo_box.get_tooltip_text().partition(":")
profile_name = self._profile_combo_box.get_active_text()

View File

@@ -512,7 +512,7 @@ Author: Dmitriy Yefremov
<property name="receives_default">True</property>
<property name="margin_bottom">2</property>
<property name="text" translatable="yes">Settings</property>
<signal name="clicked" handler="on_preferences" swapped="no"/>
<signal name="clicked" handler="on_settings" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
@@ -2718,7 +2718,6 @@ Author: Dmitriy Yefremov
<object class="GtkSeparator" id="bottom_separator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">2</property>
</object>
<packing>
<property name="expand">False</property>
@@ -2799,7 +2798,6 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkBox" id="status_bar_box">
<property name="height_request">35</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="receiver_info_box">
@@ -2844,6 +2842,7 @@ Author: Dmitriy Yefremov
</child>
<child type="center">
<object class="GtkComboBoxText" id="profile_combo_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="focus_on_click">False</property>
<property name="tooltip_text" translatable="yes">Current IP:</property>
@@ -2852,22 +2851,21 @@ Author: Dmitriy Yefremov
<property name="margin_top">1</property>
<property name="margin_bottom">1</property>
<property name="active">0</property>
<property name="has_frame">False</property>
<property name="has_entry">True</property>
<items>
<item translatable="yes">default</item>
</items>
<signal name="changed" handler="on_profile_changed" swapped="no"/>
<child internal-child="entry">
<object class="GtkEntry">
<object class="GtkEntry" id="profile_entry">
<property name="can_focus">False</property>
<property name="has_tooltip">True</property>
<property name="halign">center</property>
<property name="halign">baseline</property>
<property name="valign">baseline</property>
<property name="margin_top">1</property>
<property name="margin_bottom">1</property>
<property name="editable">False</property>
<property name="has_frame">False</property>
<property name="max_width_chars">9</property>
<property name="text" translatable="yes">default</property>
<property name="overwrite_mode">True</property>
<property name="caps_lock_warning">False</property>
<property name="primary_icon_stock">gtk-connect</property>
</object>
</child>

View File

@@ -64,6 +64,7 @@ Author: Dmitriy Yefremov
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="gravity">center</property>
<signal name="response" handler="on_response" swapped="no"/>
<child type="titlebar">
<object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property>
@@ -185,6 +186,7 @@ Author: Dmitriy Yefremov
<property name="wide_handle">True</property>
<child>
<object class="GtkFrame" id="profile_frame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
@@ -195,7 +197,6 @@ Author: Dmitriy Yefremov
<property name="width_request">155</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Set as default</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
@@ -212,6 +213,7 @@ Author: Dmitriy Yefremov
<property name="model">profile_lists_tore</property>
<property name="headers_visible">False</property>
<property name="search_column">0</property>
<signal name="cursor-changed" handler="on_profile_selected" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
@@ -265,7 +267,6 @@ Author: Dmitriy Yefremov
<child>
<object class="GtkToolButton" id="profile_add_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Add</property>
<property name="label" translatable="yes">Add</property>
@@ -280,7 +281,6 @@ Author: Dmitriy Yefremov
</child>
<child>
<object class="GtkToolButton" id="profile_edit_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Edit</property>
<property name="label" translatable="yes">Edit</property>
@@ -988,7 +988,6 @@ Author: Dmitriy Yefremov
<object class="GtkEntry" id="picons_dir_field">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text">/data/picons</property>
<property name="primary_icon_stock">gtk-edit</property>
<property name="secondary_icon_name">folder-open</property>
<property name="primary_icon_activatable">False</property>
@@ -1018,7 +1017,6 @@ Author: Dmitriy Yefremov
<object class="GtkEntry" id="data_dir_field">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text">/data</property>
<property name="primary_icon_stock">gtk-edit</property>
<property name="secondary_icon_name">folder-open</property>
<property name="primary_icon_activatable">False</property>
@@ -1047,7 +1045,6 @@ Author: Dmitriy Yefremov
<object class="GtkEntry" id="backup_dir_field">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">/data/backup</property>
<property name="primary_icon_stock">gtk-edit</property>
<property name="secondary_icon_name">folder-open</property>
<property name="primary_icon_activatable">False</property>

View File

@@ -1,10 +1,13 @@
import os
from enum import Enum
from pathlib import Path
from app.commons import run_task, run_idle
from app.connections import test_telnet, test_ftp, TestException, test_http
from app.settings import SettingsType
from app.settings import SettingsType, Settings
from app.ui.dialogs import show_dialog, DialogType
from .main_helper import update_entry_data, scroll_to
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON
from .main_helper import update_entry_data
def show_settings_dialog(transient, options):
@@ -19,10 +22,11 @@ class Property(Enum):
class SettingsDialog:
def __init__(self, transient, settings):
def __init__(self, transient, settings: Settings):
handlers = {"on_field_icon_press": self.on_field_icon_press,
"on_settings_type_changed": self.on_settings_type_changed,
"on_reset": self.on_reset,
"on_response": self.on_response,
"apply_settings": self.apply_settings,
"on_connection_test": self.on_connection_test,
"on_info_bar_close": self.on_info_bar_close,
@@ -36,6 +40,7 @@ class SettingsDialog:
"on_profile_deleted": self.on_profile_deleted,
"on_profile_inserted": self.on_profile_inserted,
"on_profile_edited": self.on_profile_edited,
"on_profile_selected": self.on_profile_selected,
"on_profile_set_default": self.on_profile_set_default}
builder = Gtk.Builder()
@@ -70,7 +75,7 @@ class SettingsDialog:
self._info_bar = builder.get_object("info_bar")
self._message_label = builder.get_object("info_bar_message_label")
self._test_spinner = builder.get_object("test_spinner")
# Profile
# Settings type
self._enigma_radio_button = builder.get_object("enigma_radio_button")
self._neutrino_radio_button = builder.get_object("neutrino_radio_button")
self._support_ver5_switch = builder.get_object("support_ver5_switch")
@@ -97,14 +102,15 @@ class SettingsDialog:
self._extra_support_grid.bind_property("sensitive", builder.get_object("v5_support_grid"), "sensitive")
# Profiles
self._profile_view = builder.get_object("profile_tree_view")
self._profile_add_button = builder.get_object("profile_add_button")
self._profile_remove_button = builder.get_object("profile_remove_button")
self._profile_view.get_model().append(("default", DEFAULT_ICON))
# Settings
self._settings = settings
self._profiles = settings.profiles
self._s_type = settings.setting_type
self._profiles = self._settings.profiles
self._s_type = self._settings.setting_type
self.set_settings()
self.init_ui_elements(self._s_type)
self.init_profiles()
@run_idle
def init_ui_elements(self, s_type):
@@ -116,10 +122,15 @@ class SettingsDialog:
self._extra_support_grid.set_sensitive(is_enigma_profile)
http_active = self._support_http_api_switch.get_active()
self._click_mode_zap_button.set_sensitive(is_enigma_profile and http_active)
self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1)
self.on_info_bar_close() if is_enigma_profile else self.show_info_message(
"The Neutrino has only experimental support. Not all features are supported!", Gtk.MessageType.WARNING)
def init_profiles(self):
p_def = self._settings.default_profile
for p in self._profiles:
self._profile_view.get_model().append((p, DEFAULT_ICON if p == p_def else None))
self._profile_remove_button.set_sensitive(len(self._profile_view.get_model()) > 1)
def update_header_bar(self):
label, sep, st = self._header_bar.get_subtitle().partition(":")
if self._s_type is SettingsType.ENIGMA_2:
@@ -128,12 +139,14 @@ class SettingsDialog:
self._header_bar.set_subtitle("{}: {}".format(label, self._neutrino_radio_button.get_label()))
def show(self):
response = self._dialog.run()
if response == Gtk.ResponseType.OK:
self.apply_settings()
self._dialog.destroy()
self._dialog.run()
return response
def on_response(self, dialog, resp):
if resp == Gtk.ResponseType.OK and not self.apply_settings():
return
self._dialog.destroy()
return resp
def on_field_icon_press(self, entry, icon, event_button):
update_entry_data(entry, self._dialog, self._settings)
@@ -149,6 +162,7 @@ class SettingsDialog:
self._settings.reset()
self.set_settings()
@run_idle
def set_settings(self):
self._host_field.set_text(self._settings.host)
self._port_field.set_text(self._settings.port)
@@ -186,6 +200,9 @@ class SettingsDialog:
self._extra_color_button.set_rgba(extra_rgb)
def apply_settings(self, item=None):
if show_dialog(DialogType.QUESTION, self._dialog) == Gtk.ResponseType.CANCEL:
return
self._s_type = SettingsType.ENIGMA_2 if self._enigma_radio_button.get_active() else SettingsType.NEUTRINO_MP
self._settings.setting_type = self._s_type
self._settings.host = self._host_field.get_text()
@@ -219,7 +236,9 @@ class SettingsDialog:
self._settings.enable_yt_dl = self._enable_y_dl_switch.get_active()
self._settings.enable_send_to = self._enable_send_to_switch.get_active()
self._settings.default_profile = list(filter(lambda r: r[1], self._profile_view.get_model()))[0][0]
self._settings.save()
return True
@run_task
def on_connection_test(self, item):
@@ -300,8 +319,15 @@ class SettingsDialog:
while name in self._profiles:
count += 1
name = "profile{}".format(count)
self._profiles[name] = {"host": self._host_field.get_text()}
itr = model.append((name, None))
self._profiles[name] = self._s_type.get_default_settings()
model.append((name, None))
scroll_to(len(model) - 1, self._profile_view)
self.on_profile_selected(self._profile_view)
p = name + "/"
self._settings.data_local_path += p
self._settings.picons_local_path += p
self._settings.backup_local_path += p
def on_profile_edit(self, item):
self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING)
@@ -310,21 +336,61 @@ class SettingsDialog:
model, paths = self._profile_view.get_selection().get_selected_rows()
if paths:
row = model[paths]
is_default = row[1]
self._profiles.pop(row[0], None)
del model[paths]
if is_default:
model.set_value(model.get_iter_first(), 1, DEFAULT_ICON)
def on_profile_deleted(self, model, paths):
self._profile_remove_button.set_sensitive(len(model) > 1)
def on_profile_edited(self, render, path, new_text):
def on_profile_edited(self, render, path, new_value):
p_name = render.get_property("text")
p_data = self._profiles.pop(p_name, None)
row = self._profile_view.get_model()[path]
row[0] = new_text
self._profiles[new_text] = p_data
p_name = self._profiles.pop(p_name, None)
if p_name:
row = self._profile_view.get_model()[path]
row[0] = new_value
self._profiles[new_value] = p_name
if p_name == self._settings.current_profile:
self._settings.current_profile = new_value
if p_name == self._settings.default_profile:
self._settings.default_profile = new_value
if p_name != new_value:
self.update_local_paths(new_value)
self.on_profile_selected(self._profile_view)
def update_local_paths(self, profile_name):
data_path = self._settings.data_local_path
self._settings.data_local_path = "{}/{}/".format(Path(data_path).parent, profile_name)
if os.path.isdir(data_path):
os.rename(data_path, self._settings.data_local_path)
picons_path = self._settings.picons_local_path
self._settings.picons_local_path = "{}/{}/".format(Path(picons_path).parent, profile_name)
if os.path.isdir(picons_path):
os.rename(picons_path, self._settings.picons_local_path)
backup_path = self._settings.backup_local_path
self._settings.backup_local_path = "{}/{}/".format(Path(self._settings.backup_local_path).parent, profile_name)
if os.path.isdir(backup_path):
os.rename(backup_path, self._settings.backup_local_path)
def on_profile_selected(self, view):
model, paths = self._profile_view.get_selection().get_selected_rows()
if paths:
profile = model.get_value(model.get_iter(paths), 0)
self._settings.current_profile = profile
self.set_settings()
def on_profile_set_default(self, item):
self.show_info_message("Not implemented yet!", Gtk.MessageType.WARNING)
model, paths = self._profile_view.get_selection().get_selected_rows()
if paths:
itr = model.get_iter(paths)
model.foreach(lambda m, p, i: model.set_value(i, 1, None))
model.set_value(itr, 1, DEFAULT_ICON)
self._settings.default_profile = model.get_value(itr, 0)
def on_profile_inserted(self, model, path, itr):
self._profile_remove_button.set_sensitive(len(model) > 1)