def select_record(tablename, multi=False, size=None, where_expr=None, code_azienda=None, num_esercizio=None): """Mostra una finestra di selezione record di Konga; la finestra mostrerà i record della tabella *tablename* e avrà dimensione *size* (tupla di due elementi nella forma ``(width, height)``). *where_expr* può essere un'espressione SQL *WHERE* per filtrare i record selezionabili; *code_azienda* e *code_esercizio* filtrano ulteriormente i record visualizzati usando l'azienda e l'esercizio specificati. Se *multi* è ``True``, la funzione restituisce una lista di record selezionati sotto forma di ``dict``, altrimenti restituisce il ``dict`` del singolo record selezionato. In tutti i casi se l'utente annulla l'operazione, la funzione restituirà ``None``. .. warning:: Questa funzione è disponibile solo all'interno di Konga; eseguendola da fuori verrà lanciata l'eccezione :class:`kongautil.KongaRequiredError`. """ if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.select_record(tablename, multi, size, where_expr, code_azienda=code_azienda, num_esercizio=num_esercizio) else: raise kongautil.KongaRequiredError
def choose_directory(message=None, path=''): """Mostra una finestra di selezione directory con titolo *message* e percorso iniziale *path*. La funzione restituisce il percorso della directory selezionata oppure ``None`` se l'utente ha annullato l'operazione. Se eseguita al di fuori di Konga, questa funzione ignora il parametro *path*, e l'utente dovrà inserire il percorso completo della directory; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.choose_directory(message, path) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) while True: try: dirname = input( 'Enter an existing directory to open or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if not dirname: return None if os.path.exists(dirname) and os.path.isdir(dirname): break return dirname
def open_file(message=None, specs=None, path='', multi=False): """Mostra una finestra di caricamento file con titolo *message. *specs* può essere una lista di tuple ``(extension, description)`` per permettere di caricare solo file di tipi specifici; *path* è il percorso predefinito, e *multi* permette di selezionare più di un file da caricare. Se *multi* è ``False``, la funzione restituisce il percorso del file selezionato o ``None`` se l'utente ha annullato il caricamento, altrimenti restituisce la lista di file selezionati. Se eseguita al di fuori di Konga, questa funzione ignora i parametri *specs*, *path* e *multi*, e l'utente dovrà inserire il percorso completo del file da caricare; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.open_file(message, specs, path, multi) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) while True: try: filename = input( 'Enter an existing filename to open or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if not filename: return None if os.path.exists(filename) and os.path.isfile(filename): break return filename
def save_file(message=None, spec=None, path=''): """Mostra una finestra di salvataggio file con titolo *message. *spec* può essere una tupla nella forma ``(extension, description)`` per permettere di salvare file di un tipo specifico; *path* è il percorso predefinito. La funzione restituisce il percorso del file da salvare oppure ``None`` se l'utente ha annullato il salvataggio. Se eseguita al di fuori di Konga, questa funzione ignora i parametri *specs* e *path*, e l'utente dovrà inserire il percorso completo del file da salvare; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.save_file(message, spec, path) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) try: filename = input('Enter filename to be saved or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None return filename or None
def execute_form(form_data, title=None, message=None, condition=None): """Apre un form di immissione dati con titolo *title*; se *message* è specificato, il testo corrispondente sarà visualizzato in alto nella finestra del form. *form_data* deve essere una lista di ``dict`` con le specifiche dei campi da mostrare; nel ``dict`` di un singolo campo, l'unica chiave richiesta è ``name``, che deve identificare univocamente il nome del campo. E' possibile specificare l'etichetta da mostrare accando al campo stesso tramite la chiave ``label``; la tipologia di dato consentità è specificata tramite la chiave ``type``, che può assumere i valori: * ``str``: testo semplice, con possibile lunghezza massima ``length`` se la chiave è specificata; * ``password``: parola chiave; * ``int``: valore intero; * ``decimal``: valore decimale (:class:`kongalib.Decimal`); * ``range``: valore intero compreso tra un valore minimo (specificato dalla chiave ``min`` con valore predefinito ``0``) e un valore massimo (specificato dalla chiave ``max`` con valore predefinito ``100``); * ``slider``: simile a ``range`` ma viene visualizzato come cursore di selezione valore scorrevole; * ``bool``: valore booleano; * ``date``: data (``datetime.date``); * ``choice``: valore interno che identifica l'indice di una scelta tra quelle specificate nella chiave ``items`` (lista di stringhe); * ``listbox``: simile a ``choice`` ma viene visualizzato come lista di elementi da cui fare una scelta; * ``load``: nome di file esistente da caricare; * ``save``: nome di file da salvare; * ``dir``: nome di directory esistente; * ``code``: stringa di testo che identifica il codice di un record, la cui tabella è indicata dalla chiave ``table``; * ``company_code``: simile a ``code``, specifica l'azienda su cui gli altri campi ``code`` possono essere ricercati; * ``accounting_year_code``: simile a ``code``, specifica l'esercizio su cui gli altri campi ``code`` possono essere ricercati; Se presente, la chiave ``default`` permette di specificare il valore predefinito per un dato campo; inoltre se è presente la chiave ``focus`` (con qualsiasi valore), il campo corrispondente prenderà il focus all'avvio della finestra. Se l'utente annulla il form la funzione restituisce ``None``, altrimenti un ``dict`` le cui chiavi sono i nome dei campi e i valori i dati immessi dall'utente. Il parametro *condition*, se presente, permette di specificare una condizione di validazione per il form sotto forma di espressione Python; i nomi dei campi specificati in *form_data* saranno disponibili come variabili nell'esecuzione di questa condizione, il cui esito determinerà se consentire o meno l'uscita dal form con successo.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.execute_form(form_data, title, message, condition) else: import kongalib, decimal, datetime, getpass class InvalidInput(RuntimeError): pass if title: print(colorama.Style.BRIGHT + textwrap.fill(title, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) if message: print(textwrap.fill(message, width=_get_term_width() - 1)) result = {} for entry in form_data: if not isinstance(entry, dict): raise RuntimeError("Expected dict as form data entry") if 'name' not in entry: raise RuntimeError( "Expected 'name' key in form data entry dict") name = str(entry['name']) label = str(entry.get('label', name)) prompt = input wtype = entry.get('type', str) if wtype in ('integer', 'int'): try: default = str(int(entry.get('default', 0))) except: default = '0' def validate(text): try: return int(text) except: raise InvalidInput('Expected integer number') elif wtype in ('decimal', kongalib.Decimal, decimal.Decimal): try: default = str(kongalib.Decimal(entry.get('default', 0))) except: default = str(kongalib.Decimal(0)) def validate(text): try: return kongalib.Decimal(text) except: raise InvalidInput('Expected decimal number') elif wtype in ('range', 'slider'): try: default = str(int(entry.get('default', 0))) except: default = '0' try: min_value = int(entry.get('min', 0)) except: min_value = 0 try: max_value = int(entry.get('max', 100)) except: max_value = 100 label += ' (%d-%d)' % (min_value, max_value) def validate(text): try: value = int(text) if (value < min_value) or (value > max_value): raise RuntimeError return value except: raise InvalidInput( 'Expected integer number between %d and %d' % (min_value, max_value)) elif wtype in ('bool', 'boolean', bool, 'check'): try: default = 'Y' if bool(entry.get('default', False)) else 'N' except: default = 'N' def validate(text): if text.lower() in ('t', 'true', 'y', 'yes', '1'): return True if text.lower() in ('f', 'false', 'n', 'no', '0'): return False raise InvalidInput('Expected boolean value') elif wtype in ('date', datetime.date): try: default = datetime.datetime.strptime( entry.get('default', datetime.date.today()), '%Y-%m-%d').date().isoformat() except: default = datetime.date.today().isoformat() def validate(text): try: return datetime.datetime.strptime(text, '%Y-%m-%d').date() except: raise InvalidInput('Expected iso date (YYYY-MM-DD)') elif wtype in ('choice', 'listbox', 'combobox'): items = entry.get('items', []) if (not isinstance(items, (tuple, list))) or (not all( [isinstance(item, basestring) for item in items])): raise RuntimeError( "Expected list of strings as 'items' value") print(label) for index, item in enumerate(items): print("%d) %s" % (index + 1, item)) label = 'Enter selection' try: default = str(int(entry.get('default', 0)) + 1) except: default = '1' def validate(text): try: value = int(text) if (value < 1) or (value > len(items)): raise RuntimeError return value - 1 except: raise InvalidInput( 'Expected integer number between %d and %d' % (1, len(items))) else: if wtype == 'password': prompt = getpass.getpass default = None else: try: default = str(entry.get('default', '')) except: default = '' try: length = int(entry.get('length', 0)) except: length = 0 def validate(text): if length and (len(text) > length): raise InvalidInput( 'String lengths exceeds maximum size of %d characters' % length) return text if default is not None: label += ' [%s]' % default while True: try: value = prompt(label + ': ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if (not value) and (default is not None): value = default try: value = validate(value) break except InvalidInput as e: print(colorama.Fore.RED + str(e) + colorama.Fore.RESET) result[name] = value if condition is not None: if not eval(condition, result.copy()): print(colorama.Style.BRIGHT + colorama.Fore.RED + "Form input data validation failed; aborted" + colorama.Style.RESET_ALL) result = None return result
def message_box(text, title='', buttons=BUTTON_OK, icon=ICON_INFORMATION): """Mostra una finestra di dialogo modale con titolo *title*, messaggio *text* e icona *icon* (una delle costanti ``ICON_*``). La finestra mostrerà i bottoni identificati da *buttons*, che può contenere uno o più costanti ``BUTTON_*`` in *or* tra loro, e ritornerà la costante relativa al bottone selezionato dall'utente per chiudere la finestra.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.message_box(text, title, buttons, icon) else: print() if icon == ICON_WARNING: title = colorama.Fore.YELLOW + "WARNING" + ( ': ' if title else '') + colorama.Fore.RESET + (title or '') elif icon == ICON_ERROR: title = colorama.Fore.RED + "ERROR" + ( ': ' if title else '') + colorama.Fore.RESET + (title or '') if title: print(' ' + colorama.Style.BRIGHT + textwrap.fill(title, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) print() print(textwrap.fill(text, width=_get_term_width() - 1)) print() buttons_info = [ (BUTTON_OK, 'ok'), (BUTTON_YES, 'yes'), (BUTTON_YES_ALL, 'yes all'), (BUTTON_NO, 'no'), (BUTTON_NO_ALL, 'no all'), (BUTTON_CANCEL, 'cancel'), (BUTTON_OPEN, 'open'), (BUTTON_SAVE, 'save'), (BUTTON_SAVE_ALL, 'save_all'), (BUTTON_CLOSE, 'close'), (BUTTON_DISCARD, 'discard'), (BUTTON_APPLY, 'apply'), (BUTTON_RESET, 'reset'), (BUTTON_ABORT, 'abort'), (BUTTON_RETRY, 'retry'), (BUTTON_IGNORE, 'ignore'), ] buttons_map = {} labels = [] for bit, label in buttons_info: if buttons & bit: for i, c in enumerate(label): if c not in buttons_map: buttons_map[c] = bit labels.append('%s(%s)%s' % (label[:i], c, label[i + 1:])) break else: for c in string.ascii_letters: if c not in buttons_map: buttons_map[c] = bit labels.append('%s (%s)' % (label, c)) break answer = None while answer not in buttons_map: try: answer = input(', '.join(labels) + ': ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) if buttons & BUTTON_CANCEL: return BUTTON_CANCEL elif buttons & BUTTON_NO: return BUTTON_NO elif buttons & BUTTON_CLOSE: return BUTTON_CLOSE elif buttons & BUTTON_DISCARD: return BUTTON_DISCARD elif buttons & BUTTON_ABORT: return BUTTON_ABORT return buttons & ~(buttons - 1) return buttons_map[answer]