Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre qt (publicaciones antiguas, página 8)

Extendiendo Marave

En­ton­ces la so­lu­ció­n, en la an­ti­gua tra­di­ción de Ema­cs y Vim es... ha­cer­lo ex­ten­si­ble.

Soy un gran fan de los pro­gra­mas ex­ten­si­bles por el usua­rio.

Así que... acá es­tá la ana­to­mía de un plu­gin de Ma­ra­ve tal co­mo fun­cio­na aho­ra mis­mo en SVN trunk, lo que por su­pues­to pue­de cam­biar en cual­quier mo­men­to.

Creando un plugin

Só­lo hay que crear un ar­chi­vo .py en la car­pe­ta plu­gin­s. És­te es el plu­gin más bá­si­co, que no ha­ce na­da:

# -*- coding: utf-8 -*-

from plugins import Plugin
class Smarty(Plugin):
    name='smarty'
    shortcut='Ctrl+.'
    description='Smart quote and dash replacement'
    mode="qBde"

Va­lo­res por de­fault de al­go con­fi­gu­ra­ble (en es­te ca­so "mo­de") se po­nen en la cla­se.

Los cam­pos obli­ga­to­rio­s:

  • sho­r­­tcu­­t: un ata­­jo de te­­cla­­do que dis­­pa­­ra es­­te plu­­gin

  • na­­me: un no­m­­bre co­r­­to

  • des­­cri­p­­tio­­n: una des­­cri­p­­ción de una lí­­nea

¿Qué ha­ce es­to? Agre­ga el plu­gin a la lis­ta en el diá­lo­go de pre­fe­ren­cia­s, y se pue­de abrir el diá­lo­go de con­fi­gu­ra­ción del plu­gi­n, don­de se pue­de cam­biar el shor­tcu­t:

maraveplugin1

Si se ha­bi­li­ta es­te plu­gi­n, cuan­do el usua­rio use ese shor­tcu­t, se lla­ma al mé­to­do "run" del plu­gi­n.

Haciéndolo Configurable

És­te plu­gin so­por­ta dis­tin­tos mo­dos de ope­ra­ció­n. Pa­ra ha­cer que es­to sea ac­ce­si­ble al usua­rio, hay que im­ple­men­tar unos po­cos mé­to­dos ma­s.

El mé­to­do addCon­fi­gWi­dge­ts to­ma co­mo ar­gu­men­to un diá­lo­go, y agre­ga lo que uno quie­ra 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 en­ton­ces el diá­lo­go de con­fi­gu­ra­ción se ve así:

maraveplugin2

Tam­bién que­re­mos que esas op­cio­nes se pue­dan guar­dar en al­gún la­do, en­ton­ces reim­ple­men­ta­mos save­Con­fi­g:

@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 que­re­mos que esas op­cio­nes se lean an­tes de crear el plu­gi­n, en­ton­ce­s:

@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 ha­cer que sir­va pa­ra al­go. El plu­gin tie­ne ac­ce­so a un "clien­t" que es la ven­ta­na prin­ci­pal de Ma­ra­ve. To­do es­tá ahí, en al­gu­na par­te ;-)

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 es­tá, si se ha­bi­li­ta el plu­gin smar­ty, se pue­den "a­rre­gla­r" las co­mi­lla­s, guio­nes y elip­sis con una com­bi­na­ción de te­clas :-)

Có­di­go fuen­te com­ple­to aquí: http://­co­de.­google.­co­m/­p/­ma­ra­ve/­sour­ce/­bro­wse/­trunk/­ma­ra­ve/­plu­gin­s/s­mar­ty.­py

Fal­ta ha­ce­r: otras ma­ne­ras de in­te­grar plu­gins en la in­ter­fa­ce, bo­to­nes, pa­ne­le­s, etc.

Afeitando yaks: 16/2/2010

Ha­ce un tiem­po es­cri­bí acer­ca de co­mo im­ple­men­té un re­sal­ta­dor de sin­ta­xis ge­ne­ra­li­za­do pa­ra Py­Qt usan­do pyg­men­ts.

Re­ci­bí un pe­di­do de un fea­tu­re si­mi­lar en Ma­ra­ve, así que des­en­te­rré ese có­di­go y... no sir­ve pa­ra na­da. Es de­ma­sia­do len­to pa­ra un uso ra­zo­na­ble.

En­ton­ces a es­te yak ya le cre­ció de nue­vo to­do el pe­lo, y ¡jus­to ten­go es­te par de ti­je­ras nue­va­s!

La me­ta es lo­grar re­sal­tar sin­ta­xis en un QPlain­TextE­dit de for­ma que:

  • No re­­quie­­ra pro­­­gra­­mar pa­­ra aña­­dir un nue­­vo re­s­al­­ta­­do­­­r.

  • No re­­quie­­ra pro­­­gra­­mar pa­­ra aña­­dir un es­­que­­ma de co­­­lo­­­res.

  • No re­­quie­­ra que me pa­­se el 2010 es­­cri­­bien­­­do re­s­al­­ta­­do­­­res pa­­ra len­­gua­­jes exis­­ten­­tes.

  • Sea su­­fi­­cien­­te­­men­­te rá­­pi­­do

Una bús­que­da rá­pi­da en google mues­tra que pa­ra C++ se pue­de usar Sour­ce hi­gh­li­ght qt que es­tá ba­sa­do en GNU sour­ce hi­gh­li­ght.

Ob­via­men­te, no hay bin­ding py­thon que yo vea, así que... ¡me es­cri­bí uno!

Acá es­tá: http://­ma­ra­ve.­google­co­de.­co­m/s­vn/­trunk/­ma­ra­ve/hi­gh­li­gh­t/

Y és­ta es una cap­tu­ra del pro­gra­ma de de­mo co­rrien­do, mos­tran­do­se a sí mis­mo en­te­ro:

Se pue­de crear un es­que­ma de co­lo­res usan­do CSS, un len­gua­je se de­fi­ne con un ar­chi­vo de tex­to, hay una pi­la ya he­cho­s, y pa­re­ce lo bas­tan­te rá­pi­do.

En­ton­ces de­cla­ro a es­te yak afei­ta­do, y otro fea­tu­re (no ter­mi­na­do­!) pa­ra Ma­ra­ve

Como implementar "reemplazar todos" en un QPlainTextEdit

Así es co­mo se im­ple­men­ta 'reem­pla­zar to­do­s' en un QPlain­TextE­dit (o un QTextE­di­t, pro­ba­ble­men­te) usan­do Py­Qt (es si­mi­lar pa­ra C++).

def doReplaceAll(self):
    # Replace all occurences without interaction

    # Here I am just getting the replacement data
    # from my UI so it will be different for you
    old=self.searchReplaceWidget.ui.text.text()
    new=self.searchReplaceWidget.ui.replaceWith.text()

    # Beginning of undo block
    cursor=self.editor.textCursor()
    cursor.beginEditBlock()

    # Use flags for case match
    flags=QtGui.QTextDocument.FindFlags()
    if self.searchReplaceWidget.ui.matchCase.isChecked():
        flags=flags|QtGui.QTextDocument.FindCaseSensitively

    # Replace all we can
    while True:
        # self.editor is the QPlainTextEdit
        r=self.editor.find(old,flags)
        if r:
            qc=self.editor.textCursor()
            if qc.hasSelection():
                qc.insertText(new)
        else:
            break

    # Mark end of undo block
    cursor.endEditBlock()

Hay otras ma­ne­ras más fá­ci­le­s, pe­ro es­ta ha­ce que to­do apa­rez­ca en una so­la ope­ra­ción en la pi­la un­do­/­re­do y esas co­sas.

Marave 0.5 publicado

¡Has­ta in­clu­ye un pe­que­ño re­pro­duc­tor mu­si­cal pa­ra que no ten­gas que pen­sar en otra apli­ca­ció­n!

Es­ta ver­sión tie­ne va­rios bugs arre­gla­do­s, y se ve un po­co más bo­ni­ta.

El nue­vo fea­tu­re es... in­ter­na­cio­na­li­za­ció­n. Por aho­ra só­lo una tra­duc­ción al cas­te­lla­no, pe­ro si al­guien quie­re ayu­dar tra­du­cién­do­lo a otro idio­ma, por fa­vo­r, ade­lan­te!

Hay só­lo unas 60 fra­ses, por lo que no de­be­ría ser más de una ho­ra de tra­ba­jo.

Aquí hay una cap­tu­ra de pan­ta­lla de es­ta ver­sió­n:

.. raw:: ht­ml marave7

Ma­ra­ve es so­ftwa­re li­bre ba­jo la GPL­v2, y de­be­ría fun­cio­nar en cual­quier pla­ta­for­ma don­de fun­cio­ne Py­Q­t, lo que quie­re de­cir Ma­c, Win­do­ws, y sis­te­mas ope­ra­ti­vos ti­po unix, por lo me­no­s.

Empaquetar es DIFÍCIL

O ni si­quie­ra eso, quie­ro que la gen­te ten­ga la opor­tu­ni­dad de usar­lo.

Eso sig­ni­fi­ca que quie­ro que fun­cio­ne en Win­do­ws (y tal vez en OSX al­gún día, si al­guien me da una ma­no­). Lo que sig­ni­fi­ca que ten­go que ha­cer una ver­sión pa­ra Win­do­ws.

ha­ga­mos una com­pa­ra­ción rá­pi­da des­de el pun­to de vis­ta del usua­rio y del de­sa­rro­lla­do­r.

El usuario, en Linux

Es­to es en Ar­ch Li­nu­x, que es lo que yo uso, en otras va­rian­tes es más o me­nos lo mis­mo una vez que Ma­ra­ve sea mas co­no­ci­do.

yaourt -S marave-svn --noconfirm

Eso ob­tie­ne el có­di­go de SVN (por aho­ra es lo me­jo­r, más ade­lan­te em­pa­que­ta­ré re­lea­ses), to­das las de­pen­den­cia­s, y lo ins­ta­la. Tar­da unos 15 se­gun­dos en mi no­te­book.

Des­pués de eso, te­nés un Ma­ra­ve fun­cio­nan­do.

En ca­so de que no es­té en tu dis­tro, te­nés que ins­ta­lar Py­Qt (que se­gu­ro si es­tá) y co­rre­r:

easy_install marave

El usuario, en windows

Vas a http://­ma­ra­ve.­google­co­de.­com, cli­ck en "Ma­ra­ve-0.5.wi­n32.exe" (No lo bus­ques, to­da­vía no es­tá) ba­jás un pro­gra­ma de 10M­B. Eso es un pro­gra­ma de 10MB por­que win­do­ws no cree en pa­que­tes y en de­pen­den­cia­s. En Li­nux un pa­que­te de Ma­ra­ve se­ría 1MB (ca­si to­do imá­ge­nes), y se­ría da­to­s, no eje­cu­ta­ble.

Por su­pues­to hoy en día un bro­w­ser no te eje­cu­ta un pro­gra­ma que ba­jas­te, asi que... ha­ga­mos una ga­le­ría!

110111105613-My-Desktop

Sí, guar­da­r.

11011111220-My-Desktop

Do­ble cli­ck pa­ra abri­r.

11011111417-My-Desktop

Sí, es­toy de acuer­do.

11011111514-My-Desktop

Hm­m­m, bue­no.

1101111167-My-Desktop

Bár­ba­ro­...

11011111750-My-Desktop

Ge­nia­l!

Aho­ra es­te Ma­ra­ve pue­de fun­cio­nar o no pe­ro eso es pa­ra más ade­lan­te...

El desarrollador, en Linux

Pri­me­ro, es­te es el ma­yor pro­ble­ma un "em­pa­que­ta­do­r" pue­de te­ner en Li­nu­x:

Co­mo Ma­ra­ve es una apli­ca­ción nue­va, y la de­sa­rro­llo en Ar­ch Li­nux que es me­dio cu­tting edge, usa fea­tu­res que só­lo es­tán en ver­sio­nes nue­vas de Py­Q­t. De he­cho no fun­cio­na con Py­Qt < 4.6, que no es­tá en al­gu­nas dis­tros "len­ta­s" o en un Ubun­tu que no es el úl­ti­mo.

So­lu­ció­n? Bue­no, po­dría ig­no­rar­lo, pe­ro que tan­to, va­mos a arre­glar­lo!

Graias a PyIns­ta­ller ni si­quie­ra es tan di­fí­ci­l, es­te es el ar­chi­vo spe­c:

a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), 'marave/main.py'],
            pathex=['/home/ralsina/trunk/trunk'])

pyz = PYZ(a.pure)
exe = EXE(pyz,
        a.scripts,
        exclude_binaries=1,
        name=os.path.join('build/pyi.linux2/main', 'marave.exe'),
        debug=False,
        strip=False,
        upx=True,
        console=0 )

coll = COLLECT( exe,
            a.binaries,
            [('radios.txt','marave/radios.txt','DATA')],
            Tree('marave/icons','icons'),
            Tree('marave/backgrounds','backgrounds'),
            Tree('marave/clicks','clicks'),
            Tree('marave/stylesheets','stylesheets'),
            Tree('marave/themes','themes'),
            a.zipfiles,
            a.datas,
            strip=False,
            upx=True,
            name=os.path.join('dist', 'marave'))

Usan­do es­to, PyIns­ta­ller pro­du­ce una lin­da car­pe­ta lle­na de to­do lo que Ma­ra­ve ne­ce­si­ta pa­ra fun­cio­nar en cual­quier Li­nu­x.

Por otro la­do, si se pue­de con­tar con que ha­ya un Py­Qt re­cien­te dis­po­ni­ble, tam­bién es fá­ci­l. És­te es el ar­chi­vo de con­fi­gu­ra­ción pa­ra un pa­que­te si­mi­lar en Ar­ch Li­nux (to­da­vía no hi­ce uno pa­ra Ma­ra­ve). Pa­ra otros Li­nux es más o me­nos lo mis­mo, y nor­mal­men­te al­guien te lo ha­ce:

# Contributor: Roberto Alsina <ralsina@kde.org>
pkgname=python-rst2pdf
pkgver=0.12.1
pkgrel=4
pkgdesc="Create PDFs from simple text markup, no LaTeX required."
arch=('i686' 'x86_64')
url="http://rst2pdf.googlecode.com"
license=('custom')
depends=('python' 'setuptools' 'docutils' 'pygments' 'python-reportlab' 'python-simplejson' 'pil')
source=(http://rst2pdf.googlecode.com/files/rst2pdf-$pkgver.tar.gz LICENSE.txt)
optdepends=('uniconvertor: vector images support'
            'python-svglib: SVG support'
            'python-wordaxe: hyphenation'
            'pythonmagick: PDF images support')
build() {
cd $startdir/src/rst2pdf-$pkgver
python setup.py install --root=$startdir/pkg || return 1
install -D ../LICENSE.txt $startdir/pkg/usr/share/licenses/python-rst2pdf/COPYING
install -D doc/rst2pdf.1 $startdir/pkg/usr/share/man/man1/rst2pdf.1
}
md5sums=('ea6beda9a46f34ba42c4c94d48cc607a'
        '416f8046c66b9476cdbacda69a673afe')

Y eso es to­do lo que hay que sa­ber del pro­ce­so de em­pa­que­tar tu apli­ca­ción pa­ra Li­nu­x, es fá­cil de ha­ce­r, y la ma­yor par­te del tiem­po, fá­cil de ha­cer bien.

Aho­ra, la sec­ción fi­na­l...

Windows para el desarrollador

¿Pri­me­ro, te acor­dás de eso de de­pen­der de la ver­sión de sis­te­ma de Qt? Ol­ví­da­lo, no hay ver­sión de sis­te­ma. Tam­po­co hay Py­tho­n, así que no im­por­ta. Y na­die los va a ins­ta­lar pa­ra tu apli­ca­ció­n, así que te­ne­mos que me­ter to­do no­so­tro­s, o na­da.

Pe­ro lo bue­no es que PyIns­ta­ller fun­cio­na pa­ra Win­do­ws! En­ton­ce­s, usan­do el mis­mo spec fun­cio­na, no?

Bue­no, hay dos pro­ble­ma­s...

Problema 1: El instalador

Los usua­rios de Win­do­ws no van a abrir un zip y co­nec­tar el bi­na­rio con el me­nú de ini­cio ni na­da pa­re­ci­do, así que hay que ha­cer un ins­ta­la­do­r.

Es­to es lo que hi­ce pa­ra NSIS, un crea­dor de ins­ta­la­do­res gra­tui­to:

;--------------------------------
;Include Modern UI

!include "MUI2.nsh"

;--------------------------------
;General

;Name and file
Name "Marave"
OutFile "Marave-0.5.win32.exe"

;Default installation folder
InstallDir "$LOCALAPPDATA\Marave"

;Get installation folder from registry if available
InstallDirRegKey HKCU "Software\Marave" ""

;Request application privileges for Windows Vista
RequestExecutionLevel user

;--------------------------------
;Interface Settings

!define MUI_ABORTWARNING

;--------------------------------
;Pages

!insertmacro MUI_PAGE_LICENSE "LICENSE"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES

!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES

;--------------------------------
;Languages

!insertmacro MUI_LANGUAGE "English"

;--------------------------------
;Installer Sections

Section "Install"

SetOutPath "$INSTDIR"
File /r "dist\marave"


;Store installation folder
WriteRegStr HKCU "Software\Marave" "" $INSTDIR

;Create uninstaller
WriteUninstaller "$INSTDIR\Uninstall.exe"

;Create shortcuts
CreateDirectory $SMPROGRAMS\Marave
CreateShortCut "$SMPROGRAMS\Marave\Marave.lnk" "$INSTDIR\marave\marave.exe" ; use defaults for parameters, icon, etc.
CreateShortCut "$SMPROGRAMS\Marave\Uninstall Marave.lnk" "$INSTDIR\Uninstall.exe" ; use defaults for parameters, icon, etc.

SectionEnd


;--------------------------------
;Uninstaller Section

Section "Uninstall"

Delete "$INSTDIR\Uninstall.exe"
RMDir /r "$INSTDIR"

DeleteRegKey /ifempty HKCU "Software\Marave"

SectionEnd

Es com­pa­ra­ble al es­fuer­zo de ha­cer un arhi­vo de em­pa­que­ta­do, ex­cep­to que ca­da vez que lo que­rés pro­ba­r... lo ins­ta­lá­s. No hay ma­ne­ra (que yo vea) de sa­ber qué hay aden­tro del ins­ta­la­dor ex­cep­to co­rrer­lo.

Cuan­do las co­sas fa­llan, no hay men­sa­jes de erro­r, por lo me­nos no del ti­po que es útil pa­ra un de­sa­rro­lla­do­r, el que ne­ce­si­ta sa­ber que salió mal.

Des­pués de que ter­mi­na, tal vez no fun­cio­ne por­que...

Problema 2: bibliotecas de sistema. Ja!

Los bi­na­rios de Py­thon 2.6 es­tán com­pi­la­dos con Vi­sual Stu­dio. Eso quie­re de­cir que ne­ce­si­tan el Vi­sual Stu­dio Runti­me, es­pe­cí­fi­ca­men­te MS­V­CR90.D­LL. És­ta con­tie­ne co­sas que en Li­nux se­rían con­si­de­ra­do par­te de la li­bc (li­nu­xe­ro: ima­gi­na­te apli­ca­cio­nes que de­pen­den de una li­bc es­pe­cí­fi­ca... ¡no es fá­ci­l!)

En Li­nux eso es par­te del sis­te­ma. Más aú­n, si lo ne­ce­si­ta­s, lo re­dis­tri­buís. En Win­do­ws... es di­fe­ren­te.

  1. Es pa­r­­te del "Vi­­sual C++ re­­dis­­tri­­bu­­ta­­ble­s"

  2. In­s­­ta­­la­r­­lo no es ga­­ran­­tía de que an­­de (sí, lo pro­­­bé)

  3. La li­­cen­­cia de esos 're­­dis­­tri­­bu­­ta­­ble­s' di­­ce que no lo po­­­dés ha­­cer dis­­po­­­ni­­ble pa­­ra des­­ca­r­­ga.

    Me han di­­cho que in­­cluír­­lo en tu in­s­­ta­­la­­dor es le­­ga­­l, pe­­ro a mí me pa­­re­­ce que eso es ha­­ce­r­­lo dis­­po­­­ni­­ble pa­­ra des­­ca­r­­ga!

¿Qué se ha­ce cuan­do ne­ce­si­tás una bi­blio­te­ca, no la po­dés dis­tri­buir y el usua­rio no la va a ins­ta­la­r?

Bue­no, por al­go no hay bi­na­rios de Ma­ra­ve pa­ra Win­do­ws to­da­vía ;-) Por su­pues­to si al­guien lo pue­de re­sol­ve­r, me en­can­ta­ría!


Contents © 2000-2023 Roberto Alsina