From f2aefa67ff18c6b171579a136d5e3744f45a75e7 Mon Sep 17 00:00:00 2001 From: "St.Huang" Date: Thu, 9 Aug 2018 19:34:49 +0800 Subject: [PATCH] multi config supported --- addons/fastwq/gui/base.py | 6 +- addons/fastwq/gui/options.py | 361 ++++++++++++++++++++------------ addons/fastwq/lang.py | 1 + addons/fastwq/query/__init__.py | 4 +- addons/fastwq/query/common.py | 6 +- 5 files changed, 240 insertions(+), 138 deletions(-) diff --git a/addons/fastwq/gui/base.py b/addons/fastwq/gui/base.py index 6cad186..bdd2c2a 100644 --- a/addons/fastwq/gui/base.py +++ b/addons/fastwq/gui/base.py @@ -55,10 +55,10 @@ class WidgetSize(object): ''' constant values ''' - dialog_width = 700 - dialog_height_margin = 120 + dialog_width = 720 + dialog_height_margin = 146 map_min_height = 0 - map_max_height = 32 + map_max_height = 30 map_fld_width = 100 map_dictname_width = 150 map_dictfield_width = 160 diff --git a/addons/fastwq/gui/options.py b/addons/fastwq/gui/options.py index df29d02..c38af05 100644 --- a/addons/fastwq/gui/options.py +++ b/addons/fastwq/gui/options.py @@ -59,10 +59,9 @@ class OptionsDialog(Dialog): #self.loading_layout.addLayout(models_layout) self.setLayout(self.main_layout) #initlize properties - self.___last_checkeds___ = None - self.___options___ = list() self.model_id = model_id if model_id != -1 else config.last_model_id self.current_model = None + self.tabs = [] # size and signal self.resize(WIDGET_SIZE.dialog_width, 4 * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin) self._signal.emit('before_build') @@ -92,17 +91,16 @@ class OptionsDialog(Dialog): models_layout.addWidget(mdx_button) models_layout.addWidget(self.models_button) self.main_layout.addLayout(models_layout) - # add dicts mapping - dicts_widget = QWidget() - self.dicts_layout = QGridLayout() - self.dicts_layout.setSizeConstraint(QLayout.SetMinAndMaxSize) - dicts_widget.setLayout(self.dicts_layout) - - scroll_area = QScrollArea() - scroll_area.setWidgetResizable(True) - scroll_area.setWidget(dicts_widget) - - self.main_layout.addWidget(scroll_area) + # tabs + self.tab_widget = QTabWidget() + self.tab_bar = TabBarPlus() + self.tab_widget.setTabBar(self.tab_bar) + self.tab_widget.setTabsClosable(True) + # signals + self.tab_bar.plusClicked.connect(self.addTab) + self.tab_widget.tabCloseRequested.connect(self.removeTab) + # layout + self.main_layout.addWidget(self.tab_widget) # add description of radio buttons AND ok button bottom_layout = QHBoxLayout() paras_btn = QPushButton(_('SETTINGS')) @@ -113,18 +111,16 @@ class OptionsDialog(Dialog): chk_update_btn = QPushButton(_('UPDATE')) chk_update_btn.clicked.connect(self.check_updates) home_label = QLabel( - 'User Guide'.format(url=Endpoint.user_guide)) + 'User Guide'.format(url=Endpoint.user_guide) + ) home_label.setOpenExternalLinks(True) - # shop_label = QLabel( - # 'Service Shop'.format(url=Endpoint.service_shop)) - # shop_label.setOpenExternalLinks(True) + # buttons btnbox = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal, self) btnbox.accepted.connect(self.accept) bottom_layout.addWidget(paras_btn) bottom_layout.addWidget(chk_update_btn) bottom_layout.addWidget(about_btn) bottom_layout.addWidget(home_label) - # bottom_layout.addWidget(shop_label) bottom_layout.addWidget(btnbox) #self.setLayout(self.main_layout) self.main_layout.addLayout(bottom_layout) @@ -136,7 +132,7 @@ class OptionsDialog(Dialog): self.models_button.setText( u'%s [%s]' % (_('CHOOSE_NOTE_TYPES'), self.current_model['name'])) # build fields -- dicts layout - self.build_mappings_layout(self.current_model) + self.build_tabs_layout(self.current_model) def show_paras(self): '''open setting dialog''' @@ -172,55 +168,57 @@ class OptionsDialog(Dialog): self.save() self.current_model = self.show_models() if self.current_model: - self.build_mappings_layout(self.current_model) + self.build_tabs_layout(self.current_model) - def build_mappings_layout(self, model): + def build_tabs_layout(self, model): ''' build dictionary、fields etc ''' - def clear_layout(layout): - if layout is not None: - while layout.count(): - item = layout.takeAt(0) - widget = item.widget() - if widget is not None: - widget.deleteLater() - else: - clear_layout(item.layout()) - - clear_layout(self.dicts_layout) - del self.___options___[:] - self.___last_checkeds___ = None - - labels = ['', '', 'DICTS', 'DICT_FIELDS', ''] - for i, s in enumerate(labels): - label = QLabel(_(s)) - label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) - self.dicts_layout.addWidget(label, 0, i) - - maps = config.get_maps(model['id']) - self.radio_group = QButtonGroup() - for i, fld in enumerate(model['flds']): - ord = fld['ord'] - name = fld['name'] - if maps: - for j, each in enumerate(maps): - if each.get('fld_ord', -1) == ord or each.get('fld_name', '') == name: - each['fld_name'] = name - each['fld_ord'] = ord - self.add_dict_layout(j, **each) - break - else: - self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0) - else: - self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0) - - #self.setLayout(self.main_layout) - self.resize( - WIDGET_SIZE.dialog_width, - max(3, (i + 1)) * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin - ) + try: self.tab_widget.currentChanged.disconnect() + except Exception: pass + while len(self.tabs) > 0: + self.removeTab(0, True) + # + conf = config.get_maps(model['id']) + length = 0 + maps_list = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf + for i, maps in enumerate(maps_list['list']): + length = len(maps) + tab = TabContent(model, maps) + self.tabs.append(tab) + self.tab_widget.addTab(tab, _('CONFIG_INDEX') % (i+1)) + self.tab_widget.setCurrentIndex(maps_list['def']) + self.tab_widget.currentChanged.connect(self.changedTab) + self.changedTab(self.tab_widget.currentIndex()) + # size + #self.resize( + # WIDGET_SIZE.dialog_width, + # length * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin + #) + self.adjustSize() self.save() + + def addTab(self): + tab = TabContent(self.current_model, None) + i = len(self.tabs) + self.tabs.append(tab) + self.tab_widget.addTab(tab, _('CONFIG_INDEX') % (i+1)) + self.tab_widget.setCurrentIndex(i) + + def removeTab(self, i, forcus=False): + # less than one config + if not forcus and len(self.tabs) <= 1: + return + tab = self.tabs[i] + del self.tabs[i] + self.tab_widget.removeTab(i) + tab.destroy() + for k in range(0, len(self.tabs)): + self.tab_widget.setTabText(k, _('CONFIG_INDEX') % (k+1)) + + def changedTab(self, i): + tab = self.tabs[i] + tab.build_mappings_layout() def show_models(self): ''' @@ -238,69 +236,72 @@ class OptionsDialog(Dialog): u'%s [%s]' % (_('CHOOSE_NOTE_TYPES'), ret.name)) return model - def fill_dict_combo_options(self, dict_combo, current_unique): - '''setup dict combo box''' - dict_combo.clear() - #dict_combo.addItem(_('NOT_DICT_FIELD')) + def save(self): + '''save config to file''' + if not self.current_model: + return + data = dict() + maps_list = { + 'list': [], + 'def': self.tab_widget.currentIndex() + } + for tab in self.tabs: + maps_list['list'].append(tab.data) + current_model_id = str(self.current_model['id']) + data[current_model_id] = maps_list + data['last_model'] = self.current_model['id'] + config.update(data) - # local dict service - #dict_combo.insertSeparator(dict_combo.count()) - has_local_service = False - for cls in service_manager.local_services: - # combo_data.insert("data", each.label) - service = service_pool.get(cls.__unique__) - if service and service.support: - dict_combo.addItem( - service.title, userData=service.unique) - service_pool.put(service) - has_local_service = True - # hr - if has_local_service: - dict_combo.insertSeparator(dict_combo.count()) - - # web dict service - for cls in service_manager.web_services: - service = service_pool.get(cls.__unique__) - if service and service.support: - dict_combo.addItem( - service.title, userData=service.unique) - service_pool.put(service) +class TabContent(QWidget): + '''Options tab content''' - def set_dict_combo_index(): - #dict_combo.setCurrentIndex(-1) - dict_combo.setCurrentIndex(0) - if current_unique: - for i in range(dict_combo.count()): - if dict_combo.itemData(i) == current_unique: - dict_combo.setCurrentIndex(i) + def __init__(self, model, conf): + super(TabContent, self).__init__() + self._conf = conf + self._model = model + self.___last_checkeds___ = None + self.___options___ = list() + self.___was_built___ = False + # add dicts mapping + self.dicts_layout = QGridLayout() + self.dicts_layout.setSizeConstraint(QLayout.SetMinAndMaxSize) + self.setLayout(self.dicts_layout) + + def build_mappings_layout(self): + ''' + build dictionary、fields etc + ''' + if self.___was_built___: + return + + del self.___options___[:] + self.___last_checkeds___ = None + self.___was_built___ = True + + model = self._model + maps = self._conf + labels = ['', '', 'DICTS', 'DICT_FIELDS', ''] + for i, s in enumerate(labels): + label = QLabel(_(s)) + label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) + self.dicts_layout.addWidget(label, 0, i) + + self.radio_group = QButtonGroup() + for i, fld in enumerate(model['flds']): + ord = fld['ord'] + name = fld['name'] + if maps: + for j, each in enumerate(maps): + if each.get('fld_ord', -1) == ord or each.get('fld_name', '') == name: + each['fld_name'] = name + each['fld_ord'] = ord + self.add_dict_layout(j, **each) break - - set_dict_combo_index() - - def fill_field_combo_options(self, field_combo, dict_combo_text, dict_combo_itemdata, dict_fld_name, dict_fld_ord): - '''setup field combobox''' - field_combo.clear() - field_combo.setEditable(False) - #if dict_combo_text in _sl('NOT_DICT_FIELD'): - # field_combo.setEnabled(False) - #el - if dict_combo_text in _sl('MDX_SERVER'): - text = dict_fld_name if dict_fld_name else 'http://' - field_combo.setEditable(True) - field_combo.setEditText(text) - field_combo.setFocus(Qt.MouseFocusReason) # MouseFocusReason - else: - unique = dict_combo_itemdata - service = service_pool.get(unique) - # problem - field_combo.setCurrentIndex(0) - if service and service.support and service.fields: - for i, each in enumerate(service.fields): - field_combo.addItem(each, userData=i) - if each == dict_fld_name or i == dict_fld_ord: - field_combo.setCurrentIndex(i) - service_pool.put(service) + else: + self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0) + else: + self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0) def add_dict_layout(self, i, **kwargs): """ @@ -418,11 +419,74 @@ class OptionsDialog(Dialog): 'skip_check_btn': skip_check_btn }) - def save(self): - '''save config to file''' - if not self.current_model: - return - data = dict() + def fill_dict_combo_options(self, dict_combo, current_unique): + '''setup dict combo box''' + dict_combo.clear() + #dict_combo.addItem(_('NOT_DICT_FIELD')) + + # local dict service + #dict_combo.insertSeparator(dict_combo.count()) + has_local_service = False + for cls in service_manager.local_services: + # combo_data.insert("data", each.label) + service = service_pool.get(cls.__unique__) + if service and service.support: + dict_combo.addItem( + service.title, userData=service.unique) + service_pool.put(service) + has_local_service = True + + # hr + if has_local_service: + dict_combo.insertSeparator(dict_combo.count()) + + # web dict service + for cls in service_manager.web_services: + service = service_pool.get(cls.__unique__) + if service and service.support: + dict_combo.addItem( + service.title, userData=service.unique) + service_pool.put(service) + + def set_dict_combo_index(): + #dict_combo.setCurrentIndex(-1) + dict_combo.setCurrentIndex(0) + if current_unique: + for i in range(dict_combo.count()): + if dict_combo.itemData(i) == current_unique: + dict_combo.setCurrentIndex(i) + break + + set_dict_combo_index() + + def fill_field_combo_options(self, field_combo, dict_combo_text, dict_combo_itemdata, dict_fld_name, dict_fld_ord): + '''setup field combobox''' + field_combo.clear() + field_combo.setEditable(False) + #if dict_combo_text in _sl('NOT_DICT_FIELD'): + # field_combo.setEnabled(False) + #el + if dict_combo_text in _sl('MDX_SERVER'): + text = dict_fld_name if dict_fld_name else 'http://' + field_combo.setEditable(True) + field_combo.setEditText(text) + field_combo.setFocus(Qt.MouseFocusReason) # MouseFocusReason + else: + unique = dict_combo_itemdata + service = service_pool.get(unique) + # problem + field_combo.setCurrentIndex(0) + if service and service.support and service.fields: + for i, each in enumerate(service.fields): + field_combo.addItem(each, userData=i) + if each == dict_fld_name or i == dict_fld_ord: + field_combo.setCurrentIndex(i) + service_pool.put(service) + + @property + def data(self): + if not self.___was_built___: + return self._conf maps = [] for row in self.___options___: maps.append({ @@ -436,7 +500,42 @@ class OptionsDialog(Dialog): 'ignore': row['ignore_check_btn'].isChecked(), 'skip_valued': row['skip_check_btn'].isChecked() }) - current_model_id = str(self.current_model['id']) - data[current_model_id] = maps - data['last_model'] = self.current_model['id'] - config.update(data) + return maps + + +class TabBarPlus(QTabBar): + '''Tab bar that has a plus button floating to the right of the tabs.''' + + plusClicked = pyqtSignal() + + def __init__(self): + super(TabBarPlus, self).__init__() + # Plus Button + self.plusButton = QPushButton(u'+') + self.plusButton.setParent(self) + self.plusButton.setFixedSize(26, 26) + self.plusButton.clicked.connect(self.plusClicked.emit) + self.movePlusButton() + + def sizeHint(self): + sh = super(TabBarPlus, self).sizeHint() + width = sh.width() + height = sh.height() + return QSize(width+25, height) + + def resizeEvent(self, event): + super(TabBarPlus, self).resizeEvent(event) + self.movePlusButton() + + def tabLayoutChange(self): + super(TabBarPlus, self).tabLayoutChange() + self.movePlusButton() + + def movePlusButton(self): + size = sum([self.tabRect(i).width() for i in range(self.count())]) + h = self.geometry().top() + w = self.width() + if size > w: + self.plusButton.move(w-54, h) + else: + self.plusButton.move(size, h) diff --git a/addons/fastwq/lang.py b/addons/fastwq/lang.py index 5f96f33..171f6e2 100644 --- a/addons/fastwq/lang.py +++ b/addons/fastwq/lang.py @@ -67,6 +67,7 @@ _arr = [ ['THREAD_NUMBER', u'线程数', u'Number of Threads'], ['INITLIZING_DICT', u'初始化词典...', u'Initlizing Dictionary...'], ['PLS_SET_DICTIONARY_FIELDS', u'请设置字典和字段', u'Please set the dictionary and fields.'], + ['CONFIG_INDEX', u'配置 %s', u'Config %s'], ['BRE_PRON', u'英式发音', u'British Pronunciation'], ['AME_PRON', u'美式发音', u'American Pronunciation'], diff --git a/addons/fastwq/query/__init__.py b/addons/fastwq/query/__init__.py index 78e904d..fc76fef 100644 --- a/addons/fastwq/query/__init__.py +++ b/addons/fastwq/query/__init__.py @@ -26,7 +26,7 @@ from aqt import mw from aqt.utils import showInfo, showText, tooltip from .worker import QueryWorkerManager -from .common import promot_choose_css +from .common import promot_choose_css, inspect_note from ..constants import Endpoint, Template from ..context import config @@ -66,7 +66,7 @@ def query_from_editor_all_fields(editor): if not editor or not editor.note: return - maps = config.get_maps(editor.note.model()['id']) + word_ord, word, maps = inspect_note(editor.note) nomaps = True for each in maps: dict_unique = each.get('dict_unique', '').strip() diff --git a/addons/fastwq/query/common.py b/addons/fastwq/query/common.py index d2fc2bc..db48912 100644 --- a/addons/fastwq/query/common.py +++ b/addons/fastwq/query/common.py @@ -34,7 +34,7 @@ from ..utils import wrap_css __all__ = [ 'InvalidWordException', 'update_note_fields', 'update_note_field', 'promot_choose_css', 'add_to_tmpl', - 'query_all_flds' + 'query_all_flds', 'inspect_note' ] @@ -50,7 +50,9 @@ def inspect_note(note): return maps: dicts map of current note """ - maps = config.get_maps(note.model()['id']) + conf = config.get_maps(note.model()['id']) + maps_list = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf + maps = maps_list['list'][maps_list['def']] for i, m in enumerate(maps): if m.get('word_checked', False): word_ord = i