Neutrino-MP settings profile skeleton

This commit is contained in:
Dmitriy Yefremov
2017-12-30 21:51:57 +03:00
parent 37d4cbe1f4
commit d723ecd7f7
8 changed files with 302 additions and 90 deletions

View File

@@ -1,4 +1,7 @@
# DemonEditor # DemonEditor
Experimental branch! For support Neutrino-MP.
Enigma2 channel and satellites list editor for GNU/Linux. Enigma2 channel and satellites list editor for GNU/Linux.
Focused on the convenience of working in lists from the keyboard. Focused on the convenience of working in lists from the keyboard.
The mouse is also fully supported (Drag and Drop etc) The mouse is also fully supported (Drag and Drop etc)

View File

@@ -1,25 +1,35 @@
import json import json
import os import os
from enum import Enum
from pathlib import Path from pathlib import Path
CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/" CONFIG_PATH = str(Path.home()) + "/.config/demon-editor/"
CONFIG_FILE = CONFIG_PATH + "config.json" CONFIG_FILE = CONFIG_PATH + "config.json"
DATA_PATH = "data/" DATA_PATH = "data/"
class Profile(Enum):
""" Profiles for settings """
ENIGMA_2 = "0"
NEUTRINO_MP = "1"
def get_config(): def get_config():
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True) # create dir if not exist 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) 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: if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
with open(CONFIG_FILE, "w") as default_config_file: reset_config()
json.dump(get_default_settings(), default_config_file)
with open(CONFIG_FILE, "r") as config_file: with open(CONFIG_FILE, "r") as config_file:
return json.load(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): def write_config(config):
assert isinstance(config, dict) assert isinstance(config, dict)
with open(CONFIG_FILE, "w") as config_file: with open(CONFIG_FILE, "w") as config_file:
@@ -27,12 +37,22 @@ def write_config(config):
def get_default_settings(): 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", "user": "root", "password": "root",
"services_path": "/etc/enigma2/", "services_path": "/etc/enigma2/",
"user_bouquet_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/",
"satellites_xml_path": "/etc/tuxbox/", "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__": if __name__ == "__main__":

View File

@@ -9,7 +9,7 @@
<property name="icon_name">system-help</property> <property name="icon_name">system-help</property>
<property name="type_hint">normal</property> <property name="type_hint">normal</property>
<property name="program_name">DemonEditor</property> <property name="program_name">DemonEditor</property>
<property name="version">0.1.2 Pre-alpha</property> <property name="version">0.2.0 Pre-alpha</property>
<property name="copyright" translatable="yes">2017 Dmitriy Yefremov <property name="copyright" translatable="yes">2017 Dmitriy Yefremov
dmitry.v.yefremov@gmail.com dmitry.v.yefremov@gmail.com
</property> </property>
@@ -613,7 +613,10 @@ dmitry.v.yefremov@gmail.com
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox3"> <object class="GtkBox" id="dialog-vbox3">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">2</property>
<property name="margin_right">2</property>
<property name="margin_top">5</property> <property name="margin_top">5</property>
<property name="margin_bottom">2</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">2</property> <property name="spacing">2</property>
<child internal-child="action_area"> <child internal-child="action_area">
@@ -628,11 +631,12 @@ dmitry.v.yefremov@gmail.com
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">2</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -642,6 +646,7 @@ dmitry.v.yefremov@gmail.com
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="use_stock">True</property> <property name="use_stock">True</property>
<property name="always_show_image">True</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -733,7 +738,7 @@ dmitry.v.yefremov@gmail.com
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="text" translatable="yes">root</property> <property name="text" translatable="yes">root</property>
<property name="primary_icon_name">emblem-personal</property> <property name="primary_icon_name">avatar-default-symbolic</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
</object> </object>
<packing> <packing>
@@ -748,7 +753,7 @@ dmitry.v.yefremov@gmail.com
<property name="visibility">False</property> <property name="visibility">False</property>
<property name="invisible_char">●</property> <property name="invisible_char">●</property>
<property name="text" translatable="yes">root</property> <property name="text" translatable="yes">root</property>
<property name="primary_icon_name">emblem-nowrite</property> <property name="primary_icon_name">emblem-readonly</property>
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="input_purpose">password</property> <property name="input_purpose">password</property>
</object> </object>
@@ -777,6 +782,10 @@ dmitry.v.yefremov@gmail.com
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child> <child>
<object class="GtkGrid" id="grid2"> <object class="GtkGrid" id="grid2">
<property name="visible">True</property> <property name="visible">True</property>
@@ -849,6 +858,105 @@ dmitry.v.yefremov@gmail.com
</packing> </packing>
</child> </child>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator" id="separator5">
<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="orientation">vertical</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_right">5</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Active profile:</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="enigma_radio_button">
<property name="label" translatable="yes">Enigma2 </property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">neutrino_radio_button</property>
<signal name="toggled" handler="on_profile_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="neutrino_radio_button">
<property name="label" translatable="yes">Neutrino-MP
(experimental)</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">enigma_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="GtkButton" id="reset_button">
<property name="label" translatable="yes">Reset profile</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="xalign">0.49000000953674316</property>
<signal name="clicked" handler="on_reset" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>

View File

@@ -25,11 +25,12 @@ def show_dialog(dialog_type: DialogType, transient, text=None, options=None, act
dialog.set_action(action_type) dialog.set_action(action_type)
if file_filter is not None: if file_filter is not None:
dialog.add_filter(file_filter) 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() response = dialog.run()
if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed if response == -12: # -12 for fix assertion 'gtk_widget_get_can_default (widget)' failed
path = options["data_dir_path"]
if dialog.get_filename(): if dialog.get_filename():
path = dialog.get_filename() path = dialog.get_filename()
if action_type is not Gtk.FileChooserAction.OPEN: if action_type is not Gtk.FileChooserAction.OPEN:

View File

@@ -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 import get_channels, get_bouquets, write_bouquets, write_channels, Bouquets, Bouquet, Channel
from app.eparser.__constants import CAS, FLAG from app.eparser.__constants import CAS, FLAG
from app.eparser.bouquets import BqServiceType 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 . import Gtk, Gdk, UI_RESOURCES_PATH
from .dialogs import show_dialog, DialogType from .dialogs import show_dialog, DialogType
from .download_dialog import show_download_dialog from .download_dialog import show_download_dialog
@@ -83,6 +83,7 @@ class MainAppWindow:
"on_locate_in_services": self.on_locate_in_services} "on_locate_in_services": self.on_locate_in_services}
self.__options = get_config() self.__options = get_config()
self.__profile = self.__options.get("profile")
# Used for copy/paste. When adding the previous data will not be deleted. # Used for copy/paste. When adding the previous data will not be deleted.
# Clearing only after the insertion! # Clearing only after the insertion!
self.__rows_buffer = [] self.__rows_buffer = []
@@ -105,7 +106,9 @@ class MainAppWindow:
self.__services_model = builder.get_object("services_list_store") self.__services_model = builder.get_object("services_list_store")
self.__bouquets_model = builder.get_object("bouquets_tree_store") self.__bouquets_model = builder.get_object("bouquets_tree_store")
self.__status_bar = builder.get_object("status_bar") 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 # dynamically active elements depending on the selected view
self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS} self.__tool_elements = {k: builder.get_object(k) for k in self.__DYNAMIC_ELEMENTS}
self.__cas_label = builder.get_object("cas_label") self.__cas_label = builder.get_object("cas_label")
@@ -581,8 +584,11 @@ class MainAppWindow:
v.get_selection().unselect_all() v.get_selection().unselect_all()
def on_preferences(self, item): def on_preferences(self, item):
show_settings_dialog(self.__main_window, self.__options) response = show_settings_dialog(self.__main_window, self.__options)
self.__status_bar.push(0, "Current IP: " + self.__options["host"]) 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): def on_tree_view_key_release(self, view, event):
""" Handling keystrokes """ """ Handling keystrokes """

View File

@@ -1660,13 +1660,46 @@
<property name="spacing">2</property> <property name="spacing">2</property>
<property name="homogeneous">True</property> <property name="homogeneous">True</property>
<child> <child>
<placeholder/> <object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="label1">
<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="GtkLabel" id="profile_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Enigma 2 v.4</property>
</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">False</property>
<property name="position">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="ver_label"> <object class="GtkLabel" id="ver_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Ver. 0.1.2 Pre-alpha</property> <property name="label" translatable="yes">Ver. 0.2.0 Pre-alpha</property>
<property name="xalign">0.94999998807907104</property> <property name="xalign">0.94999998807907104</property>
</object> </object>
<packing> <packing>

View File

@@ -20,7 +20,7 @@ class SatellitesDialog:
_aggr = [None for x in range(9)] # aggregate _aggr = [None for x in range(9)] # aggregate
def __init__(self, transient, options): 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 self._options = options
handlers = {"on_open": self.on_open, handlers = {"on_open": self.on_open,

View File

@@ -1,39 +1,87 @@
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 app.ui.dialogs import show_dialog, DialogType
from . import Gtk, UI_RESOURCES_PATH from . import Gtk, UI_RESOURCES_PATH
def show_settings_dialog(transient, options): def show_settings_dialog(transient, options):
SettingsDialog(transient, options) return SettingsDialog(transient, options).show()
class SettingsDialog: class SettingsDialog:
def __init__(self, transient, options): 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 = 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) builder.connect_signals(handlers)
self._options = options
self._dialog = builder.get_object("settings_dialog") self._dialog = builder.get_object("settings_dialog")
self._dialog.set_transient_for(transient) self._dialog.set_transient_for(transient)
self._host_field = builder.get_object("host_field") 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 = builder.get_object("port_field")
self._port_field.set_text(options["port"])
self._login_field = builder.get_object("login_field") 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 = builder.get_object("password_field")
self._password_field.set_text(options["password"])
self._services_field = builder.get_object("services_field") 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 = 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 = 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 = 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: 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["host"] = self._host_field.get_text()
options["port"] = self._port_field.get_text() options["port"] = self._port_field.get_text()
options["user"] = self._login_field.get_text() options["user"] = self._login_field.get_text()
@@ -42,13 +90,6 @@ class SettingsDialog:
options["user_bouquet_path"] = self._user_bouquet_field.get_text() options["user_bouquet_path"] = self._user_bouquet_field.get_text()
options["satellites_xml_path"] = self._satellites_xml_field.get_text() options["satellites_xml_path"] = self._satellites_xml_field.get_text()
options["data_dir_path"] = self._data_dir_field.get_text() options["data_dir_path"] = self._data_dir_field.get_text()
write_config(options)
self._dialog.destroy()
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)
if __name__ == "__main__": if __name__ == "__main__":