#!/usr/bin/env python3 import os import easymacro as app ID_EXTENSION = '' _ = None TITLE = 'ZAZ-PIP' URL_PIP = 'https://bootstrap.pypa.io/get-pip.py' URL_TEST = 'http://duckduckgo.com' PIP = 'pip' URL_GIT = 'https://git.cuates.net/elmau/zaz-pip' ICON_OK = 'ok.svg' ICON_QUESTION = 'question.svg' PACKAGES = { 'cffi': ICON_OK, 'cryptography': ICON_OK, 'httpx': ICON_OK, 'lxml': ICON_OK, 'numpy': ICON_OK, 'pandas': ICON_OK, 'psycopg2-binary': ICON_OK, 'peewee': ICON_OK, 'pillow': ICON_OK, 'pytesseract': ICON_OK, 'sounddevice': ICON_OK, } def open_dialog_pip(): dialog = _create_dialog() proxy = app.get_config('proxy', {}, TITLE) if proxy: app.debug(proxy) HOST = proxy['txt_proxy_host'] PORT = proxy['txt_proxy_port'] USER = proxy['txt_proxy_user'] PASS = proxy['txt_proxy_pass'] os.environ['http_proxy'] = f'http://{USER}:{PASS}@{HOST}:{PORT}' os.environ['https_proxy'] = f'http://{USER}:{PASS}@{HOST}:{PORT}' dialog.open() return def run(path_locales): global _ _ = app.install_locales(path_locales) open_dialog_pip() return class Controllers(object): OK1 = 'Successfully installed' OK2 = 'Requirement already' OK3 = 'Successfully uninstalled' def __init__(self, dialog): self.d = dialog self.path_python = app.paths.python def _set_state(self, state): self._states = { 'list': False, 'install': False, 'search': False, 'versions': False, } self._states[state] = True return def cmd_install_pip_action(self, event): msg = _("Do you want to install 'Python installer package' (PIP)?") if not app.question(msg, TITLE): return self._install_pip() return @app.run_in_thread def _install_pip(self): self.d.link_proyect.visible = False self.d.lst_log.visible = True path_pip = app.paths.tmp() self.d.lst_log.insert(_('Downloading PIP…')) data, h, err = app.url_open(URL_PIP, verify=False) if err: msg = _('Unable to connect to internet') app.errorbox('{}\n\n{}'.format(msg, err)) return app.paths.save_bin(path_pip, data) if not app.paths.exists(path_pip): msg = _('Unable to copy PIP installation file') app.errorbox(msg) return self.d.lst_log.insert(_('PIP installation file has been saved')) try: self.d.lst_log.insert(_("Starting PIP installation…")) cmd = f'"{self.path_python}" "{path_pip}" --user' for line in app.popen(cmd): if isinstance(line, tuple): app.errorbox(line) break self.d.lst_log.insert(line) cmd = self._cmd_pip('-V') label = app.run(cmd, True) if label: self.d.lbl_pip.value = label self.d.cmd_install_pip.visible = False self.d.cmd_admin_pip.visible = True msg = _('PIP was installed sucessfully') app.msgbox(msg) else: msg = _('PIP installation has failed, see log') app.warning(msg) except Exception as e: app.errorbox(e) return def _cmd_pip(self, args): cmd = '"{}" -m pip {}'.format(self.path_python, args) return cmd def cmd_admin_pip_action(self, event): self.d.lst_log.ps_from(self.d.lst_package) self.d.lst_log.step = 1 self.d.step = 1 self.cmd_home_action(None) return def cmd_close_action(self, event): self.d.close() return def cmd_home_action(self, event): self.d.txt_search.value = '' self._list() return def txt_search_key_released(self, event): if event.KeyCode == app.KEY['enter']: # ~ self.cmd_search_action(None) self.cmd_install_action(None) return if not self.d.txt_search.value.strip(): self.cmd_home_action(None) return @app.run_in_thread def _list(self): self._set_state('list') self.d.lst_log.visible = False self.d.lst_package.visible = True cmd = self._cmd_pip(' list --format=json') self.d.lst_package.clear() result = app.run(cmd, True) packages = app.json_loads(result) for p in packages: t = '{} - ({})'.format(p['name'], p['version']) self.d.lst_package.insert(t, ICON_OK) self.d.lst_package.select() self.d.txt_search.set_focus() return @app.run_in_thread def _search(self, value): self._set_state('search') line = '' cmd = self._cmd_pip(' search {}'.format(value)) self.d.lst_package.clear() for line in app.popen(cmd): parts = line.split(')') name = parts[0].strip() + ')' description = parts[-1].strip() parts = name.split('(') name_verify = parts[0].strip() package = '{} {}'.format(name, description) image = PACKAGES.get(name_verify, ICON_QUESTION) self.d.lst_package.insert(package, image) if line: self.d.lst_package.select() else: self.d.lst_package.insert(_('Package not found…'), 'error.png', show=False) return def cmd_search_action(self, event): search = self.d.txt_search.value.strip() if not search: self.d.txt_search.set_focus() return self._search(search) return def cmd_install_action(self, event): name = self.d.txt_search.value.strip() if not name: msg = _('Enter the package name to install') app.warning(msg) self.d.txt_search.set_focus() return msg = _('Do you really want to install package: {}?').format(name) if not app.question(msg): return self._install(name) return @app.run_in_thread def _install(self, value: str='', path: str=''): self._set_state('install') self.d.lst_package.visible = False self.d.lst_log.visible = True line = '' cmd = ' install --upgrade --user' if value: name = value.split(' ')[0].strip() cmd = self._cmd_pip(f'{cmd} {name}') else: cmd = self._cmd_pip(f'{cmd} -r "{path}"') self.d.lst_log.clear() for line in app.popen(cmd): if self.OK1 in line or self.OK2 in line: self.d.lst_log.insert(line, 'ok.png') else: self.d.lst_log.insert(line) return def lst_package_double_click(self, event): opt = _('install') if self._states['list']: opt = _('upgrade') name = self.d.lst_package.value msg = _('Do you want to {}:\n\n{}?').format(opt, name) if not app.question(msg, TITLE): return self._install(name) return @app.run_in_thread def _uninstall(self, value): self._set_state('install') self.d.lst_package.visible = False self.d.lst_log.visible = True line = '' name = value.split(' ')[0].strip() cmd = self._cmd_pip(' uninstall -y {}'.format(name)) self.d.lst_log.clear() for line in app.popen(cmd): if self.OK3 in line: self.d.lst_log.insert(line, 'ok.png') else: self.d.lst_log.insert(line) return def cmd_uninstall_action(self, event): if not self._states['list']: msg = _('Select installed package') app.warning(msg) return name = self.d.lst_package.value msg = _('Do you want to uninstall:\n\n{}?').format(name) if not app.question(msg): return self._uninstall(name) return def cmd_shell_action(self, event): if app.IS_WIN: cmd = '"{}"'.format(self.path_python) app.paths.open(cmd) else: cmd = 'exec "{}"' if app.IS_MAC: cmd = 'open "{}"' elif app.DESKTOP == 'gnome': cmd = 'gnome-terminal -- {}' cmd = cmd.format(self.path_python) app.run(cmd) return def cmd_explore_action(self, event): path = app.paths.get_file(filters='txt') if not path: return msg = _('Do you want to install from:\n\n{}').format(path) if not app.question(msg): return self._install(path=path) return def cmd_proxy_action(self, event): dialog = _create_dialog_proxy() data = app.get_config('proxy', {}, TITLE) dialog.set_values(data) dialog.open() return class ControllersProxy(object): def __init__(self, dialog): self.d = dialog def cmd_proxy_save_action(self, event): data = dict( txt_proxy_host = self.d.txt_proxy_host.value, txt_proxy_port = self.d.txt_proxy_port.value, txt_proxy_user = self.d.txt_proxy_user.value, txt_proxy_pass = self.d.txt_proxy_pass.value, ) msg = _('Do you want to save this Proxy settings?') if not app.question(msg): return app.set_config('proxy', data, TITLE) msg = _('Proxy settings saved sucessfully') app.msgbox(msg) self.d.close() return def cmd_proxy_delete_action(self, event): msg = _("Do you want to remove Proxy settings?") if not app.question(msg): return app.set_config('proxy', {}, TITLE) msg = _('Proxy settings deleted sucessfully') app.msgbox(msg) self.d.close() return def _create_dialog(): BUTTON_WH = 20 args= { 'Name': 'dialog', 'Title': 'Zaz-Pip', 'Width': 200, 'Height': 220, } dialog = app.create_dialog(args) dialog.id = ID_EXTENSION dialog.events = Controllers lbl_title = '{} {} - {}'.format(app.NAME, app.VERSION, app.OS) args = { 'Type': 'Label', 'Name': 'lbl_title', 'Label': lbl_title, 'Width': 100, 'Height': 15, 'Border': 1, 'Align': 1, 'VerticalAlign': 1, 'Step': 10, 'FontHeight': 12, } dialog.add_control(args) dialog.center(dialog.lbl_title, y=5) path_python = app.paths.python cmd = '"{}" -V'.format(path_python) label = app.run(cmd, True) args = { 'Type': 'Label', 'Name': 'lbl_python', 'Label': str(label), 'Width': 100, 'Height': 15, 'Border': 1, 'Align': 1, 'VerticalAlign': 1, 'Step': 10, 'FontHeight': 11, } dialog.add_control(args) dialog.center(dialog.lbl_python, y=25) cmd = '"{}" -m pip -V'.format(path_python) label = app.run(cmd, True) exists_pip = True if not label: exists_pip = False label = _("'Python installer package' (PIP) is not installed") args = { 'Type': 'Label', 'Name': 'lbl_pip', 'Label': label, 'Width': 160, 'Height': 30, 'Border': 1, 'Align': 1, 'VerticalAlign': 1, 'Step': 10, 'MultiLine': True, } dialog.add_control(args) dialog.center(dialog.lbl_pip, y=45) args = { 'Type': 'Button', 'Name': 'cmd_admin_pip', 'Label': _('Admin PIP'), 'Width': 70, 'Height': BUTTON_WH, 'Step': 10, 'ImageURL': 'python.svg', 'ImagePosition': 1, } dialog.add_control(args) dialog.center(dialog.cmd_admin_pip, y=80) args = { 'Type': 'Button', 'Name': 'cmd_install_pip', 'Label': _('Install PIP'), 'Width': 60, 'Height': BUTTON_WH, 'Step': 10, 'ImageURL': 'install.svg', 'ImagePosition': 1, } dialog.add_control(args) dialog.center(dialog.cmd_install_pip, y=80) args = { 'Type': 'Link', 'Name': 'link_proyect', 'URL': URL_GIT, 'Label': URL_GIT, 'Border': 1, 'Width': 130, 'Height': 15, 'Align': 1, 'VerticalAlign': 1, 'Step': 10, } dialog.add_control(args) dialog.center(dialog.link_proyect, y=-5) args = { 'Type': 'Button', 'Name': 'cmd_proxy', 'Width': 15, 'Height': 15, 'Step': 10, 'ImageURL': 'proxy.svg', 'FocusOnClick': False, } dialog.add_control(args) args = { 'Type': 'Listbox', 'Name': 'lst_log', 'Width': 160, 'Height': 105, 'Step': 10, } dialog.add_control(args) dialog.center(dialog.lst_log, y=105) args = { 'Type': 'Label', 'Name': 'lbl_package', 'Label': _('Packages'), 'Width': 100, 'Height': 15, 'Border': 1, 'Align': 1, 'VerticalAlign': 1, 'Step': 1, } lbl = dialog.add_control(args) args = { 'Type': 'Text', 'Name': 'txt_search', 'Width': 165, 'Height': 12, 'Step': 1, 'Border': 0, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_explore', 'Label': '...', 'Width': 12, 'Height': 12, 'Step': 1, } dialog.add_control(args) args = { 'Type': 'Listbox', 'Name': 'lst_package', 'Width': 180, 'Height': 128, 'Step': 1, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_close', 'Label': _('~Close'), 'Width': 70, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'close.svg', 'ImagePosition': 1, } dialog.add_control(args) dialog.center(dialog.cmd_close, y=-5) args = { 'Type': 'Button', 'Name': 'cmd_home', 'Width': BUTTON_WH, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'home.svg', 'FocusOnClick': False, 'Y': 2, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_search', 'Width': BUTTON_WH, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'search.svg', 'FocusOnClick': False, 'Enabled': False, 'Y': 2, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_uninstall', 'Width': BUTTON_WH, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'uninstall.svg', 'FocusOnClick': False, 'Y': 2, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_install', 'Width': BUTTON_WH, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'install.svg', 'FocusOnClick': False, 'Y': 2, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_shell', 'Width': BUTTON_WH, 'Height': BUTTON_WH, 'Step': 1, 'ImageURL': 'shell.svg', 'FocusOnClick': False, 'Y': 2, } dialog.add_control(args) dialog.cmd_proxy.move(dialog.link_proyect, x=5, y=0) controls = (dialog.link_proyect, dialog.cmd_proxy) dialog.center(controls) controls = (dialog.cmd_home, dialog.cmd_search, dialog.cmd_install, dialog.cmd_uninstall, dialog.cmd_shell) dialog.lbl_package.move(dialog.cmd_home) dialog.txt_search.move(dialog.lbl_package) dialog.cmd_explore.move(dialog.txt_search, x=0, y=0) dialog.lst_package.move(dialog.txt_search) dialog.lbl_package.center() dialog.lst_package.center() dialog.txt_search.center() dialog.center(controls) controls = (dialog.txt_search, dialog.cmd_explore) dialog.center(controls) dialog.step = 10 dialog.cmd_install_pip.visible = not exists_pip dialog.cmd_admin_pip.visible = exists_pip dialog.lst_log.visible = False return dialog def _create_dialog_proxy(): args= { 'Name': 'dlg_proxy', 'Title': _('Zaz-Pip - Proxy settings'), 'Width': 150, 'Height': 100, } dialog = app.create_dialog(args) dialog.id = ID_EXTENSION dialog.events = ControllersProxy args = { 'Type': 'Label', 'Name': 'lbl_proxy_host', 'Label': _('Host: '), 'Width': 40, 'Height': 12, 'Border': 1, 'Align': 2, 'VerticalAlign': 1, 'X': 5, 'Y': 5, } dialog.add_control(args) args = { 'Type': 'Text', 'Name': 'txt_proxy_host', 'Width': 95, 'Height': 12, 'Border': 0, } dialog.add_control(args) args = { 'Type': 'Label', 'Name': 'lbl_proxy_port', 'Label': _('Port: '), 'Width': 40, 'Height': 12, 'Border': 1, 'Align': 2, 'VerticalAlign': 1, } dialog.add_control(args) args = { 'Type': 'Text', 'Name': 'txt_proxy_port', 'Width': 95, 'Height': 12, 'Border': 0, } dialog.add_control(args) args = { 'Type': 'Label', 'Name': 'lbl_proxy_user', 'Label': _('User: '), 'Width': 40, 'Height': 12, 'Border': 1, 'Align': 2, 'VerticalAlign': 1, } dialog.add_control(args) args = { 'Type': 'Text', 'Name': 'txt_proxy_user', 'Width': 95, 'Height': 12, 'Border': 0, } dialog.add_control(args) args = { 'Type': 'Label', 'Name': 'lbl_proxy_pass', 'Label': _('Password: '), 'Width': 40, 'Height': 12, 'Border': 1, 'Align': 2, 'VerticalAlign': 1, } dialog.add_control(args) args = { 'Type': 'Text', 'Name': 'txt_proxy_pass', 'Width': 95, 'Height': 12, 'Border': 0, } txt = dialog.add_control(args) txt.echochar = '*' args = { 'Type': 'Button', 'Name': 'cmd_proxy_save', 'Label': _('Save'), 'Width': 50, 'Height': 15, 'ImageURL': 'save.svg', 'ImagePosition': 1, 'FocusOnClick': False, } dialog.add_control(args) args = { 'Type': 'Button', 'Name': 'cmd_proxy_delete', 'Label': _('Delete'), 'Width': 50, 'Height': 15, 'ImageURL': 'delete.svg', 'ImagePosition': 1, 'FocusOnClick': False, } dialog.add_control(args) dialog.txt_proxy_host.move(dialog.lbl_proxy_host, x=5, y=0) dialog.lbl_proxy_port.move(dialog.lbl_proxy_host) dialog.txt_proxy_port.move(dialog.lbl_proxy_port, x=5, y=0) dialog.lbl_proxy_user.move(dialog.lbl_proxy_port) dialog.txt_proxy_user.move(dialog.lbl_proxy_user, x=5, y=0) dialog.lbl_proxy_pass.move(dialog.lbl_proxy_user) dialog.txt_proxy_pass.move(dialog.lbl_proxy_pass, x=5, y=0) dialog.cmd_proxy_save.move(dialog.lbl_proxy_pass, y=10) dialog.cmd_proxy_delete.move(dialog.lbl_proxy_pass, y=10) controls = (dialog.cmd_proxy_save, dialog.cmd_proxy_delete) dialog.center(controls) return dialog