From d17098b8ce4ee61469928dbd8f1a15cd029ef753 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 12 Oct 2008 00:21:54 +0900 Subject: [PATCH] number of ui changes (see full log) - use default theme on linux - add active tag browser - allow custom toolbar icon sizes - add plugins menu (enable/disable/etc) - force plastique for progress bars --- ankiqt/__init__.py | 2 +- ankiqt/config.py | 1 + ankiqt/ui/__init__.py | 1 + ankiqt/ui/activetags.py | 59 ++++++++++++++++++ ankiqt/ui/main.py | 120 +++++++++++++++++++++++++++++-------- ankiqt/ui/preferences.py | 7 +++ ankiqt/ui/status.py | 9 +-- designer/activetags.ui | 77 ++++++++++++++++++++++++ designer/deckproperties.ui | 18 ++---- designer/main.ui | 67 +++++++++++++++++++-- designer/preferences.ui | 76 +++++++++++++++++------ 11 files changed, 370 insertions(+), 67 deletions(-) create mode 100644 ankiqt/ui/activetags.py create mode 100644 designer/activetags.ui diff --git a/ankiqt/__init__.py b/ankiqt/__init__.py index 2b6feea8b..9114f5d41 100644 --- a/ankiqt/__init__.py +++ b/ankiqt/__init__.py @@ -40,7 +40,7 @@ def run(): pass app = QApplication(sys.argv) - if not sys.platform.startswith("darwin"): + if sys.platform.startswith("win32"): app.setStyle("plastique") # setup paths for forms, icons diff --git a/ankiqt/config.py b/ankiqt/config.py index 15991834f..d7c8b172e 100644 --- a/ankiqt/config.py +++ b/ankiqt/config.py @@ -28,6 +28,7 @@ class Config(dict): def defaults(self): fields = { + 'iconSize': 32, 'syncOnLoad': False, 'syncOnClose': False, 'checkForUpdates': True, diff --git a/ankiqt/ui/__init__.py b/ankiqt/ui/__init__.py index 3cb6fc1e0..06ccda87d 100644 --- a/ankiqt/ui/__init__.py +++ b/ankiqt/ui/__init__.py @@ -25,6 +25,7 @@ def importAll(): import facteditor import tagedit import tray + import activetags class DialogManager(object): diff --git a/ankiqt/ui/activetags.py b/ankiqt/ui/activetags.py new file mode 100644 index 000000000..ec6a10caf --- /dev/null +++ b/ankiqt/ui/activetags.py @@ -0,0 +1,59 @@ +# Copyright: Damien Elmes +# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html + +from PyQt4.QtGui import * +from PyQt4.QtCore import * +import ankiqt +from anki.utils import parseTags, joinTags + +class ActiveTagsChooser(QDialog): + + def __init__(self, parent): + QDialog.__init__(self, parent) + self.parent = parent + self.dialog = ankiqt.forms.activetags.Ui_Dialog() + self.dialog.setupUi(self) + self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), + self.onHelp) + self.rebuildTagList() + + + def rebuildTagList(self): + self.tags = self.parent.deck.allTags() + self.tags.sort() + self.items = [] + self.suspended = {} + for t in parseTags(self.parent.deck.suspended): + self.suspended[t] = 1 + for t in self.tags: + item = QListWidgetItem(t, self.dialog.list) + self.dialog.list.addItem(item) + self.items.append(item) + idx = self.dialog.list.indexFromItem(item) + if t in self.suspended: + mode = QItemSelectionModel.Deselect + else: + mode = QItemSelectionModel.Select + self.dialog.list.selectionModel().select(idx, mode) + + def accept(self): + n = 0 + suspended = [] + for item in self.items: + idx = self.dialog.list.indexFromItem(item) + if not self.dialog.list.selectionModel().isSelected(idx): + suspended.append(self.tags[n]) + n += 1 + self.parent.deck.suspended = joinTags(suspended + ["Suspended"]) + self.parent.deck.setModified() + self.parent.reset() + QDialog.accept(self) + + def onHelp(self): + QDesktopServices.openUrl(QUrl(ankiqt.appWiki + + "ActiveTags")) + + +def show(parent): + at = ActiveTagsChooser(parent) + at.exec_() diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py index c1ad42eff..939ef87c3 100644 --- a/ankiqt/ui/main.py +++ b/ankiqt/ui/main.py @@ -8,7 +8,7 @@ from PyQt4.QtCore import * # fixme: sample files read only, need to copy import os, sys, re, types, gettext, stat, traceback -import copy, shutil, time +import copy, shutil, time, glob from PyQt4.QtCore import * from PyQt4.QtGui import * @@ -39,9 +39,10 @@ class AnkiQt(QMainWindow): self.setupFonts() self.setupBackupDir() self.setupHooks() - self.loadUserCustomisations() + self.loadPlugins() self.mainWin = ankiqt.forms.main.Ui_MainWindow() self.mainWin.setupUi(self) + self.rebuildPluginsMenu() self.alterShortcuts() self.help = ui.help.HelpArea(self.mainWin.helpFrame, self.config, self) self.trayIcon = ui.tray.AnkiTrayIcon( self ) @@ -72,8 +73,8 @@ class AnkiQt(QMainWindow): try: self.runHook('init') except: - print _("Error running initHook. Broken plugin?") - traceback.print_exc() + ui.utils.showWarning(_("Broken plugin:\n\n%s") % + traceback.format_exc()) # check for updates self.setupAutoUpdate() @@ -798,13 +799,13 @@ class AnkiQt(QMainWindow): ########################################################################## def setupToolbar(self): + mw = self.mainWin if not self.config['showToolbar']: - self.removeToolBar(self.mainWin.toolBar) - self.mainWin.toolBar.hide() - if self.config['simpleToolbar']: - mw = self.mainWin self.removeToolBar(mw.toolBar) - self.mainWin.toolBar.hide() + mw.toolBar.hide() + if self.config['simpleToolbar']: + self.removeToolBar(mw.toolBar) + mw.toolBar.hide() mw.toolBar = QToolBar(self) mw.toolBar.addAction(mw.actionAddcards) mw.toolBar.addAction(mw.actionEditdeck) @@ -813,6 +814,8 @@ class AnkiQt(QMainWindow): mw.toolBar.addAction(mw.actionGraphs) mw.toolBar.addAction(mw.actionDisplayProperties) self.addToolBar(Qt.TopToolBarArea, mw.toolBar) + mw.toolBar.setIconSize(QSize(self.config['iconSize'], + self.config['iconSize'])) # Tools - looking up words in the dictionary ########################################################################## @@ -979,6 +982,9 @@ class AnkiQt(QMainWindow): def onAbout(self): ui.about.show(self) + def onActiveTags(self): + ui.activetags.show(self) + # Importing & exporting ########################################################################## @@ -1223,6 +1229,11 @@ class AnkiQt(QMainWindow): self.connect(m.actionMergeModels, s, self.onMergeModels) self.connect(m.actionCheckMediaDatabase, s, self.onCheckMediaDB) self.connect(m.actionCram, s, self.onCram) + self.connect(m.actionGetPlugins, s, self.onGetPlugins) + self.connect(m.actionOpenPluginFolder, s, self.onOpenPluginFolder) + self.connect(m.actionEnableAllPlugins, s, self.onEnableAllPlugins) + self.connect(m.actionDisableAllPlugins, s, self.onDisableAllPlugins) + self.connect(m.actionActiveTags, s, self.onActiveTags) def enableDeckMenuItems(self, enabled=True): "setEnabled deck-related items." @@ -1316,32 +1327,89 @@ class AnkiQt(QMainWindow): "type": ret } ) - # User customisations + # Plugins ########################################################################## - def loadUserCustomisations(self): - # look for config file + def pluginsFolder(self): dir = self.config.configPath file = os.path.join(dir, "custom.py") - plugdir = os.path.join(dir, "plugins") - sys.path.insert(0, dir) - if os.path.exists(file): - try: - import custom - except: - print "Error in custom.py" - print traceback.print_exc() + return os.path.join(dir, "plugins") + + def loadPlugins(self): + plugdir = self.pluginsFolder() sys.path.insert(0, plugdir) - import glob - plugins = [f.replace(".py", "") for f in os.listdir(plugdir) \ - if f.endswith(".py")] + plugins = self.enabledPlugins() plugins.sort() for plugin in plugins: try: - __import__(plugin) + nopy = plugin.replace(".py", "") + __import__(nopy) except: - print "Error in %s.py" % plugin - print traceback.print_exc() + print "Error in %s" % plugin + traceback.print_exc() + + def rebuildPluginsMenu(self): + if getattr(self, "pluginActions", None) is None: + self.pluginActions = [] + for action in self.pluginActions: + self.mainWin.menuStartup.removeAction(action) + all = self.allPlugins() + all.sort() + for fname in all: + enabled = fname.endswith(".py") + p = re.sub("\.py(\.off)?", "", fname) + a = QAction(p, self) + a.setCheckable(True) + a.setChecked(enabled) + self.connect(a, SIGNAL("triggered()"), + lambda fname=fname: self.togglePlugin(fname)) + self.mainWin.menuStartup.addAction(a) + self.pluginActions.append(a) + + def enabledPlugins(self): + return [p for p in os.listdir(self.pluginsFolder()) + if p.endswith(".py")] + + def disabledPlugins(self): + return [p for p in os.listdir(self.pluginsFolder()) + if p.endswith(".py.off")] + + def allPlugins(self): + return [p for p in os.listdir(self.pluginsFolder()) + if p.endswith(".py.off") or p.endswith(".py")] + + def onOpenPluginFolder(self): + QDesktopServices.openUrl(QUrl("file://" + self.pluginsFolder())) + + def onGetPlugins(self): + QDesktopServices.openUrl(QUrl("http://ichi2.net/anki/wiki/Plugins")) + + def enablePlugin(self, p): + pd = self.pluginsFolder() + os.rename(os.path.join(pd, p), + os.path.join(pd, p.replace(".off", ""))) + + def disablePlugin(self, p): + pd = self.pluginsFolder() + os.rename(os.path.join(pd, p), + os.path.join(pd, p.replace(".py", ".py.off"))) + + def onEnableAllPlugins(self): + for p in self.disabledPlugins(): + self.enablePlugin(p) + self.rebuildPluginsMenu() + + def onDisableAllPlugins(self): + for p in self.enabledPlugins(): + self.disablePlugin(p) + self.rebuildPluginsMenu() + + def togglePlugin(self, plugin): + if plugin.endswith(".py"): + self.disablePlugin(plugin) + else: + self.enablePlugin(plugin) + self.rebuildPluginsMenu() # Font localisation ########################################################################## diff --git a/ankiqt/ui/preferences.py b/ankiqt/ui/preferences.py index 9ee9e6513..f669f930c 100644 --- a/ankiqt/ui/preferences.py +++ b/ankiqt/ui/preferences.py @@ -172,6 +172,7 @@ class Preferences(QDialog): self.dialog.showTray.setChecked(self.config['showTrayIcon']) self.dialog.showTimer.setChecked(self.config['showTimer']) self.dialog.simpleToolbar.setChecked(self.config['simpleToolbar']) + self.dialog.toolbarIconSize.setText(str(self.config['iconSize'])) def updateAdvanced(self): self.config['showToolbar'] = self.dialog.showToolbar.isChecked() @@ -186,6 +187,12 @@ class Preferences(QDialog): self.config['showTimer'] = self.dialog.showTimer.isChecked() self.config['suppressEstimates'] = self.dialog.suppressEstimates.isChecked() self.config['simpleToolbar'] = self.dialog.simpleToolbar.isChecked() + i = 32 + try: + i = int(self.dialog.toolbarIconSize.text()) + except: + pass + self.config['iconSize'] = i def codeToIndex(self, code): n = 0 diff --git a/ankiqt/ui/status.py b/ankiqt/ui/status.py index 24a73b038..7d6af6def 100644 --- a/ankiqt/ui/status.py +++ b/ankiqt/ui/status.py @@ -99,12 +99,9 @@ class StatusView(object): if sys.platform.startswith("darwin"): self.timer.setFixedWidth(40) self.addWidget(self.timer) - # mac - if sys.platform.startswith("darwin"): - # we don't want non-coloured, throbbing widgets - self.plastiqueStyle = QStyleFactory.create("plastique") - self.progressBar.setStyle(self.plastiqueStyle) - self.retentionBar.setStyle(self.plastiqueStyle) + self.plastiqueStyle = QStyleFactory.create("plastique") + self.progressBar.setStyle(self.plastiqueStyle) + self.retentionBar.setStyle(self.plastiqueStyle) self.optionsButton = QPushButton() self.optionsButton.setIcon(QIcon(":/icons/configure.png")) self.optionsButton.setFixedSize(20, 20) diff --git a/designer/activetags.ui b/designer/activetags.ui new file mode 100644 index 000000000..5391b82ed --- /dev/null +++ b/designer/activetags.ui @@ -0,0 +1,77 @@ + + Dialog + + + + 0 + 0 + 202 + 265 + + + + Active Tags + + + + + + <h1>Select Active Tags</h1> + + + + + + + QAbstractItemView::MultiSelection + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/deckproperties.ui b/designer/deckproperties.ui index 38e05cfda..cffd42727 100644 --- a/designer/deckproperties.ui +++ b/designer/deckproperties.ui @@ -8,7 +8,7 @@ 0 0 - 366 + 388 408 @@ -29,7 +29,7 @@ 0 0 - 350 + 372 342 @@ -143,7 +143,7 @@ p, li { white-space: pre-wrap; } 0 0 - 350 + 372 342 @@ -340,7 +340,7 @@ p, li { white-space: pre-wrap; } 0 0 - 350 + 372 342 @@ -405,7 +405,7 @@ p, li { white-space: pre-wrap; } 0 0 - 350 + 372 342 @@ -446,7 +446,7 @@ p, li { white-space: pre-wrap; } 0 0 - 350 + 372 342 @@ -551,12 +551,6 @@ p, li { white-space: pre-wrap; } - - - 220 - 0 - - <b>2: Initial Hard Interval</b> diff --git a/designer/main.ui b/designer/main.ui index a96d1100b..1ae5b0753 100644 --- a/designer/main.ui +++ b/designer/main.ui @@ -6,7 +6,7 @@ 0 0 543 - 285 + 301 @@ -26,7 +26,7 @@ 0 - 53 + 69 543 212 @@ -345,6 +345,8 @@ + help + mainTextFrame @@ -383,12 +385,13 @@ + + - @@ -465,9 +468,27 @@ + + + &Plugins + + + + Startup + + + + + + + + + + + @@ -475,7 +496,7 @@ 0 - 265 + 281 543 20 @@ -490,12 +511,18 @@ 0 23 543 - 30 + 46 Qt::Horizontal + + + 32 + 32 + + TopToolBarArea @@ -945,6 +972,36 @@ Release Notes.. + + + &Get Plugins.. + + + + + &Open Plugin Folder.. + + + + + &Enable All Plugins + + + + + &Disable All Plugins + + + + + a + + + + + Active &Tags.. + + diff --git a/designer/preferences.ui b/designer/preferences.ui index 01cf564d2..3094afcbf 100644 --- a/designer/preferences.ui +++ b/designer/preferences.ui @@ -462,6 +462,13 @@ + + + + Simple toolbar + + + @@ -511,13 +518,33 @@ - - + + + + + + - Simple toolbar + Toolbar icon size + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -548,8 +575,6 @@ - buttonBox - tabWidget tabWidget @@ -581,6 +606,7 @@ showToolbar tallButtons showTray + toolbarIconSize buttonBox @@ -592,8 +618,8 @@ accept() - 266 - 476 + 270 + 412 157 @@ -608,8 +634,8 @@ reject() - 334 - 476 + 317 + 412 286 @@ -624,12 +650,12 @@ setEnabled(bool) - 94 - 157 + 105 + 133 - 235 - 160 + 227 + 134 @@ -640,12 +666,28 @@ setEnabled(bool) - 80 - 178 + 91 + 164 - 174 - 176 + 227 + 165 + + + + + simpleToolbar + toggled(bool) + toolbarIconSize + setEnabled(bool) + + + 116 + 115 + + + 308 + 335