def __init__(self, parent=None): iTab.__init__(self, parent=parent) logging.Handler.__init__(self) self.levels = { 'NOTSET': dict(level=0, color='#808080'), 'DEBUG': dict(level=10, color='#808080'), 'INFO': dict(level=20, color='#000000'), 'WARNING': dict(level=30, color='#FF5500'), 'ERROR': dict(level=40, color='#FF0000'), 'CRITICAL': dict(level=50, color='#FF0000'), } self.records = [] self.setLevel(logging.DEBUG) self.setFormatter( logging.Formatter( '%(asctime)s: %(levelname)s: %(module)s.%(funcName)s - %(message)s', '%H:%M:%S')) self.combo = Combo(on_change=self.combo_changed, choices=['DEBUG', 'INFO', 'WARNING', 'ERROR']) self.filter_line_edit_msg = LineEdit('', self._filter_updated, clear_btn_enabled=True) self.filter_line_edit_module = LineEdit('', self._filter_updated, clear_btn_enabled=True) self.log_text = PlainTextEdit(read_only=True) self._min_lvl = self.levels[Config().log_level]['level'] self.combo.set_index_from_text(Config().log_level) self.clear_btn = PushButton('Clear log', self._clean) self.send_btn = PushButton('Send log', self._send) self.setLayout( VLayout([ HLayout([ (self.combo, dict(stretch=1)), 20, (self.clear_btn, dict(stretch=0)), (self.send_btn, dict(stretch=0)), ]), GridLayout([ [Label('Filter message'), self.filter_line_edit_msg], [Label('Filter module'), self.filter_line_edit_module], ]), self.log_text, ])) logger = logging.getLogger('__main__') logger.addHandler(self) self._clean()
def __init__(self): self.auto_group = GroupBox() auto_help = QLabel('Looks for the latest TRMT MIZ (must be named "TRMT_*.miz") in the source folder.') self._latest_trmt = None self.auto_src_le = QLineEdit() if Config().auto_source_folder: self.auto_src_le.setText(Config().auto_source_folder) self.auto_src_le.setEnabled(False) self.auto_src_browse_btn = PushButton('Browse', self.auto_src_browse) self.auto_src_open_btn = PushButton('Open', self.auto_src_open) self.auto_scan_label_local = QLabel('') self.auto_scan_label_remote = Label('') self.auto_scan_combo_branch = Combo(self._branch_changed, ['All'] + github.get_available_branches()) try: self.auto_scan_combo_branch.set_index_from_text(Config().selected_TRMT_branch) except ValueError: pass self.auto_scan_btn = PushButton('Refresh', self.scan) self.auto_scan_download_btn = PushButton('Download', self.download) self.auto_out_le = QLineEdit() self.auto_out_le.setEnabled(False) self.auto_out_browse_btn = PushButton('Browse', self.auto_out_browse) self.auto_out_open_btn = PushButton('Open', self.auto_out_open) scan_layout = HLayout([ QLabel('Latest local version of the TRMT:'), (self.auto_scan_label_local, dict(stretch=1)), QLabel('Latest remote version of the TRMT:'), self.auto_scan_combo_branch, (self.auto_scan_label_remote, dict(stretch=1)), ]) self.auto_reorder_btn = PushButton('Reorder MIZ file', self.auto_reorder) self.auto_reorder_btn.setMinimumHeight(40) self.auto_reorder_btn.setMinimumWidth(400) self.auto_layout = VLayout([ auto_help, GridLayout( [ [ (Label('Source folder'), dict(align='r')), self.auto_src_le, self.auto_src_browse_btn, self.auto_src_open_btn, ], [ (Label('Output folder'), dict(align='r')), self.auto_out_le, self.auto_out_browse_btn, self.auto_out_open_btn, ], [ None, scan_layout, self.auto_scan_btn, self.auto_scan_download_btn ], ] ), self.auto_reorder_btn ]) self.auto_group.setLayout(self.auto_layout) if Config().auto_output_folder: self.auto_out_le.setText(Config().auto_output_folder) if Config().auto_source_folder: self.auto_src_le.setText(Config().auto_source_folder)
class _AutoLayout: def __init__(self): self.auto_group = GroupBox() auto_help = QLabel('Looks for the latest TRMT MIZ (must be named "TRMT_*.miz") in the source folder.') self._latest_trmt = None self.auto_src_le = QLineEdit() if Config().auto_source_folder: self.auto_src_le.setText(Config().auto_source_folder) self.auto_src_le.setEnabled(False) self.auto_src_browse_btn = PushButton('Browse', self.auto_src_browse) self.auto_src_open_btn = PushButton('Open', self.auto_src_open) self.auto_scan_label_local = QLabel('') self.auto_scan_label_remote = Label('') self.auto_scan_combo_branch = Combo(self._branch_changed, ['All'] + github.get_available_branches()) try: self.auto_scan_combo_branch.set_index_from_text(Config().selected_TRMT_branch) except ValueError: pass self.auto_scan_btn = PushButton('Refresh', self.scan) self.auto_scan_download_btn = PushButton('Download', self.download) self.auto_out_le = QLineEdit() self.auto_out_le.setEnabled(False) self.auto_out_browse_btn = PushButton('Browse', self.auto_out_browse) self.auto_out_open_btn = PushButton('Open', self.auto_out_open) scan_layout = HLayout([ QLabel('Latest local version of the TRMT:'), (self.auto_scan_label_local, dict(stretch=1)), QLabel('Latest remote version of the TRMT:'), self.auto_scan_combo_branch, (self.auto_scan_label_remote, dict(stretch=1)), ]) self.auto_reorder_btn = PushButton('Reorder MIZ file', self.auto_reorder) self.auto_reorder_btn.setMinimumHeight(40) self.auto_reorder_btn.setMinimumWidth(400) self.auto_layout = VLayout([ auto_help, GridLayout( [ [ (Label('Source folder'), dict(align='r')), self.auto_src_le, self.auto_src_browse_btn, self.auto_src_open_btn, ], [ (Label('Output folder'), dict(align='r')), self.auto_out_le, self.auto_out_browse_btn, self.auto_out_open_btn, ], [ None, scan_layout, self.auto_scan_btn, self.auto_scan_download_btn ], ] ), self.auto_reorder_btn ]) self.auto_group.setLayout(self.auto_layout) if Config().auto_output_folder: self.auto_out_le.setText(Config().auto_output_folder) if Config().auto_source_folder: self.auto_src_le.setText(Config().auto_source_folder) @abc.abstractmethod def _branch_changed(self): """""" def auto_reorder(self): if self.latest_trmt and self.auto_out_path: self.reorder_miz(self.latest_trmt, self.auto_out_path, self.skip_options_file) def auto_out_open(self): if self.auto_out_path.exists(): os.startfile(self.auto_out_path) def auto_out_browse(self): if self.auto_out_path: init_dir = self.auto_out_path.dirname() else: init_dir = Path('.') p = BrowseDialog.get_directory(self, 'Select output directory', init_dir=init_dir.abspath()) if p: p = Path(p) self.auto_out_le.setText(p.abspath()) Config().auto_output_folder = p.abspath() @property def auto_out_path(self) -> Path or None: t = self.auto_out_le.text() if len(t) > 3: return Path(t) return None @property @abc.abstractmethod def selected_branch(self): """""" @abc.abstractmethod def scan(self): """""" @property @abc.abstractmethod def pool(self) -> ThreadPool: """""" @staticmethod def get_av_token(): # noinspection SpellCheckingInspection webbrowser.open_new_tab(r'https://ci.appveyor.com/api-token') def _download(self): def proceed_with_download(): downloader.download( url=dl_url, local_file=local_file, progress_title='Downloading {}'.format(dl_url.split('/').pop()), progress_text=local_file, file_size=file_size ) dl_url, file_size, local_file_name = appveyor.latest_version_download_url(self.selected_branch) local_file = Path(self.auto_src_path).joinpath(local_file_name).abspath() if local_file.exists(): I.confirm(text='Local file already exists; do you want to overwrite?', follow_up=proceed_with_download) else: proceed_with_download() def download(self): self.pool.queue_task(self._download) self.scan() @property def auto_src_path(self) -> Path or None: t = self.auto_src_le.text() if len(t) > 3: return Path(t) return None def auto_src_browse(self): if self.auto_src_path: init_dir = self.auto_src_path.dirname() else: init_dir = get_saved_games_path() p = BrowseDialog.get_directory(self, 'Select source directory', init_dir=init_dir.abspath()) if p: p = Path(p) self.auto_src_le.setText(p.abspath()) Config().auto_source_folder = p.abspath() self.scan() def auto_src_open(self): if self.auto_src_path: os.startfile(self.auto_src_path.abspath()) @property def latest_trmt(self) -> Path or None: return self._latest_trmt @abc.abstractmethod def reorder_miz(self, miz_file, output_dir, skip_options_file): """""" @property @abc.abstractmethod def skip_options_file(self) -> bool: """"""
def __init__(self, parent=None): iTab.__init__(self, parent=parent) self.sg = LineEdit(Config().saved_games_path or '', self._on_change_sg, read_only=True) self.update_channel_combo = Combo(self._on_change_update_channel, [ 'stable', 'rc', 'dev', 'beta', 'alpha' ]) self.latest_release = None self.remote_version = Label('') self.update_channel_combo.set_index_from_text(Config().update_channel) self.update_scan_btn = PushButton('Check for new version', self._check_for_new_version) self.install_new_version_btn = PushButton('Install this version', self._install_latest_version) updater_layout = GroupBox( 'Auto-update', VLayout( [ Label('During the initial testing phase of this application, auto-update cannot be turned off.\n' 'You can, however, elect to participate in early testing, or stick to the most stable' ' versions only.'), 10, GridLayout( [ [ Label('Active update channel'), HLayout( [ self.update_channel_combo, self.update_scan_btn, HSpacer() ] ), ], [10], [ Label('Current version'), Label(__version__), ], [ Label('Remote version'), HLayout([self.remote_version, self.install_new_version_btn, HSpacer()]), ], # [ # Label('GUID'), # Label(__guid__), # ], ], [0, 1] ) ] ) ) sg_path_layout = GroupBox( 'Saved Games directory', GridLayout( [ [ self.sg, PushButton('Browse', self._sg_browse), PushButton('Scan', self._sg_scan), PushButton('Open', self._sg_open), ] ], [1], False ) ) dcs_installations = [] for x in ['stable', 'beta', 'alpha']: setattr(self, '{}_group'.format(x), GroupBox('DCS {} installation'.format(x))) setattr(self, '{}_install'.format(x), Label('')) setattr(self, '{}_variant'.format(x), Label('')) setattr(self, '{}_version'.format(x), Label('')) getattr(self, '{}_group'.format(x)).setLayout( GridLayout( [ [Label('Installation'), getattr(self, '{}_install'.format(x)), ], [Label('Variant'), getattr(self, '{}_variant'.format(x))], [Label('Version'), getattr(self, '{}_version'.format(x))], ], [0, 1] ) ) dcs_installations.append(getattr(self, '{}_group'.format(x))) dcs_installations = VLayout( [ *dcs_installations ] ) self.setLayout( VLayout( [ updater_layout, VSpacer(), sg_path_layout, VSpacer(), dcs_installations, VSpacer(), ] ) ) self.install_new_version_btn.setVisible(False)
class TabConfig(iTab): @property def tab_title(self) -> str: return 'Config' def __init__(self, parent=None): iTab.__init__(self, parent=parent) self.sg = LineEdit(Config().saved_games_path or '', self._on_change_sg, read_only=True) self.update_channel_combo = Combo(self._on_change_update_channel, [ 'stable', 'rc', 'dev', 'beta', 'alpha' ]) self.latest_release = None self.remote_version = Label('') self.update_channel_combo.set_index_from_text(Config().update_channel) self.update_scan_btn = PushButton('Check for new version', self._check_for_new_version) self.install_new_version_btn = PushButton('Install this version', self._install_latest_version) updater_layout = GroupBox( 'Auto-update', VLayout( [ Label('During the initial testing phase of this application, auto-update cannot be turned off.\n' 'You can, however, elect to participate in early testing, or stick to the most stable' ' versions only.'), 10, GridLayout( [ [ Label('Active update channel'), HLayout( [ self.update_channel_combo, self.update_scan_btn, HSpacer() ] ), ], [10], [ Label('Current version'), Label(__version__), ], [ Label('Remote version'), HLayout([self.remote_version, self.install_new_version_btn, HSpacer()]), ], # [ # Label('GUID'), # Label(__guid__), # ], ], [0, 1] ) ] ) ) sg_path_layout = GroupBox( 'Saved Games directory', GridLayout( [ [ self.sg, PushButton('Browse', self._sg_browse), PushButton('Scan', self._sg_scan), PushButton('Open', self._sg_open), ] ], [1], False ) ) dcs_installations = [] for x in ['stable', 'beta', 'alpha']: setattr(self, '{}_group'.format(x), GroupBox('DCS {} installation'.format(x))) setattr(self, '{}_install'.format(x), Label('')) setattr(self, '{}_variant'.format(x), Label('')) setattr(self, '{}_version'.format(x), Label('')) getattr(self, '{}_group'.format(x)).setLayout( GridLayout( [ [Label('Installation'), getattr(self, '{}_install'.format(x)), ], [Label('Variant'), getattr(self, '{}_variant'.format(x))], [Label('Version'), getattr(self, '{}_version'.format(x))], ], [0, 1] ) ) dcs_installations.append(getattr(self, '{}_group'.format(x))) dcs_installations = VLayout( [ *dcs_installations ] ) self.setLayout( VLayout( [ updater_layout, VSpacer(), sg_path_layout, VSpacer(), dcs_installations, VSpacer(), ] ) ) self.install_new_version_btn.setVisible(False) def update_config_tab(self, latest_release: AVRelease): self.remote_version.set_text_color('black') for x in ['stable', 'beta', 'alpha']: dcs_install = getattr(dcs_installs, x) if dcs_install: getattr(self, '{}_install'.format(x)).setText(dcs_install.install_path) getattr(self, '{}_variant'.format(x)).setText(dcs_install.saved_games) getattr(self, '{}_version'.format(x)).setText(dcs_install.version) else: getattr(self, '{}_install'.format(x)).setText('not found') self.update_channel_combo.set_index_from_text(Config().update_channel) if latest_release: app_version = Version(global_.APP_VERSION) self.latest_release = latest_release self.remote_version.setText(latest_release.version.version_str) if app_version < self.latest_release.version: self.remote_version.set_text_color('green') if app_version != self.latest_release.version: self.install_new_version_btn.setVisible(True) def _on_change_sg(self, *_): Config().saved_games_path = str(Path(self.sg.text()).abspath()) def _on_change_update_channel(self, *_): Config().update_channel = self.update_channel_combo.currentText() self.remote_version.setText('') self._check_for_new_version() def _check_for_new_version(self): if hasattr(self, 'install_new_version_btn'): self.install_new_version_btn.setVisible(False) updater.get_latest_release( channel=Config().update_channel, branch=Version(global_.APP_VERSION), success_callback=I.update_config_tab, ) def _install_latest_version(self): if self.latest_release: logger.debug('installing release: {}'.format(self.latest_release.version)) updater.download_and_install_release(self.latest_release, 'emft.exe') else: logger.error('no release to install') # self.updater.install_latest_remote() def _sg_browse(self): p = BrowseDialog.get_directory(self, 'Saved Games directory', Path(self.sg.text()).dirname()) if p: p = str(Path(p).abspath()) self.sg.setText(p) def _sg_scan(self): self.sg.setText(dcs_installs.discover_saved_games_path()) def _sg_open(self): os.startfile(self.sg.text()) def stable_open(self): pass def alpha_open(self): pass def beta_open(self): pass
def __init__(self): super(TabSkins, self).__init__() self.no_install_label = Label( 'No DSC installation found on this system') self.no_install_label.set_text_color('red') self.no_install_label.setVisible( len(list(dcs_installs.present_dcs_installations)) == 0) self.allow_mv_autoexec_changes = Checkbox( 'Allow EMFT to edit the "autoexec.cfg" of DCS ModelViewer to inject required VFS textures paths', self._on_allow_mv_autoexec_changes, ) self.allow_mv_autoexec_changes.setChecked( Config().allow_mv_autoexec_changes) def _make_filter(): return LineEdit('', on_text_changed=self._apply_filter, clear_btn_enabled=True) self.filter_skin_name = _make_filter() self.filter_ac_name = _make_filter() self.filter_folder = _make_filter() self.combo_active_dcs_installation = Combo( self._on_active_dcs_installation_change, list(x.label for x in dcs_installs.present_dcs_installations), ) if Config().skins_active_dcs_installation: try: self.combo_active_dcs_installation.set_index_from_text( Config().skins_active_dcs_installation) except ValueError: pass data = [] header_data = ['Skin name', 'Aircraft', 'Containing folder'] self._menu = Menu('Title') # self._menu.add_action('Test menu', self._test_menu) self._menu.add_action('Open skin folder', self._context_open_folder) # self._menu.add_action('Show skin in model viewer', self._show_skin_in_model_viewer) self.table = TableViewWithSingleRowMenu(self._menu) self.table.setSelectionMode(self.table.SingleSelection) # noinspection PyUnresolvedReferences self.table.doubleClicked.connect(self._table_double_clicked) self.model = TableModel(data, header_data) self.proxy = TableProxy() self.proxy.setSourceModel(self.model) # noinspection PyUnresolvedReferences self.model.modelReset.connect(self.proxy.default_sort) self.table.setModel(self.proxy) self.setLayout( VLayout( [ # self.allow_mv_autoexec_changes, HLayout([ Label('Active DCS installation:'), self.combo_active_dcs_installation, self.no_install_label, HSpacer(), ]), GroupBox( 'Filters', GridLayout([ [ Label('Skin name:'), self.filter_skin_name, ], [ Label('Aircraft:'), self.filter_ac_name, ], [ Label('Folder:'), self.filter_folder, ], ]), ), (self.table, dict(stretch=1)), ], )) self._display_list_of_skins_for_currently_selected_install()
class TabSkins(iTab): @property def tab_title(self) -> str: return 'Skins' def __init__(self): super(TabSkins, self).__init__() self.no_install_label = Label( 'No DSC installation found on this system') self.no_install_label.set_text_color('red') self.no_install_label.setVisible( len(list(dcs_installs.present_dcs_installations)) == 0) self.allow_mv_autoexec_changes = Checkbox( 'Allow EMFT to edit the "autoexec.cfg" of DCS ModelViewer to inject required VFS textures paths', self._on_allow_mv_autoexec_changes, ) self.allow_mv_autoexec_changes.setChecked( Config().allow_mv_autoexec_changes) def _make_filter(): return LineEdit('', on_text_changed=self._apply_filter, clear_btn_enabled=True) self.filter_skin_name = _make_filter() self.filter_ac_name = _make_filter() self.filter_folder = _make_filter() self.combo_active_dcs_installation = Combo( self._on_active_dcs_installation_change, list(x.label for x in dcs_installs.present_dcs_installations), ) if Config().skins_active_dcs_installation: try: self.combo_active_dcs_installation.set_index_from_text( Config().skins_active_dcs_installation) except ValueError: pass data = [] header_data = ['Skin name', 'Aircraft', 'Containing folder'] self._menu = Menu('Title') # self._menu.add_action('Test menu', self._test_menu) self._menu.add_action('Open skin folder', self._context_open_folder) # self._menu.add_action('Show skin in model viewer', self._show_skin_in_model_viewer) self.table = TableViewWithSingleRowMenu(self._menu) self.table.setSelectionMode(self.table.SingleSelection) # noinspection PyUnresolvedReferences self.table.doubleClicked.connect(self._table_double_clicked) self.model = TableModel(data, header_data) self.proxy = TableProxy() self.proxy.setSourceModel(self.model) # noinspection PyUnresolvedReferences self.model.modelReset.connect(self.proxy.default_sort) self.table.setModel(self.proxy) self.setLayout( VLayout( [ # self.allow_mv_autoexec_changes, HLayout([ Label('Active DCS installation:'), self.combo_active_dcs_installation, self.no_install_label, HSpacer(), ]), GroupBox( 'Filters', GridLayout([ [ Label('Skin name:'), self.filter_skin_name, ], [ Label('Aircraft:'), self.filter_ac_name, ], [ Label('Folder:'), self.filter_folder, ], ]), ), (self.table, dict(stretch=1)), ], )) self._display_list_of_skins_for_currently_selected_install() def _table_double_clicked(self, index): if index.isValid(): self._context_open_folder(index.row()) def _context_open_folder(self, row): os.startfile(self.proxy.data(self.proxy.index(row, 2))) def _show_skin_in_model_viewer(self, row): skin_name = self.proxy.data(self.proxy.index(row, 0)) ac_name = self.proxy.data(self.proxy.index(row, 1)) mv_autoexec_cfg = Path(self._active_dcs_install.install_path).joinpath( 'Config', 'ModelViewer', 'autoexec.lua') mv_exe = Path(self._active_dcs_install.install_path).joinpath( 'bin', 'ModelViewer.exe') for f in mv_autoexec_cfg, mv_exe: if not f.exists(): logger.error('file not found: {}'.format(f.abspath())) return mount_lines = set() if self._active_dcs_install.autoexec_cfg: for vfs_path in self._active_dcs_install.autoexec_cfg.mounted_vfs_paths: mount_lines.add( 'mount_vfs_texture_path("{}")\n'.format(vfs_path)) backup_path = mv_autoexec_cfg.dirname().joinpath( 'autoexec.lua_EMFT_BACKUP') if not backup_path.exists(): logger.info('backing up "{}" -> "{}"'.format( mv_autoexec_cfg.abspath(), backup_path.abspath())) mv_autoexec_cfg.copy2(backup_path.abspath()) orig_lines = mv_autoexec_cfg.lines() lines = [] for line in orig_lines: if Config().allow_mv_autoexec_changes: if RE_MOUNT_LINE.match(line): # print('skipping', line) continue if RE_LOAD_MODEL_LINE.match(line): # print('skipping', line) continue if RE_LOAD_LIVERY_LINE.match(line): # print('skipping', line) continue lines.append(line) # model_path = 'LoadModel("Bazar/World/Shapes/{}.edm")'.format(self._active_dcs_install.get_object_model(ac_name)) lines.insert( 0, 'LoadLivery("{ac_name}","{skin_name}")'.format(**locals())) lines.insert( 0, 'LoadModel("Bazar/World/Shapes/{ac_name}.edm")'.format(**locals())) if Config().allow_mv_autoexec_changes: for line in mount_lines: lines.insert(0, line) mv_autoexec_cfg.write_lines(lines) os.startfile(mv_exe.abspath()) def _test_menu(self, row): print(self.proxy.data(self.proxy.index(row, 0))) @property def _active_dcs_install(self) -> DCSInstall: if self.combo_active_dcs_installation.currentText(): return getattr(dcs_installs, self.combo_active_dcs_installation.currentText()) def _display_list_of_skins_for_currently_selected_install(self): def skin_to_data_line(skin: DCSSkin): return [skin.skin_nice_name, skin.ac, skin.root_folder] if self._active_dcs_install: self.model.reset_data([ skin_to_data_line(skin) for skin in self._active_dcs_install.skins.values() ]) self.table.resizeColumnsToContents() def _on_active_dcs_installation_change(self): Config( ).skins_active_dcs_installation = self.combo_active_dcs_installation.currentText( ) self._display_list_of_skins_for_currently_selected_install() def _apply_filter(self): self.proxy.filter( self.filter_skin_name.text(), self.filter_ac_name.text(), self.filter_folder.text(), ) def _on_allow_mv_autoexec_changes(self): Config( ).allow_mv_autoexec_changes = self.allow_mv_autoexec_changes.isChecked( )
class TabLog(iTab, logging.Handler): @property def tab_title(self) -> str: return 'Log' def __init__(self, parent=None): iTab.__init__(self, parent=parent) logging.Handler.__init__(self) self.levels = { 'NOTSET': dict(level=0, color='#808080'), 'DEBUG': dict(level=10, color='#808080'), 'INFO': dict(level=20, color='#000000'), 'WARNING': dict(level=30, color='#FF5500'), 'ERROR': dict(level=40, color='#FF0000'), 'CRITICAL': dict(level=50, color='#FF0000'), } self.records = [] self.setLevel(logging.DEBUG) self.setFormatter( logging.Formatter( '%(asctime)s: %(levelname)s: %(module)s.%(funcName)s - %(message)s', '%H:%M:%S')) self.combo = Combo(on_change=self.combo_changed, choices=['DEBUG', 'INFO', 'WARNING', 'ERROR']) self.filter_line_edit_msg = LineEdit('', self._filter_updated, clear_btn_enabled=True) self.filter_line_edit_module = LineEdit('', self._filter_updated, clear_btn_enabled=True) self.log_text = PlainTextEdit(read_only=True) self._min_lvl = self.levels[Config().log_level]['level'] self.combo.set_index_from_text(Config().log_level) self.clear_btn = PushButton('Clear log', self._clean) self.send_btn = PushButton('Send log', self._send) self.setLayout( VLayout([ HLayout([ (self.combo, dict(stretch=1)), 20, (self.clear_btn, dict(stretch=0)), (self.send_btn, dict(stretch=0)), ]), GridLayout([ [Label('Filter message'), self.filter_line_edit_msg], [Label('Filter module'), self.filter_line_edit_module], ]), self.log_text, ])) logger = logging.getLogger('__main__') logger.addHandler(self) self._clean() def _filter_updated(self): self._clean() for rec in self.records: assert isinstance(rec, logging.LogRecord) if self.filter_line_edit_msg.text(): if not self.filter_line_edit_msg.text() in rec.msg: continue if self.filter_line_edit_module.text(): if not self.filter_line_edit_module.text() in rec.module: continue self.write(self.format(rec)) def emit(self, record: logging.LogRecord): self.records.append(record) if record.levelno >= self._min_lvl: I.write_log(self.format(record), str(self.levels[record.levelname]['color'])) def write(self, msg, color='#000000', bold=False): msg = '<font color="{}">{}</font>'.format(color, msg) if bold: msg = '<b>{}</b>'.format(msg) self.log_text.appendHtml(msg) def combo_changed(self, new_value): Config().log_level = new_value self._set_log_level(new_value) def _send(self): content = [] for rec in self.records: assert isinstance(rec, logging.LogRecord) content.append(self.format(rec)) # SENTRY.add_crumb(rec.msg, 'LOG:{}'.format(rec.module), rec.levelname) url = create_new_paste('\n'.join(content)) if url: SENTRY.captureMessage('Logfile', extra={'log_url': url}) self.write('Log file sent; thank you !') else: self.write('Could not send log file') def _clean(self): self.log_text.clear() self.log_text.appendHtml('<b>Running EMFT v{}</b>'.format( global_.APP_VERSION)) def _set_log_level(self, log_level): self._clean() self._min_lvl = self.levels[log_level]['level'] for rec in self.records: assert isinstance(rec, logging.LogRecord) if rec.levelno >= self._min_lvl: self.write(self.format(rec), color=self.levels[rec.levelname]['color'])