From d723ecd7f79ebab37dac40b480a9e0225e5d4fd6 Mon Sep 17 00:00:00 2001 From: Dmitriy Yefremov Date: Sat, 30 Dec 2017 21:51:57 +0300 Subject: [PATCH] Neutrino-MP settings profile skeleton --- README.md | 7 +- app/properties.py | 30 +++++- app/ui/dialogs.glade | 210 +++++++++++++++++++++++++++--------- app/ui/dialogs.py | 5 +- app/ui/main_app_window.py | 14 ++- app/ui/main_window.glade | 37 ++++++- app/ui/satellites_dialog.py | 2 +- app/ui/settings_dialog.py | 87 +++++++++++---- 8 files changed, 302 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 8b5a40dd..01489fc1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # DemonEditor -Enigma2 channel and satellites list editor for GNU/Linux. + +Experimental branch! For support Neutrino-MP. + +Enigma2 channel and satellites list editor for GNU/Linux. Focused on the convenience of working in lists from the keyboard. The mouse is also fully supported (Drag and Drop etc) @@ -15,7 +18,7 @@ Ctrl + H - hide/skip. Ability to import IPTV into bouquet from m3u files! -Tests only on OpenPLi based image with GM 990 Spark Reloaded receiver +Tests only on OpenPLi based image with GM 990 Spark Reloaded receiver in my preferred linux distro (Last Linux Mint 18.* - MATE 64-bit)! Minimum requirements: Python >= 3.5.2 and GTK+ 3 with PyGObject bindings. diff --git a/app/properties.py b/app/properties.py index 09aae814..53edb578 100644 --- a/app/properties.py +++ b/app/properties.py @@ -1,25 +1,35 @@ import json import os +from enum import Enum from pathlib import Path - CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" CONFIG_FILE = CONFIG_PATH + "config.json" DATA_PATH = "data/" +class Profile(Enum): + """ Profiles for settings """ + ENIGMA_2 = "0" + NEUTRINO_MP = "1" + + def get_config(): os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True) if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0: - with open(CONFIG_FILE, "w") as default_config_file: - json.dump(get_default_settings(), default_config_file) + reset_config() with open(CONFIG_FILE, "r") as config_file: return json.load(config_file) +def reset_config(): + with open(CONFIG_FILE, "w") as default_config_file: + json.dump(get_default_settings(), default_config_file) + + def write_config(config): assert isinstance(config, dict) with open(CONFIG_FILE, "w") as config_file: @@ -27,12 +37,22 @@ def write_config(config): def get_default_settings(): - return {"host": "127.0.0.1", "port": "21", + return { + Profile.ENIGMA_2.value: { + "host": "127.0.0.1", "port": "21", "user": "root", "password": "root", "services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/", "satellites_xml_path": "/etc/tuxbox/", - "data_dir_path": DATA_PATH} + "data_dir_path": DATA_PATH}, + Profile.NEUTRINO_MP.value: { + "host": "127.0.0.1", "port": "21", + "user": "root", "password": "root", + "services_path": "/var/tuxbox/config/zapit/", + "user_bouquet_path": "/var/tuxbox/config/zapit/", + "satellites_xml_path": "/var/tuxbox/config/", + "data_dir_path": DATA_PATH}, + "profile": Profile.ENIGMA_2.value} if __name__ == "__main__": diff --git a/app/ui/dialogs.glade b/app/ui/dialogs.glade index 2e0ada2d..5c356754 100644 --- a/app/ui/dialogs.glade +++ b/app/ui/dialogs.glade @@ -9,7 +9,7 @@ system-help normal DemonEditor - 0.1.2 Pre-alpha + 0.2.0 Pre-alpha 2017 Dmitriy Yefremov dmitry.v.yefremov@gmail.com @@ -613,7 +613,10 @@ dmitry.v.yefremov@gmail.com False + 2 + 2 5 + 2 vertical 2 @@ -628,11 +631,12 @@ dmitry.v.yefremov@gmail.com True True True + True True True - 2 + 0 @@ -642,6 +646,7 @@ dmitry.v.yefremov@gmail.com True True True + True True @@ -733,7 +738,7 @@ dmitry.v.yefremov@gmail.com True True root - emblem-personal + avatar-default-symbolic False @@ -748,7 +753,7 @@ dmitry.v.yefremov@gmail.com False root - emblem-nowrite + emblem-readonly False password @@ -778,74 +783,177 @@ dmitry.v.yefremov@gmail.com - + True False - True - + True False - Services and Bouquets files: + True + + + True + False + Services and Bouquets files: + + + 0 + 0 + + + + + True + True + /etc/enigma2/ + + + 0 + 1 + + + + + True + False + User bouquet files: + + + 0 + 2 + + + + + True + True + /etc/enigma2/ + + + 0 + 3 + + + + + True + False + Satellites.xml file: + + + 0 + 4 + + + + + True + True + /etc/tuxbox/ + + + 0 + 5 + + - 0 - 0 + True + True + 0 - - True - True - /etc/enigma2/ - - - 0 - 1 - - - - + True False - User bouquet files: + 5 + 5 + vertical - 0 - 2 + False + True + 1 - - True - True - /etc/enigma2/ - - - 0 - 3 - - - - + True False - Satellites.xml file: + 5 + vertical + + + True + False + Active profile: + 0 + + + False + True + 0 + + + + + Enigma2 + True + True + False + 0 + True + True + neutrino_radio_button + + + + False + True + 1 + + + + + Neutrino-MP +(experimental) + True + True + False + 0 + True + True + enigma_radio_button + + + False + True + 2 + + + + + Reset profile + True + True + True + 0.49000000953674316 + + + + False + True + end + 3 + + - 0 - 4 - - - - - True - True - /etc/tuxbox/ - - - 0 - 5 + False + True + 2 diff --git a/app/ui/dialogs.py b/app/ui/dialogs.py index d8a27094..784673d5 100644 --- a/app/ui/dialogs.py +++ b/app/ui/dialogs.py @@ -25,11 +25,12 @@ def show_dialog(dialog_type: DialogType, transient, text=None, options=None, act dialog.set_action(action_type) if file_filter is not None: dialog.add_filter(file_filter) - dialog.set_current_folder(options["data_dir_path"]) + + path = options.get(options.get("profile")).get("data_dir_path") + dialog.set_current_folder(path) response = dialog.run() if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed - path = options["data_dir_path"] if dialog.get_filename(): path = dialog.get_filename() if action_type is not Gtk.FileChooserAction.OPEN: diff --git a/app/ui/main_app_window.py b/app/ui/main_app_window.py index ffad5feb..d212730d 100644 --- a/app/ui/main_app_window.py +++ b/app/ui/main_app_window.py @@ -7,7 +7,7 @@ from app.eparser import get_blacklist, write_blacklist, parse_m3u from app.eparser import get_channels, get_bouquets, write_bouquets, write_channels, Bouquets, Bouquet, Channel from app.eparser.__constants import CAS, FLAG from app.eparser.bouquets import BqServiceType -from app.properties import get_config, write_config +from app.properties import get_config, write_config, Profile from . import Gtk, Gdk, UI_RESOURCES_PATH from .dialogs import show_dialog, DialogType from .download_dialog import show_download_dialog @@ -83,6 +83,7 @@ class MainAppWindow: "on_locate_in_services": self.on_locate_in_services} self.__options = get_config() + self.__profile = self.__options.get("profile") # Used for copy/paste. When adding the previous data will not be deleted. # Clearing only after the insertion! self.__rows_buffer = [] @@ -105,7 +106,9 @@ class MainAppWindow: self.__services_model = builder.get_object("services_list_store") self.__bouquets_model = builder.get_object("bouquets_tree_store") self.__status_bar = builder.get_object("status_bar") - self.__status_bar.push(0, "Current IP: " + self.__options["host"]) + self.__profile_label = builder.get_object("profile_label") + self.__status_bar.push(0, "Current IP: " + self.__options.get(self.__profile).get("host")) + # dynamically active elements depending on the selected view self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS} self.__cas_label = builder.get_object("cas_label") @@ -581,8 +584,11 @@ class MainAppWindow: v.get_selection().unselect_all() def on_preferences(self, item): - show_settings_dialog(self.__main_window, self.__options) - self.__status_bar.push(0, "Current IP: " + self.__options["host"]) + response = show_settings_dialog(self.__main_window, self.__options) + if response != Gtk.ResponseType.CANCEL: + profile = self.__options.get("profile") + self.__status_bar.push(0, "Current IP: " + self.__options.get(profile).get("host")) + self.__profile_label.set_text("Enigma 2 v.4" if Profile(profile) is Profile.ENIGMA_2 else "Neutrino-MP") def on_tree_view_key_release(self, view, event): """ Handling keystrokes """ diff --git a/app/ui/main_window.glade b/app/ui/main_window.glade index 274639f5..46fafcf7 100644 --- a/app/ui/main_window.glade +++ b/app/ui/main_window.glade @@ -1660,13 +1660,46 @@ 2 True - + + True + False + 2 + + + True + False + Profile: + + + False + True + 0 + + + + + True + False + Enigma 2 v.4 + + + False + True + 1 + + + + + False + False + 1 + True False - Ver. 0.1.2 Pre-alpha + Ver. 0.2.0 Pre-alpha 0.94999998807907104 diff --git a/app/ui/satellites_dialog.py b/app/ui/satellites_dialog.py index 8e9d5c64..4b9677c8 100644 --- a/app/ui/satellites_dialog.py +++ b/app/ui/satellites_dialog.py @@ -20,7 +20,7 @@ class SatellitesDialog: _aggr = [None for x in range(9)] # aggregate def __init__(self, transient, options): - self._data_path = options["data_dir_path"] + "satellites.xml" + self._data_path = options.get(options.get("profile")).get("data_dir_path") + "satellites.xml" self._options = options handlers = {"on_open": self.on_open, diff --git a/app/ui/settings_dialog.py b/app/ui/settings_dialog.py index 85e0f7f6..8d545dda 100644 --- a/app/ui/settings_dialog.py +++ b/app/ui/settings_dialog.py @@ -1,55 +1,96 @@ -from app.properties import write_config +from app.properties import write_config, Profile, get_default_settings from app.ui.dialogs import show_dialog, DialogType from . import Gtk, UI_RESOURCES_PATH def show_settings_dialog(transient, options): - SettingsDialog(transient, options) + return SettingsDialog(transient, options).show() class SettingsDialog: def __init__(self, transient, options): - handlers = {"on_data_dir_field_icon_press": self.on_data_dir_field_icon_press} + handlers = {"on_data_dir_field_icon_press": self.on_data_dir_field_icon_press, + "on_profile_changed": self.on_profile_changed, + "on_reset": self.on_reset} builder = Gtk.Builder() - builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("settings_dialog", )) + builder.add_objects_from_file(UI_RESOURCES_PATH + "dialogs.glade", ("settings_dialog",)) builder.connect_signals(handlers) - self._options = options + self._dialog = builder.get_object("settings_dialog") self._dialog.set_transient_for(transient) self._host_field = builder.get_object("host_field") - self._host_field.set_text(options["host"]) self._port_field = builder.get_object("port_field") - self._port_field.set_text(options["port"]) self._login_field = builder.get_object("login_field") - self._login_field.set_text(options["user"]) self._password_field = builder.get_object("password_field") - self._password_field.set_text(options["password"]) self._services_field = builder.get_object("services_field") - self._services_field.set_text(options["services_path"]) self._user_bouquet_field = builder.get_object("user_bouquet_field") - self._user_bouquet_field.set_text(options["user_bouquet_path"]) self._satellites_xml_field = builder.get_object("satellites_xml_field") - self._satellites_xml_field.set_text(options["satellites_xml_path"]) self._data_dir_field = builder.get_object("data_dir_field") - self._data_dir_field.set_text(options["data_dir_path"]) + self._enigma_radio_button = builder.get_object("enigma_radio_button") + self._neutrino_radio_button = builder.get_object("neutrino_radio_button") - if self._dialog.run() == Gtk.ResponseType.OK: - options["host"] = self._host_field.get_text() - options["port"] = self._port_field.get_text() - options["user"] = self._login_field.get_text() - options["password"] = self._password_field.get_text() - options["services_path"] = self._services_field.get_text() - options["user_bouquet_path"] = self._user_bouquet_field.get_text() - options["satellites_xml_path"] = self._satellites_xml_field.get_text() - options["data_dir_path"] = self._data_dir_field.get_text() - write_config(options) + self._options = options + self._active_profile = options.get("profile") + self.set_settings() + self._neutrino_radio_button.set_active(Profile(self._active_profile) is Profile.NEUTRINO_MP) + + def show(self): + response = self._dialog.run() + if response == Gtk.ResponseType.OK: + self.apply_settings() + write_config(self._options) self._dialog.destroy() + return response + def on_data_dir_field_icon_press(self, entry, icon, event_button): response = show_dialog(dialog_type=DialogType.CHOOSER, transient=self._dialog, options=self._options) if response != Gtk.ResponseType.CANCEL: entry.set_text(response) + def on_profile_changed(self, item): + self.set_profile(Profile.ENIGMA_2 if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP) + + def set_profile(self, profile): + self._active_profile = profile.value + self.set_settings() + + def on_reset(self, item): + def_settings = get_default_settings() + for key in def_settings: + current = self._options.get(key) + if type(current) is str: + continue + default = def_settings.get(key) + for k in default: + current[k] = default.get(k) + self.set_settings() + + def set_settings(self): + options = self._options.get(self._active_profile) + self._host_field.set_text(options.get("host")) + self._port_field.set_text(options.get("port")) + self._login_field.set_text(options.get("user")) + self._password_field.set_text(options.get("password")) + self._services_field.set_text(options.get("services_path")) + self._user_bouquet_field.set_text(options.get("user_bouquet_path")) + self._satellites_xml_field.set_text(options.get("satellites_xml_path")) + self._data_dir_field.set_text(options.get("data_dir_path")) + + def apply_settings(self): + profile = Profile.ENIGMA_2.value if self._enigma_radio_button.get_active() else Profile.NEUTRINO_MP.value + self._active_profile = profile + self._options["profile"] = profile + options = self._options.get(self._active_profile) + options["host"] = self._host_field.get_text() + options["port"] = self._port_field.get_text() + options["user"] = self._login_field.get_text() + options["password"] = self._password_field.get_text() + options["services_path"] = self._services_field.get_text() + options["user_bouquet_path"] = self._user_bouquet_field.get_text() + options["satellites_xml_path"] = self._satellites_xml_field.get_text() + options["data_dir_path"] = self._data_dir_field.get_text() + if __name__ == "__main__": pass