From f2e571185df559490650b4b16b9c637497bba7b8 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 2 Jul 2022 23:14:32 +0300 Subject: [PATCH] xml write support for DVB-T/C --- app/eparser/satxml.py | 139 ++++++++++++++++++++++++++---------------- app/ui/xml/edit.py | 13 ++-- 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/app/eparser/satxml.py b/app/eparser/satxml.py index 5bdfe207..322a9350 100644 --- a/app/eparser/satxml.py +++ b/app/eparser/satxml.py @@ -26,35 +26,47 @@ # -""" Module for parsing *.xml files. +""" Module for working with *.xml files. For more info see comments. """ -from xml.dom.minidom import Document import xml.etree.ElementTree as ETree from .ecommons import Satellite, Terrestrial, Cable, Transponder, TerTransponder, CableTransponder -_SAT_COMMENT = (" File was created in DemonEditor\n\n" - "usable flags are\n" +_SAT_COMMENT = ("\tFile was created in DemonEditor.\n\n" + "Usable flags are:\n" " 1: Network Scan\n" " 2: use BAT\n" " 4: use ONIT\n" " 8: skip NITs of known networks\n" - " and combinations of this.\n\n" + " This is a bitmap and combinations can be used.\n\n" + "Transponder parameters:\n" + "\tpolarization: 0 - Horizontal, 1 - Vertical, 2 - Left Circular, 3 - Right Circular\n" + "\tfec_inner: 0 - Auto, 1 - 1/2, 2 - 2/3, 3 - 3/4, 4 - 5/6, 5 - 7/8, 6 - 8/9, 7 - 3/5,\n" + "\t8 - 4/5, 9 - 9/10, 15 - None\n" + "\tmodulation: 0 - Auto, 1 - QPSK, 2 - 8PSK, 4 - 16APSK, 5 - 32APSK\n" + "\trolloff: 0 - 0.35, 1 - 0.25, 2 - 0.20, 3 - Auto\n" + "\tpilot: 0 - Off, 1 - On, 2 - Auto\n" + "\tinversion: 0 = Off, 1 = On, 2 = Auto (default)\n" + "\tsystem: 0 = DVB-S, 1 = DVB-S2\n" + "\tis_id: 0 - 255\n" + "\tpls_mode: 0 - Root, 1 - Gold, 2 - Combo\n" + "\tpls_code: 0 - 262142\n\n") - "transponder parameters:\n" - "polarization: 0 - Horizontal, 1 - Vertical, 2 - Left Circular, 3 - Right Circular\n" - "fec_inner: 0 - Auto, 1 - 1/2, 2 - 2/3, 3 - 3/4, 4 - 5/6, 5 - 7/8, 6 - 8/9, 7 - 3/5,\n" - "8 - 4/5, 9 - 9/10, 15 - None\n" - "modulation: 0 - Auto, 1 - QPSK, 2 - 8PSK, 4 - 16APSK, 5 - 32APSK\n" - "rolloff: 0 - 0.35, 1 - 0.25, 2 - 0.20, 3 - Auto\n" - "pilot: 0 - Off, 1 - On, 2 - Auto\n" - "inversion: 0 = Off, 1 = On, 2 = Auto (default)\n" - "system: 0 = DVB-S, 1 = DVB-S2\n" - "is_id: 0 - 255\n" - "pls_mode: 0 - Root, 1 - Gold, 2 - Combo\n" - "pls_code: 0 - 262142\n\n") +_TERRESTRIAL_COMMENT = ("\tFile was created in DemonEditor.\n\n" + "Usable flags are:\n" + " 1: Network Scan\n" + " 2: use BAT\n" + " 4: use ONIT\n" + " 8: skip NITs of known networks\n" + " This is a bitmap and combinations can be used.\n\n") + +_CABLE_COMMENT = ("\tFile was created in DemonEditor.\n\n" + "Transponder parameters:\n" + "\tmodulation:\n" + "\t3: QAM64\n" + "\t5: QAM256\n") def get_satellites(path): @@ -120,45 +132,64 @@ def get_cable_transponders(elem): e.get("modulation", None)) for e in elem.iter("transponder")] -def write_satellites(satellites, data_path): - """ Creation satellites.xml file """ - doc = Document() - comment = doc.createComment(_SAT_COMMENT) - doc.appendChild(comment) - root = doc.createElement("satellites") - doc.appendChild(root) +def write_satellites(satellites, data_path, encoding="UTF-8"): + """ Creates satellites.xml file. """ + write_xml("satellites", "sat", satellites, data_path, _SAT_COMMENT, encoding) - for sat in satellites: - # Create Element - sat_child = doc.createElement("sat") - sat_child.setAttribute("name", sat.name) - sat_child.setAttribute("flags", sat.flags) - sat_child.setAttribute("position", sat.position) - for tr in sat.transponders: - transponder_child = doc.createElement("transponder") - transponder_child.setAttribute("frequency", tr.frequency) - transponder_child.setAttribute("symbol_rate", tr.symbol_rate) - transponder_child.setAttribute("polarization", tr.polarization) - transponder_child.setAttribute("fec_inner", tr.fec_inner or "0") - transponder_child.setAttribute("system", tr.system or "0") - transponder_child.setAttribute("modulation", tr.modulation or "0") - if tr.pls_mode: - transponder_child.setAttribute("pls_mode", tr.pls_mode) - if tr.pls_code: - transponder_child.setAttribute("pls_code", tr.pls_code) - if tr.is_id: - transponder_child.setAttribute("is_id", tr.is_id) - if tr.t2mi_plp_id: - transponder_child.setAttribute("t2mi_plp_id", tr.t2mi_plp_id) - sat_child.appendChild(transponder_child) - root.appendChild(sat_child) - doc.writexml(open(data_path, "w"), - # indent="", - addindent=" ", - newl='\n', - encoding="iso-8859-1") - doc.unlink() +def write_terrestrial(terrestrial, data_path, encoding="UTF-8"): + """ Creates terrestrial.xml file. """ + write_xml("locations", "terrestrial", terrestrial, data_path, _TERRESTRIAL_COMMENT, encoding) + + +def write_cable(cables, data_path, encoding="UTF-8"): + """ Creates cables.xml file. """ + write_xml("cables", "cable", cables, data_path, _CABLE_COMMENT, encoding) + + +def write_xml(root_name, sub_name, data, data_path, comment="", encoding="UTF-8"): + """ Creates *.xml files. """ + xml = ETree.Element(root_name) + [write_element(sub_name, "transponder", t, xml) for t in data] + + tree = ETree.ElementTree(xml) + indent(tree.getroot()) + + with open(data_path, "wb") as f: + # To put comment on top. + f.write(f'\n\n\n'.encode("utf-8")) + tree.write(f, encoding=encoding) + + +def write_element(e_name, ch_name, e_data, root): + """ Writes element with sub elements. + + @param e_name: Element name. + @param ch_name: Child element name. + @param e_data: Element data -> defaultdict + @param root: Parent of the element. + """ + t = e_data._asdict() + subs = t.pop("transponders") + root_sub = ETree.SubElement(root, e_name, {k: v for k, v in t.items() if v}) + [ETree.SubElement(root_sub, ch_name, {k: v for k, v in tr._asdict().items() if v}) for tr in subs] + + +def indent(elem, parent=None, index=-1, level=0, space=" "): + """ Appends whitespace to the subtree to indent the tree visually. + + Since the minimum supported version < 3.9, we will use our own implementation. + """ + for i, sub in enumerate(elem): + indent(sub, elem, i, level + 1) + if parent: + if index == 0: + parent.text = f"\n{space * level}" + else: + parent[index - 1].tail = f"\n{space * level}" + + if index == len(parent) - 1: + elem.tail = f"\n{space * (level - 1)}" if __name__ == "__main__": diff --git a/app/ui/xml/edit.py b/app/ui/xml/edit.py index b62c86c0..96098eff 100644 --- a/app/ui/xml/edit.py +++ b/app/ui/xml/edit.py @@ -35,8 +35,9 @@ from app.commons import run_idle from app.connections import DownloadType from app.eparser import get_satellites, write_satellites, Satellite, Transponder from app.eparser.ecommons import (POLARIZATION, FEC, SYSTEM, MODULATION, T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC, - GUARD_INTERVAL, TRANSMISSION_MODE, HIERARCHY, Inversion, FEC_DEFAULT, C_MODULATION) -from app.eparser.satxml import get_terrestrial, get_cable + GUARD_INTERVAL, TRANSMISSION_MODE, HIERARCHY, Inversion, FEC_DEFAULT, C_MODULATION, + Terrestrial, Cable) +from app.eparser.satxml import get_terrestrial, get_cable, write_terrestrial, write_cable from .dialogs import SatelliteDialog, TransponderDialog, SatellitesUpdateDialog from ..dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder from ..main_helper import move_items, on_popup_menu @@ -457,9 +458,13 @@ class SatellitesTool(Gtk.Box): if page is Page.SATELLITE and show_dialog(DialogType.QUESTION, self._app.app_window) == Gtk.ResponseType.OK: if self._dvb_type is self.DVB.SAT: write_satellites((Satellite(*r) for r in self._satellite_view.get_model()), - self._settings.profile_data_path + "satellites.xml") + f"{self._settings.profile_data_path}satellites.xml") + elif self._dvb_type is self.DVB.TERRESTRIAL: + write_terrestrial((Terrestrial(*r) for r in self._terrestrial_view.get_model()), + f"{self._settings.profile_data_path}terrestrial.xml") else: - self._app.show_error_message("Not implemented yet!") + write_cable((Cable(*r) for r in self._cable_view.get_model()), + f"{self._settings.profile_data_path}cables.xml") def on_save_as(self, app, page): show_dialog(DialogType.ERROR, transient=self._app.app_window, text="Not implemented yet!")