Extendiendo Marave
Entonces la solución, en la antigua tradición de Emacs y Vim es... hacerlo extensible.
Soy un gran fan de los programas extensibles por el usuario.
Así que... acá está la anatomía de un plugin de Marave tal como funciona ahora mismo en SVN trunk, lo que por supuesto puede cambiar en cualquier momento.
Creando un plugin
Sólo hay que crear un archivo .py en la carpeta plugins. Éste es el plugin más básico, que no hace nada:
# -*- coding: utf-8 -*- from plugins import Plugin class Smarty(Plugin): name='smarty' shortcut='Ctrl+.' description='Smart quote and dash replacement' mode="qBde"
Valores por default de algo configurable (en este caso "mode") se ponen en la clase.
Los campos obligatorios:
shortcut: un atajo de teclado que dispara este plugin
name: un nombre corto
description: una descripción de una línea
¿Qué hace esto? Agrega el plugin a la lista en el diálogo de preferencias, y se puede abrir el diálogo de configuración del plugin, donde se puede cambiar el shortcut:
Si se habilita este plugin, cuando el usuario use ese shortcut, se llama al método "run" del plugin.
Haciéndolo Configurable
Éste plugin soporta distintos modos de operación. Para hacer que esto sea accesible al usuario, hay que implementar unos pocos métodos mas.
El método addConfigWidgets toma como argumento un diálogo, y agrega lo que uno quiera ahí:
@classmethod def addConfigWidgets(self, dialog): print 'Adding widgets to smarty config' l=dialog.ui.layout self.q=QtGui.QCheckBox(dialog.tr('Replace normal quotes')) if 'q' in self.mode: self.q.setChecked(True) self.b=QtGui.QCheckBox(dialog.tr('Replace backtick-style quotes (` and ``)')) if 'B' in self.mode: self.b.setChecked(True) self.d=QtGui.QCheckBox(dialog.tr('Replace -- by en-dash, --- by em-dash')) if 'd' in self.mode: self.d.setChecked(True) self.e=QtGui.QCheckBox(dialog.tr('Replace ellipses')) if 'e' in self.mode: self.e.setChecked(True) l.addWidget(self.q) l.addWidget(self.b) l.addWidget(self.d) l.addWidget(self.e)
Y entonces el diálogo de configuración se ve así:
También queremos que esas opciones se puedan guardar en algún lado, entonces reimplementamos saveConfig:
@classmethod def saveConfig(self, dialog): self.shortcut=unicode(dialog.ui.shortcut.text()) self.settings.setValue('plugin-'+self.name+'-shortcut', self.shortcut) newmode="" if self.q.isChecked(): newmode+='q' if self.b.isChecked(): newmode+='B' if self.d.isChecked(): newmode+='d' if self.e.isChecked(): newmode+='e' self.mode=newmode self.settings.setValue('plugin-smarty-mode',self.mode) self.settings.sync()
Y queremos que esas opciones se lean antes de crear el plugin, entonces:
@classmethod def loadConfig(self): print 'SMARTY loadconfig', self.settings if self.settings: sc=self.settings.value('plugin-'+self.name+'-shortcut') if sc.isValid(): self.shortcut=unicode(sc.toString()) mode=self.settings.value('plugin-smarty-mode') if mode.isValid(): self.mode=unicode(mode.toString())
Que haga algo!
Y sí, hay que hacer que sirva para algo. El plugin tiene acceso a un "client" que es la ventana principal de Marave. Todo está ahí, en alguna parte ;-)
def run(self): print 'running smarty plugin' text=unicode(self.client.editor.toPlainText()).splitlines() prog=QtGui.QProgressDialog(self.client.tr("Applying smarty"), self.client.tr("Cancel"), 0,len(text), self.client) prog.show() output=[] for i,l in enumerate(text): output.append(unescape(smartyPants(l,self.mode))) prog.setValue(i) QtGui.QApplication.instance().processEvents() prog.hide() self.client.editor.setPlainText('\n'.join(output))
Y ya está, si se habilita el plugin smarty, se pueden "arreglar" las comillas, guiones y elipsis con una combinación de teclas :-)
Código fuente completo aquí: http://code.google.com/p/marave/source/browse/trunk/marave/plugins/smarty.py
Falta hacer: otras maneras de integrar plugins en la interface, botones, paneles, etc.