Files
addon/platformcode/xbmc_config_menu.py
2020-02-18 19:43:47 +01:00

1018 lines
45 KiB
Python

# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# XBMC Config Menu
# ------------------------------------------------------------
from __future__ import division
#from builtins import str
import sys
PY3 = False
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
from builtins import range
from past.utils import old_div
import inspect
import os
import xbmcgui
from core import channeltools
from core import servertools, scrapertools
from platformcode import config, logger
class SettingsWindow(xbmcgui.WindowXMLDialog):
""" Clase derivada que permite utilizar cuadros de configuracion personalizados.
Esta clase deriva de xbmcgui.WindowXMLDialog y permite crear un cuadro de dialogo con controles del tipo:
Radio Button (bool), Cuadro de texto (text), Lista (list) y Etiquetas informativas (label).
Tambien podemos personalizar el cuadro añadiendole un titulo (title).
Metodo constructor:
SettingWindow(listado_controles, dict_values, title, callback, item)
Parametros:
listado_controles: (list) Lista de controles a incluir en la ventana, segun el siguiente esquema:
(opcional)list_controls= [
{'id': "nameControl1",
'type': "bool", # bool, text, list, label
'label': "Control 1: tipo RadioButton",
'color': '0xFFee66CC', # color del texto en formato ARGB hexadecimal
'default': True,
'enabled': True,
'visible': True
},
{'id': "nameControl2",
'type': "text", # bool, text, list, label
'label': "Control 2: tipo Cuadro de texto",
'color': '0xFFee66CC',
'default': "Valor por defecto",
'hidden': False, # only for type = text Indica si hay que ocultar
el texto (para passwords)
'enabled': True,
'visible': True
},
{'id': "nameControl3",
'type': "list", # bool, text, list, label
'label': "Control 3: tipo Lista",
'color': '0xFFee66CC',
'default': 0, # Indice del valor por defecto en lvalues
'enabled': True,
'visible': True,
'lvalues':["item1", "item2", "item3", "item4"], # only for type = list
},
{'id': "nameControl4",
'type': "label", # bool, text, list, label
'label': "Control 4: tipo Etiqueta",
'color': '0xFFee66CC',
'enabled': True,
'visible': True
}]
Si no se incluye el listado_controles, se intenta obtener del json del canal desde donde se hace la
llamada.
El formato de los controles en el json es:
{
...
...
"settings": [
{
"id": "name_control_1",
"type": "bool",
"label": "Control 1: tipo RadioButton",
"default": false,
"enabled": true,
"visible": true,
"color": "0xFFee66CC"
},
{
"id": "name_control_2",
"type": "text",
"label": "Control 2: tipo Cuadro de texto",
"default": "Valor por defecto",
"hidden": true,
"enabled": true,
"visible": true,
"color": "0xFFee66CC"
},
{
"id": "name_control_3",
"type": "list",
"label": "Control 3: tipo Lista",
"default": 0,
"enabled": true,
"visible": true,
"color": "0xFFee66CC",
"lvalues": [
"item1",
"item2",
"item3",
"item4"
]
},
{
"id": "name_control_4",
"type": "label",
"label": "Control 4: tipo Etiqueta",
"enabled": true,
"visible": true,
"color": "0xFFee66CC"
},
]
}
Los campos 'label', 'default', 'enabled' y 'lvalues' pueden ser un numero precedido de '@'. En cuyo caso se
buscara el literal en el archivo string.xml del idioma seleccionado.
Los campos 'enabled' y 'visible' admiten los comparadores eq(), gt() e it() y su funcionamiento se
describe en: http://kodi.wiki/view/Add-on_settings#Different_types
(opcional)dict_values: (dict) Diccionario que representa el par (id: valor) de los controles de la
lista.
Si algun control de la lista no esta incluido en este diccionario se le asignara el valor por
defecto.
dict_values={"nameControl1": False,
"nameControl2": "Esto es un ejemplo"}
(opcional) caption: (str) Titulo de la ventana de configuracion. Se puede localizar mediante un numero
precedido de '@'
(opcional) callback (str) Nombre de la funcion, del canal desde el que se realiza la llamada, que sera
invocada al pulsar el boton aceptar de la ventana. A esta funcion se le pasara como parametros el
objeto 'item' y el dicionario 'dict_values'. Si este parametro no existe, se busca en el canal una
funcion llamada 'cb_validate_config' y si existe se utiliza como callback.
Retorno: Si se especifica 'callback' o el canal incluye 'cb_validate_config' se devolvera lo que devuelva
esa funcion. Si no devolvera None
Ejemplos de uso:
platformtools.show_channel_settings(): Así tal cual, sin pasar ningún argumento, la ventana detecta de que canal
se ha hecho la llamada,
y lee los ajustes del json y carga los controles, cuando le das a Aceptar los vuelve a guardar.
return platformtools.show_channel_settings(list_controls=list_controls, dict_values=dict_values, callback='cb',
item=item):
Así abre la ventana con los controles pasados y los valores de dict_values, si no se pasa dict_values, carga
los valores por defecto de los controles,
cuando le das a aceptar, llama a la función 'callback' del canal desde donde se ha llamado, pasando como
parámetros, el item y el dict_values
"""
def start(self, list_controls=None, dict_values=None, caption="", callback=None, item=None,
custom_button=None, channelpath=None):
logger.info()
# Ruta para las imagenes de la ventana
self.mediapath = os.path.join(config.get_runtime_path(), 'resources', 'skins', 'Default', 'media')
# Capturamos los parametros
self.list_controls = list_controls
self.values = dict_values
self.caption = caption
self.callback = callback
self.item = item
if isinstance(custom_button, dict):
self.custom_button = {}
self.custom_button["label"] = custom_button.get("label", "")
self.custom_button["function"] = custom_button.get("function", "")
self.custom_button["visible"] = bool(custom_button.get("visible", True))
self.custom_button["close"] = bool(custom_button.get("close", False))
else:
self.custom_button = None
# Obtenemos el canal desde donde se ha echo la llamada y cargamos los settings disponibles para ese canal
if not channelpath:
channelpath = inspect.currentframe().f_back.f_back.f_code.co_filename
self.channel = os.path.basename(channelpath).replace(".py", "")
self.ch_type = os.path.basename(os.path.dirname(channelpath))
logger.info('PATH= ' + channelpath)
# Si no tenemos list_controls, hay que sacarlos del json del canal
if not self.list_controls:
# Si la ruta del canal esta en la carpeta "channels", obtenemos los controles y valores mediante chaneltools
if os.path.join(config.get_runtime_path(), "channels") or os.path.join(config.get_runtime_path(), "specials") in channelpath:
# La llamada se hace desde un canal
self.list_controls, default_values = channeltools.get_channel_controls_settings(self.channel)
self.kwargs = {"channel": self.channel}
# Si la ruta del canal esta en la carpeta "servers", obtenemos los controles y valores mediante servertools
elif os.path.join(config.get_runtime_path(), "servers") in channelpath:
# La llamada se hace desde un canal
self.list_controls, default_values = servertools.get_server_controls_settings(self.channel)
self.kwargs = {"server": self.channel}
# En caso contrario salimos
else:
return None
# Si no se pasan dict_values, creamos un dict en blanco
if self.values is None:
self.values = {}
# Ponemos el titulo
if self.caption == "":
self.caption = str(config.get_localized_string(30100)) + " -- " + self.channel.capitalize()
elif self.caption.startswith('@') and unicode(self.caption[1:]).isnumeric():
self.caption = config.get_localized_string(int(self.caption[1:]))
# Muestra la ventana
self.return_value = None
self.doModal()
return self.return_value
@staticmethod
def set_enabled(c, val):
if c["type"] == "list":
c["control"].setEnabled(val)
c["downBtn"].setEnabled(val)
c["upBtn"].setEnabled(val)
c["label"].setEnabled(val)
else:
c["control"].setEnabled(val)
@staticmethod
def set_visible(c, val):
if c["type"] == "list":
c["control"].setVisible(val)
c["downBtn"].setVisible(val)
c["upBtn"].setVisible(val)
c["label"].setVisible(val)
else:
c["control"].setVisible(val)
def evaluate_conditions(self):
for c in self.list_controls:
c["active"] = self.evaluate(self.list_controls.index(c), c["enabled"])
self.set_enabled(c, c["active"])
c["show"] = self.evaluate(self.list_controls.index(c), c["visible"])
if not c["show"]:
self.set_visible(c, c["show"])
self.visible_controls = [c for c in self.list_controls if c["show"]]
def evaluate(self, index, cond):
import re
ok = False
# Si la condicion es True o False, no hay mas que evaluar, ese es el valor
if isinstance(cond, bool):
return cond
# Obtenemos las condiciones
# conditions = re.compile("(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?").findall(cond)
conditions = re.compile(r'''(!?eq|!?gt|!?lt)?\s*\(\s*([^, ]+)\s*,\s*["']?([^"'\)]+)["']?\)([+|])?''').findall(cond)
# conditions = scrapertools.find_multiple_matches(cond, r"(!?eq|!?gt|!?lt)?\(([^,]+),[\"|']?([^)|'|\"]*)['|\"]?\)[ ]*([+||])?")
for operator, id, value, next in conditions:
# El id tiene que ser un numero, sino, no es valido y devuelve False
try:
id = int(id)
except:
return False
# El control sobre el que evaluar, tiene que estar dentro del rango, sino devuelve False
if index + id < 0 or index + id >= len(self.list_controls):
return False
else:
# Obtenemos el valor del control sobre el que se compara
c = self.list_controls[index + id]
if c["type"] == "bool":
control_value = bool(c["control"].isSelected())
if c["type"] == "text":
control_value = c["control"].getText()
if c["type"] == "list":
control_value = c["label"].getLabel()
if c["type"] == "label":
control_value = c["control"].getLabel()
if value.startswith('@') and unicode(value[1:]).isnumeric():
value = config.get_localized_string(int(value[1:]))
# Operaciones lt "menor que" y gt "mayor que", requieren que las comparaciones sean numeros, sino devuelve
# False
if operator in ["lt", "!lt", "gt", "!gt"]:
try:
value = int(value)
except ValueError:
return False
# Operacion eq "igual a"
if operator in ["eq", "!eq"]:
# valor int
try:
value = int(value)
except ValueError:
pass
# valor bool
if not isinstance(value, int) and value.lower() == "true":
value = True
elif not isinstance(value, int) and value.lower() == "false":
value = False
# operacion "eq" "igual a"
if operator == "eq":
if control_value == value:
ok = True
else:
ok = False
# operacion "!eq" "no igual a"
if operator == "!eq":
if not control_value == value:
ok = True
else:
ok = False
# operacion "gt" "mayor que"
if operator == "gt":
if control_value > value:
ok = True
else:
ok = False
# operacion "!gt" "no mayor que"
if operator == "!gt":
if not control_value > value:
ok = True
else:
ok = False
# operacion "lt" "menor que"
if operator == "lt":
if control_value < value:
ok = True
else:
ok = False
# operacion "!lt" "no menor que"
if operator == "!lt":
if not control_value < value:
ok = True
else:
ok = False
# Siguiente operación, si es "|" (or) y el resultado es True, no tiene sentido seguir, es True
if next == "|" and ok is True:
break
# Siguiente operación, si es "+" (and) y el resultado es False, no tiene sentido seguir, es False
if next == "+" and ok is False:
break
# Siguiente operación, si es "+" (and) y el resultado es True, Seguira, para comprobar el siguiente valor
# Siguiente operación, si es "|" (or) y el resultado es False, Seguira, para comprobar el siguiente valor
return ok
def add_control_label(self, c):
control = xbmcgui.ControlLabel(0, -100, self.controls_width, 40, "", alignment=4, font=self.font,
textColor=c["color"])
self.addControl(control)
control.setVisible(False)
control.setLabel(c["label"])
# Lo añadimos al listado
c["control"] = control
def add_control_list(self, c):
control = xbmcgui.ControlButton(0, -100, self.controls_width + 20, self.height_control,
c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'),
10, textColor=c["color"],
font=self.font)
label = xbmcgui.ControlLabel(0, -100, self.controls_width - 80, self.height_control,
"", font=self.font, textColor=c["color"], alignment= 1 | 4)
upBtn = xbmcgui.ControlButton(0, -100, 15, 7, "",
focusTexture=os.path.join(self.mediapath, 'Controls', 'spinUp-Focus.png'),
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'spinUp-noFocus.png'))
downBtn = xbmcgui.ControlButton(0, -100 + 15, 15, 7, "",
focusTexture=os.path.join(self.mediapath, 'Controls', 'spinDown-Focus.png'),
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'spinDown-noFocus.png'))
self.addControl(control)
self.addControl(label)
self.addControl(upBtn)
self.addControl(downBtn)
control.setVisible(False)
label.setVisible(False)
upBtn.setVisible(False)
downBtn.setVisible(False)
label.setLabel(c["lvalues"][self.values[c["id"]]])
c["control"] = control
c["label"] = label
c["downBtn"] = downBtn
c["upBtn"] = upBtn
def add_control_text(self, c):
if xbmcgui.ControlEdit == ControlEdit:
control = xbmcgui.ControlEdit(0, -100, self.controls_width, self.height_control,
c["label"], os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'),
0, textColor=c["color"],
font=self.font, isPassword=c["hidden"], window=self)
else:
control = xbmcgui.ControlEdit(0, -100, self.controls_width - 5, self.height_control,
c["label"], self.font, c["color"], '', 4, isPassword=c["hidden"],
focusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemFO.png'),
noFocusTexture=os.path.join(self.mediapath, 'Controls', 'MenuItemNF.png'))
self.addControl(control)
control.setVisible(False)
control.setLabel(c["label"])
# frodo fix
s = self.values[c["id"]]
if s is None:
s = ''
control.setText(s)
# control.setText(self.values[c["id"]])
control.setWidth(self.controls_width - 5)
control.setHeight(self.height_control)
c["control"] = control
def add_control_bool(self, c):
# Versiones antiguas no admite algunas texturas
if xbmcgui.__version__ in ["1.2", "2.0"]:
control = xbmcgui.ControlRadioButton(0, -100, self.controls_width + 20, self.height_control,
label=c["label"], font=self.font, textColor=c["color"],
focusTexture=os.path.join(self.mediapath, 'Controls',
'MenuItemFO.png'),
noFocusTexture=os.path.join(self.mediapath, 'Controls',
'MenuItemNF.png'))
else:
control = xbmcgui.ControlRadioButton(0, -100, self.controls_width + 20,
self.height_control, label=c["label"], font=self.font,
textColor=c["color"],
focusTexture=os.path.join(self.mediapath, 'Controls',
'MenuItemFO.png'),
noFocusTexture=os.path.join(self.mediapath, 'Controls',
'MenuItemNF.png'),
focusOnTexture=os.path.join(self.mediapath, 'Controls',
'radiobutton-focus.png'),
noFocusOnTexture=os.path.join(self.mediapath, 'Controls',
'radiobutton-focus.png'),
focusOffTexture=os.path.join(self.mediapath, 'Controls',
'radiobutton-nofocus.png'),
noFocusOffTexture=os.path.join(self.mediapath, 'Controls',
'radiobutton-nofocus.png'))
self.addControl(control)
control.setVisible(False)
control.setRadioDimension(x=self.controls_width + 10 - (self.height_control - 5), y=0,
width=self.height_control - 5, height=self.height_control - 5)
control.setSelected(self.values[c["id"]])
c["control"] = control
def onInit(self):
self.getControl(10004).setEnabled(False)
self.getControl(10005).setEnabled(False)
self.getControl(10006).setEnabled(False)
self.ok_enabled = False
self.default_enabled = False
#### Compatibilidad con Kodi 18 ####
if config.get_platform(True)['num_version'] < 18:
if xbmcgui.__version__ == "1.2":
self.setCoordinateResolution(1)
else:
self.setCoordinateResolution(5)
# Ponemos el título
self.getControl(10002).setLabel(self.caption)
if self.custom_button is not None:
if self.custom_button['visible']:
self.getControl(10006).setLabel(self.custom_button['label'])
else:
self.getControl(10006).setVisible(False)
self.getControl(10004).setPosition(self.getControl(10004).getPosition()[0] + 80,
self.getControl(10004).getPosition()[1])
self.getControl(10005).setPosition(self.getControl(10005).getPosition()[0] + 80,
self.getControl(10005).getPosition()[1])
# Obtenemos las dimensiones del area de controles
self.controls_width = self.getControl(10007).getWidth() - 30
self.controls_height = self.getControl(10007).getHeight() -100
self.controls_pos_x = self.getControl(10007).getPosition()[0] + self.getControl(10001).getPosition()[0] + 10
self.controls_pos_y = self.getControl(10007).getPosition()[1] + self.getControl(10001).getPosition()[1]
self.height_control = 40
self.font = "font12"
# En versiones antiguas: creamos 5 controles, de lo conrtario al hacer click al segundo control,
# automaticamente cambia el label del tercero a "Short By: Name" no se porque...
if xbmcgui.ControlEdit == ControlEdit:
for x in range(5):
control = xbmcgui.ControlRadioButton(-500, 0, 0, 0, "")
self.addControl(control)
for c in self.list_controls:
# Saltamos controles que no tengan los valores adecuados
if "type" not in c:
continue
if "label" not in c:
continue
if c["type"] != "label" and "id" not in c:
continue
if c["type"] == "list" and "lvalues" not in c:
continue
if c["type"] == "list" and not isinstance(c["lvalues"], list):
continue
if c["type"] == "list" and not len(c["lvalues"]) > 0:
continue
if c["type"] != "label" and len(
[control.get("id") for control in self.list_controls if c["id"] == control.get("id")]) > 1:
continue
# Translation label y lvalues
if c['label'].startswith('@') and unicode(c['label'][1:]).isnumeric():
c['label'] = config.get_localized_string(int(c['label'][1:]))
if c['type'] == 'list':
lvalues = []
for li in c['lvalues']:
if li.startswith('@') and unicode(li[1:]).isnumeric():
lvalues.append(config.get_localized_string(int(li[1:])))
else:
lvalues.append(li)
c['lvalues'] = lvalues
# Valores por defecto en caso de que el control no disponga de ellos
if c["type"] == "bool":
default = False
elif c["type"] == "list":
default = 0
else:
# label or text
default = ""
c["default"] = c.get("default", default)
c["color"] = c.get("color", "0xFFFFFFFF")
c["visible"] = c.get("visible", True)
c["enabled"] = c.get("enabled", True)
if c["type"] == "label" and "id" not in c:
c["id"] = None
if c["type"] == "text":
c["hidden"] = c.get("hidden", False)
# Decidimos si usar el valor por defecto o el valor guardado
if c["type"] in ["bool", "text", "list"]:
if c["id"] not in self.values:
if not self.callback:
self.values[c["id"]] = config.get_setting(c["id"], **self.kwargs)
else:
self.values[c["id"]] = c["default"]
if c["type"] == "bool":
self.add_control_bool(c)
elif c["type"] == 'text':
self.add_control_text(c)
elif c["type"] == 'list':
self.add_control_list(c)
elif c["type"] == 'label':
self.add_control_label(c)
self.list_controls = [c for c in self.list_controls if "control" in c]
self.evaluate_conditions()
self.index = -1
self.dispose_controls(0)
self.getControl(100010).setVisible(False)
self.getControl(10004).setEnabled(True)
self.getControl(10005).setEnabled(True)
self.getControl(10006).setEnabled(True)
self.ok_enabled = True
self.default_enabled = True
self.check_default()
self.check_ok(self.values)
def dispose_controls(self, index, focus=False, force=False):
show_controls = old_div(self.controls_height, self.height_control) - 1
visible_count = 0
if focus:
if not index >= self.index or not index <= self.index + show_controls:
if index < self.index:
new_index = index
else:
new_index = index - show_controls
else:
new_index = self.index
else:
if index + show_controls >= len(self.visible_controls): index = len(
self.visible_controls) - show_controls - 1
if index < 0: index = 0
new_index = index
if self.index != new_index or force:
for x, c in enumerate(self.visible_controls):
if x < new_index or visible_count > show_controls or not c["show"]:
self.set_visible(c, False)
else:
c["y"] = self.controls_pos_y + visible_count * self.height_control
visible_count += 1
if c["type"] != "list":
if c["type"] == "bool":
c["control"].setPosition(self.controls_pos_x, c["y"])
else:
c["control"].setPosition(self.controls_pos_x, c["y"])
else:
c["control"].setPosition(self.controls_pos_x, c["y"])
if xbmcgui.__version__ == "1.2":
c["label"].setPosition(self.controls_pos_x + self.controls_width - 30, c["y"])
else:
c["label"].setPosition(self.controls_pos_x, c["y"])
c["upBtn"].setPosition(self.controls_pos_x + c["control"].getWidth() - 40, c["y"] + 15)
c["downBtn"].setPosition(self.controls_pos_x + c["control"].getWidth() - 25, c["y"] + 15)
self.set_visible(c, True)
# Calculamos la posicion y tamaño del ScrollBar
hidden_controls = len(self.visible_controls) - show_controls - 1
if hidden_controls < 0: hidden_controls = 0
scrollbar_height = self.getControl(10008).getHeight() - (hidden_controls * 3)
scrollbar_y = self.getControl(10008).getPosition()[1] + (new_index * 3)
self.getControl(10009).setPosition(self.getControl(10008).getPosition()[0], scrollbar_y)
self.getControl(10009).setHeight(scrollbar_height)
self.index = new_index
if focus:
self.setFocus(self.visible_controls[index]["control"])
def check_ok(self, dict_values=None):
if not self.callback:
if dict_values:
self.init_values = dict_values.copy()
self.getControl(10004).setEnabled(False)
self.ok_enabled = False
else:
if self.init_values == self.values:
self.getControl(10004).setEnabled(False)
self.ok_enabled = False
else:
self.getControl(10004).setEnabled(True)
self.ok_enabled = True
def check_default(self):
if self.custom_button is None:
def_values = dict([[c["id"], c.get("default")] for c in self.list_controls if not c["type"] == "label"])
if def_values == self.values:
self.getControl(10006).setEnabled(False)
self.default_enabled = False
else:
self.getControl(10006).setEnabled(True)
self.default_enabled = True
def onClick(self, id):
# Valores por defecto
if id == 10006:
if self.custom_button is not None:
if self.custom_button["close"]:
self.close()
if '.' in self.callback:
package, self.callback = self.callback.rsplit('.', 1)
else:
package = '%s.%s' % (self.ch_type, self.channel)
try:
cb_channel = __import__(package, None, None, [package])
except ImportError:
logger.error('Imposible importar %s' % package)
else:
self.return_value = getattr(cb_channel, self.custom_button['function'])(self.item, self.values)
if not self.custom_button["close"]:
if isinstance(self.return_value, dict) and "label" in self.return_value:
self.getControl(10006).setLabel(self.return_value['label'])
for c in self.list_controls:
if c["type"] == "text":
c["control"].setText(self.values[c["id"]])
if c["type"] == "bool":
c["control"].setSelected(self.values[c["id"]])
if c["type"] == "list":
c["label"].setLabel(c["lvalues"][self.values[c["id"]]])
self.evaluate_conditions()
self.dispose_controls(self.index, force=True)
else:
for c in self.list_controls:
if c["type"] == "text":
c["control"].setText(c["default"])
self.values[c["id"]] = c["default"]
if c["type"] == "bool":
c["control"].setSelected(c["default"])
self.values[c["id"]] = c["default"]
if c["type"] == "list":
c["label"].setLabel(c["lvalues"][c["default"]])
self.values[c["id"]] = c["default"]
self.evaluate_conditions()
self.dispose_controls(self.index, force=True)
self.check_default()
self.check_ok()
# Boton Cancelar y [X]
if id == 10003 or id == 10005:
self.close()
# Boton Aceptar
if id == 10004:
self.close()
if self.callback and '.' in self.callback:
package, self.callback = self.callback.rsplit('.', 1)
else:
package = '%s.%s' % (self.ch_type, self.channel)
cb_channel = None
try:
cb_channel = __import__(package, None, None, [package])
except ImportError:
logger.error('Imposible importar %s' % package)
if self.callback:
# Si existe una funcion callback la invocamos ...
self.return_value = getattr(cb_channel, self.callback)(self.item, self.values)
else:
# si no, probamos si en el canal existe una funcion 'cb_validate_config' ...
try:
self.return_value = getattr(cb_channel, 'cb_validate_config')(self.item, self.values)
except AttributeError:
# ... si tampoco existe 'cb_validate_config'...
for v in self.values:
config.set_setting(v, self.values[v], **self.kwargs)
# Controles de ajustes, si se cambia el valor de un ajuste, cambiamos el valor guardado en el diccionario de
# valores
# Obtenemos el control sobre el que se ha echo click
control = self.getControl(id)
# Lo buscamos en el listado de controles
for cont in self.list_controls:
# Si el control es un "downBtn" o "upBtn" son los botones del "list"
# en este caso cambiamos el valor del list
if cont["type"] == "list" and (cont["downBtn"] == control or cont["upBtn"] == control):
# Para bajar una posicion
if cont["downBtn"] == control:
index = cont["lvalues"].index(cont["label"].getLabel())
if index > 0:
cont["label"].setLabel(cont["lvalues"][index - 1])
# Para subir una posicion
elif cont["upBtn"] == control:
index = cont["lvalues"].index(cont["label"].getLabel())
if index < len(cont["lvalues"]) - 1:
cont["label"].setLabel(cont["lvalues"][index + 1])
# Guardamos el nuevo valor en el diccionario de valores
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
# Si esl control es un "bool", guardamos el nuevo valor True/False
if cont["type"] == "bool" and cont["control"] == control:
self.values[cont["id"]] = bool(cont["control"].isSelected())
# Si esl control es un "text", guardamos el nuevo valor
if cont["type"] == "text" and cont["control"] == control:
# Versiones antiguas requieren abrir el teclado manualmente
if xbmcgui.ControlEdit == ControlEdit:
import xbmc
keyboard = xbmc.Keyboard(cont["control"].getText(), cont["control"].getLabel(),
cont["control"].isPassword)
keyboard.setHiddenInput(cont["control"].isPassword)
keyboard.doModal()
if keyboard.isConfirmed():
cont["control"].setText(keyboard.getText())
self.values[cont["id"]] = cont["control"].getText()
self.evaluate_conditions()
self.dispose_controls(self.index, force=True)
self.check_default()
self.check_ok()
# Versiones antiguas requieren esta funcion
def onFocus(self, a):
pass
def onAction(self, raw_action):
# Obtenemos el foco
focus = self.getFocusId()
action = raw_action.getId()
# Accion 1: Flecha izquierda
if action == 1:
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
if focus not in [10004, 10005, 10006]:
control = self.getFocus().getId()
for cont in self.list_controls:
if cont["type"] == "list" and cont["control"].getId() == control:
index = cont["lvalues"].index(cont["label"].getLabel())
if index > 0:
cont["label"].setLabel(cont["lvalues"][index - 1])
# Guardamos el nuevo valor en el listado de controles
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
self.evaluate_conditions()
self.dispose_controls(self.index, force=True)
self.check_default()
self.check_ok()
# Si el foco está en alguno de los tres botones inferiores, movemos al siguiente
else:
if focus == 10006:
self.setFocusId(10005)
if focus == 10005 and self.ok_enabled:
self.setFocusId(10004)
# Accion 1: Flecha derecha
elif action == 2:
# Si el foco no está en ninguno de los tres botones inferiores, y esta en un "list" cambiamos el valor
if focus not in [10004, 10005, 10006]:
control = self.getFocus().getId()
for cont in self.list_controls:
if cont["type"] == "list" and cont["control"].getId() == control:
index = cont["lvalues"].index(cont["label"].getLabel())
if index < len(cont["lvalues"]) - 1:
cont["label"].setLabel(cont["lvalues"][index + 1])
# Guardamos el nuevo valor en el listado de controles
self.values[cont["id"]] = cont["lvalues"].index(cont["label"].getLabel())
self.evaluate_conditions()
self.dispose_controls(self.index, force=True)
self.check_default()
self.check_ok()
# Si el foco está en alguno de los tres botones inferiores, movemos al siguiente
else:
if focus == 10004:
self.setFocusId(10005)
if focus == 10005 and self.default_enabled:
self.setFocusId(10006)
# Accion 4: Flecha abajo
elif action == 4:
# Si el foco no está en ninguno de los tres botones inferiores, bajamos el foco en los controles de ajustes
if focus not in [10004, 10005, 10006]:
try:
focus_control = [self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0]
focus_control += 1
except:
focus_control = 0
while not focus_control == len(self.visible_controls) and (
self.visible_controls[focus_control]["type"] == "label" or not
self.visible_controls[focus_control]["active"]):
focus_control += 1
if focus_control >= len(self.visible_controls):
self.setFocusId(10005)
return
self.dispose_controls(focus_control, True)
# Accion 4: Flecha arriba
elif action == 3:
# Si el foco no está en ninguno de los tres botones inferiores, subimos el foco en los controles de ajustes
if focus not in [10003, 10004, 10005, 10006]:
try:
focus_control = \
[self.visible_controls.index(c) for c in self.visible_controls if c["control"].getId() == self.getFocus().getId()][0]
focus_control -= 1
while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not
self.visible_controls[focus_control]["active"]):
focus_control -= 1
if focus_control < 0: focus_control = 0
except:
focus_control = 0
self.dispose_controls(focus_control, True)
# Si el foco está en alguno de los tres botones inferiores, ponemos el foco en el ultimo ajuste.
else:
focus_control = len(self.visible_controls) - 1
while not focus_control == -1 and (self.visible_controls[focus_control]["type"] == "label" or not
self.visible_controls[focus_control]["active"]):
focus_control -= 1
if focus_control < 0: focus_control = 0
self.setFocus(self.visible_controls[focus_control]["control"])
# Accion 104: Scroll arriba
elif action == 104:
self.dispose_controls(self.index - 1)
# Accion 105: Scroll abajo
elif action == 105:
self.dispose_controls(self.index + 1)
# ACTION_PREVIOUS_MENU 10
# ACTION_NAV_BACK 92
elif action in [10, 92]:
self.close()
elif action == 504:
if self.xx > raw_action.getAmount2():
if old_div((self.xx - int(raw_action.getAmount2())), self.height_control):
self.xx -= self.height_control
self.dispose_controls(self.index + 1)
else:
if old_div((int(raw_action.getAmount2()) - self.xx), self.height_control):
self.xx += self.height_control
self.dispose_controls(self.index - 1)
return
elif action == 501:
self.xx = int(raw_action.getAmount2())
class ControlEdit(xbmcgui.ControlButton):
def __new__(cls, *args, **kwargs):
del kwargs["isPassword"]
del kwargs["window"]
args = list(args)
return xbmcgui.ControlButton.__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
self.isPassword = kwargs["isPassword"]
self.window = kwargs["window"]
self.label = ""
self.text = ""
self.textControl = xbmcgui.ControlLabel(self.getX(), self.getY(), self.getWidth(), self.getHeight(), self.text,
font=kwargs["font"], textColor=kwargs["textColor"], alignment= 4 | 1)
self.window.addControl(self.textControl)
def setLabel(self, val):
self.label = val
xbmcgui.ControlButton.setLabel(self, val)
def getX(self):
return xbmcgui.ControlButton.getPosition(self)[0]
def getY(self):
return xbmcgui.ControlButton.getPosition(self)[1]
def setEnabled(self, e):
xbmcgui.ControlButton.setEnabled(self, e)
self.textControl.setEnabled(e)
def setWidth(self, w):
xbmcgui.ControlButton.setWidth(self, w)
self.textControl.setWidth(old_div(w, 2))
def setHeight(self, w):
xbmcgui.ControlButton.setHeight(self, w)
self.textControl.setHeight(w)
def setPosition(self, x, y):
xbmcgui.ControlButton.setPosition(self, x, y)
if xbmcgui.__version__ == "1.2":
self.textControl.setPosition(x + self.getWidth(), y)
else:
self.textControl.setPosition(x + old_div(self.getWidth(), 2), y)
def setText(self, text):
self.text = text
if self.isPassword:
self.textControl.setLabel("*" * len(self.text))
else:
self.textControl.setLabel(self.text)
def getText(self):
return self.text
if not hasattr(xbmcgui, "ControlEdit"):
xbmcgui.ControlEdit = ControlEdit