Ir al contenido principal

Ralsina.Me — El sitio web de Roberto Alsina

Publicaciones sobre kde

Una semana usando tiling windows.

Ha pa­sa­do un po­co más de una se­ma­na des­de que de­ci­dí usar un ti­ling win­dow ma­na­ger "en se­rio­".

Cla­ro, es­toy ha­cien­do un po­qui­to de tram­pa por­que es­toy usan­do KDE más Kröhnki­te pe­ro mis ven­ta­nas ha­cen mo­sai­cos y me gus­ta.

Por qué es­to y no i3 u otra co­sa? Por­que no quie­ro cam­biar mi es­ti­lo de vi­da, na­da más quie­ro que mis ven­ta­nas no se so­la­pen to­do el tiem­po.

Kröhnki­te me da su­fi­cien­te fun­cio­na­li­dad "ti­lin­g" que ob­ten­go (creo) los be­ne­fi­cios sin el ma­si­vo des­pe­lo­te de aban­do­nar to­das las co­sas de mi es­cri­to­rio a las que es­toy acos­tum­bra­do. To­da­vía uso la te­cla Win­do­ws (o­k, ok, la te­cla "Me­ta") pa­ra lan­zar app­s, si­go te­nien­do un pa­nel de plas­ma con plas­moi­des en la par­te de aba­jo de mi mo­ni­to­r. ¡Pue­do ha­cer que las ven­ta­nas flo­ten si quie­ro! Pue­do usar la ma­yo­ría de los ata­jos de te­cla­do de mis 24 años usan­do KDE (sí, en se­rio) etc.

¿Cuá­les co­sas tu­ve que cam­biar pa­ra adap­tar­me?

  • Tu­­ve que pa­sar a fo­­­cus-­­fo­­­llo­­ws-­­mou­­se. PE­­RO por pri­­me­­ra vez des­­de que em­­pe­­cé a usar FVWM en 1993 me gus­­ta más que cli­­ck-­­to­­-­­fo­­­cus. Re­­su­l­­ta que la im­­ple­­men­­ta­­ción de KDE es­­tá bue­­­na y bá­­si­­ca­­men­­te "ha­­ce lo que uno quie­­re". Co­­­mo di­­ce en la do­­­cu­­men­­ta­­ció­­n, "es co­­­mo cli­­ck to fo­­­cus, pe­­ro no ha­­cés cli­­ck".

  • Sa­­qué las de­­co­­­ra­­cio­­­nes de las ven­­ta­­na­s. Sí, se pue­­den de­­ja­­r, pe­­ro se ve ra­­ro.

  • Pu­­se bo­r­­des más grue­­so­­s. Ca­m­­biar el ta­­ma­­ño de las ven­­ta­­nas con el te­­cla­­do no es­­tá bue­­­no, así que los bo­r­­des más grue­­sos ayu­­dan.

¿Cuá­les co­sas me gus­ta­ro­n?

  • Te­­ner la­­yout ti­­ling en un mo­­­ni­­tor y floa­­ting en el otro es­­tá muy bue­­no cuan­­do se ne­­ce­­si­­ta. Y lo pue­­do ac­­ti­­var o des­a­c­­ti­­var con una te­­cla. En ge­­ne­­ra­­l: la­­you­­ts di­­ná­­mi­­cos y se­­pa­­ra­­dos por pan­­ta­­lla son mu­­y, muy úti­­le­s.

  • Te­­ner un WM "ti­­li­n­­g" pe­­ro que to­­­da­­vía res­­pe­­ta las co­n­­ven­­cio­­­nes de la ma­­yo­­­ría de los WMs es­­tá bue­­no. Los po­­­pups flo­­­tan, ok?

  • El ata­jo Al­t+En­ter pa­ra ha­cer que una ven­ta­na sea la "im­por­tan­te" es­tá ge­nia­l.

  • Me en­­can­­ta co­­­mo ma­­ne­­ja ma­­xi­­mi­­za­­ció­­n/­­mi­­ni­­mi­­za­­ció­­n.

¿Cuá­les co­sas no me gus­ta­ro­n?

  • El la­­yout "ti­­le­­d" tie­­ne múl­­ti­­ples ve­r­­sio­­­nes que se ca­m­­bian con Ctr­­l+I/D ... y a ve­­ces ni­n­­gu­­na es exa­c­­ta­­men­­te lo que quie­­ro? Ta­m­­bién sue­­le pa­sar que las mas "a­l­­ta­s" no pa­­re­­cen ha­­cer na­­da, pro­­­ba­­ble­­men­­te po­r­­que no ten­­go su­­fi­­cien­­tes ven­­ta­­nas en el mo­­­sai­­co.

  • Ya que no ten­go de­co­ra­cio­nes en las ven­ta­na­s, la in­con­sis­ten­cia bru­tal acer­ca de cual es el ata­jo pa­ra ce­rrar una apli­ca­ción es muy mo­les­to. Pue­de ser ctr­l+q o ctr­l+x o esc o nin­guno de ello­s. Ter­mino ha­cien­do al­t+­f4 que se sien­te co­mo Win­do­ws 3.11

  • La ex­­pe­­rien­­cia de scri­p­­ts pa­­ra KWin no es pe­r­­fe­c­­ta. In­s­­ta­­lé otro ha­­ce un tie­m­­po, lla­­ma­­do Qua­r­­te­­r-­­Ti­­li­n­­g, y lo de­­sin­s­­ta­­lé, ha­s­­ta do­n­­de sé no hay ra­s­­tros de él en mi sis­­te­­ma ... ex­­ce­p­­to por sus ata­­jos de te­­cla­­do, que van a en­­su­­ciar mi diá­­lo­­­go de ata­­jos pa­­ra sie­m­­pre.

El ex­pe­ri­men­to con­ti­núa!

PyQt Quickie: parsear línea de comandos

opt_parser = OptionParser()
opt_parser.add_option("-q", dest="quickly", action="store_true",
    help="Do it quickly (default=False)")
(options, args) = opt_parser.parse_args(sys.argv)
app = QApplication(sys.argv)
:
:
:

O tal vez incluso QA­ppli­ca­tio­n([]). Bueno, eso está mal. Y está mal en casi todos los tutoriales, también. ¿Porqué? Porque Qt (y por lo tanto PyQt) soporta un montón de opciones útiles. Al hacerlo como en ese primer listado, si le pasás "-style=oxygen" o lo que sea, va a pasar alguna de estas cosas:

  1. Op­­tPa­r­­ser te va a de­­cir que es una op­­ción in­­vá­­li­­da y abo­r­­tar

  2. Vas a ig­­no­­­rar la op­­ción y no vas a ha­­cer na­­da útil con ella

  3. Vas a te­­ner tu pro­­­pia op­­ción -s­­ty­­le y vas a ha­­cer dos co­­sas

Nin­gu­na de esas op­cio­nes es la idea. La ma­ne­ra co­rrec­ta de ha­cer­lo es és­ta:

opt_parser = OptionParser()
opt_parser.add_option("-q", dest="quickly", action="store_true",
    help="Do it quickly (default=False)")
app = QApplication(sys.argv)
(options, args) = opt_parser.parse_args(app.arguments())
:
:
:

De esta manera, le das a PyQt la oportunidad de procesar las opciones que reconoce y después, vos manejás el resto, porque a app.arguments() ya le sacaron todas las opciones de Qt.

El lado malo es que --help va a ser mas lento, porque tiene que instanciar QApplication al divino botón, y vas a tener opciones no documentadas. Soluciones para ambos problemas se dejan como ejercicio.

Shipping your PyQt app for windows


I ha­ve wri­tten about this in the pas­t, wi­th the ge­ne­ral con­clu­sion being "i­t's a pain in the ass".

So, no­w, he­re is how it's do­ne.

  1. Start wi­­th a wo­­­rking Py­­Qt appli­­ca­­tio­­n. In this exa­m­­ple, I wi­­ll use de­­vi­­cen­­zo­­.­­py mo­s­­tly be­­­cau­se:

    1. It is a wo­­­­­rking Py­­­Qt appli­­­ca­­­tio­­­n.

    2. It uses a big chunk of Py­­­Qt

    3. It's ea­sy to test

  2. Now you need a se­tu­p.­py. He­re's one that wo­rks, wi­th ex­ten­si­ve co­m­m­men­ts.

# We will be using py2exe to build the binaries.
# You may use other tools, but I know this one.

from distutils.core import setup
import py2exe

# Now you need to pass arguments to setup
# windows is a list of scripts that have their own UI and
# thus don't need to run in a console.

setup(windows=['devicenzo.py'],
      options={

# And now, configure py2exe by passing more options;

          'py2exe': {

# This is magic: if you don't add these, your .exe may
# or may not work on older/newer versions of windows.

              "dll_excludes": [
                  "MSVCP90.dll",
                  "MSWSOCK.dll",
                  "mswsock.dll",
                  "powrprof.dll",
                  ],

# Py2exe will not figure out that you need these on its own.
# You may need one, the other, or both.

              'includes': [
                  'sip',
                  'PyQt4.QtNetwork',
                  ],

# Optional: make one big exe with everything in it, or
# a folder with many things in it. Your choice
#             'bundle_files': 1,
          }
      },

# Qt's dynamically loaded plugins and py2exe really don't
# get along.

data_files = [
            ('phonon_backend', [
                'C:\Python27\Lib\site-packages\PyQt4\plugins\phonon_backend\phonon_ds94.dll'
                ]),
            ('imageplugins', [
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qgif4.dll',
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qjpeg4.dll',
            'c:\Python27\lib\site-packages\PyQt4\plugins\imageformats\qsvg4.dll',
            ]),
],

# If you choose the bundle above, you may want to use this, too.
#     zipfile=None,
)
  1. Run py­thon se­tu­p.­py py2exe and get a dist fol­der fu­ll of bi­na­ry good­ness.

And tha­t's it. Ex­cept of cour­se, tha­t's not it.

What this wi­ll do is crea­te a bi­na­ry se­t, ei­ther a fol­der fu­ll of things, or a sin­gle EXE fi­le. And tha­t's not enou­gh. You ha­ve to con­si­der at least the fo­llo­win­g:

  1. Put eve­­r­­y­­thing in re­­sou­r­­ce fi­­le­s: ima­­ges, qss fi­­le­s, ico­n­s, etc. Eve­­ry fi­­le your app nee­­d­s? Put it in a re­­sou­r­­ce fi­­le and load it from the­­re. That way you do­­n't ha­­ve to ca­­re about them if you go the "o­­­ne exe" road.

  2. Co­m­­pi­­le .ui fi­­les to .py (s­a­­me rea­­so­­n)

  3. Fi­­gu­­re out if you use Qt's plu­­gi­n­s, and make them wo­­­rk. This in­­clu­­des: using Pho­­­no­­n, using QtS­­Q­­L, and using any ima­­ge fo­r­­ma­­ts other than PN­­G.

After you ha­ve tha­t, are you do­ne? NO!

Your win­do­ws user wi­ll want an ins­ta­lle­r. I am not going to go in­to de­tail­s, but I had a good ti­me using Bi­tRo­ck's Ins­ta­ll­Buil­der for Qt. It's a ni­ce tool, and it wo­rks. Tha­t's a lot in this fiel­d.

But is that all? NO!

You ha­ve to take ca­re of the Vi­sual Stu­dio Runti­me. My su­gges­tio­n? Get a co­py of the 1.1MB vcre­dis­t_­x86.exe (not the lar­ger one, the 1.1MB one), and ei­ther te­ll peo­ple to ins­ta­ll it ma­nua­ll­y, or add it to your ins­ta­lle­r. You are le­ga­lly allo­wed (A­FAIK) to re­dis­tri­bu­te that thing as a who­le. But not wha­t's in it (un­le­ss you ha­ve a VS li­cen­se).

And we are do­ne? NO!

On­ce you run your app "ins­ta­lle­d", if it ever prin­ts an­y­thing to stde­rr, you wi­ll get ei­ther a dia­log te­lling you it di­d, or wor­se (if you are in ay­thing newer than XP), a dia­log te­lling you it can't wri­te to a log fi­le, and the app wi­ll ne­ver wo­rk agai­n.

This is be­cau­se py2exe ca­tches stde­rr and tries to save it on a lo­gfi­le. Whi­ch it tries to crea­te in the sa­me fol­der as the bi­na­r­y. Whi­ch is usua­lly not allo­wed be­cau­se of per­mis­sion­s.

So­lu­tio­n? Your app should ne­ver wri­te to stde­rr. Wri­te an ex­cep­thook and ca­tch tha­t. And then re­mo­ve stde­rr or re­pla­ce it wi­th a log fi­le, or so­me­thin­g. Just do­n't let py2exe do it, be­cau­se the way py2exe does it is bro­ken.

And is that it?

We­ll, ba­si­ca­lly ye­s. Of cour­se you should get 4 or 5 di­ffe­rent ver­sions of win­do­ws to test it on, but you are pre­tty mu­ch free to ship your app as you wis­h. Oh, mind you, do­n't upload it to do­wn­load­s.­com be­cau­se they wi­ll wrap your ins­ta­ller in a lar­ger one that ins­ta­lls bloa­twa­re and cra­p.

So, the­re you go.

De Vicenzo: un mini browser más copado

Si no que­rés leer eso de nue­vo, la idea es ver cuán­to có­di­go fal­ta pa­ra con­ver­tir el mo­tor We­bKit de Qt en un bro­w­ser "en se­rio­".

Pa­ra ello, me pu­se una me­ta com­ple­ta­men­te ar­bi­tra­ria de 128 lí­neas de có­di­go. En es­te mo­men­to lo de­cla­ro fea­tu­re-­com­ple­te (pe­ro bu­gg­y).

Los nue­vos fea­tu­res so­n:

  • Ta­­bbed bro­­w­­sing (se pue­­de agre­­ga­­r/s­a­­car ta­bs)

  • Book­­ma­­rks (se pue­­den agre­­ga­­r/s­a­­car y ele­­gir de una lis­­ta)

Es­to es lo que ya fun­cio­na­ba:

  • Zoom in (C­­tr­­l++)

  • Zoom out (C­­tr­­l+-)

  • Re­set Zoom (C­­tr­­l+=)

  • Bus­­car (C­­tr­­l+­­F)

  • Es­­co­n­­der bús­­que­­da (Es­­c)

  • Bo­­­to­­­nes de atrá­s/a­­de­­lan­­te y re­­ca­r­­gar

  • En­­tra­­da de URL que coi­n­­ci­­de con la pá­­gi­­na + au­­to­­­co­m­­ple­­ta­­do des­­de la his­­to­­­ria + arre­­gla la URL pues­­ta a ma­no (a­­gre­­ga http://, esas co­­sas)

  • Plu­­gins (i­n­­cluí­­do fla­s­h, que hay que ba­­jar apa­r­­te ;-)

  • El tí­­tu­­lo de la ven­­ta­­na mues­­tra el tí­­tu­­lo de la pá­­gi­­na (sin pro­­­pa­­gan­­da del bro­­w­se­­r)

  • Ba­­rra de pro­­­gre­­so pa­­ra la ca­r­­ga de la pá­­gi­­na

  • Ba­­rra de es­­ta­­do que mues­­tra el des­­tino de los li­nks cuan­­do pa­sas el mou­­se

  • To­­­ma una URL en la lí­­nea de co­­­man­­do (o abre http://­­p­­y­­tho­­n.org

  • Mu­l­­ti­­pla­­ta­­fo­r­­ma (fun­­cio­­­na do­n­­de fun­­cio­­­na QtWe­­bKi­­t)

Y cuan­to có­di­go es eso? 87 LI­NEAS.

O si pre­fe­rís la ver­sión que cum­ple con la PE­P8: 115 LI­NEAS.

Me ata­jo an­tes que al­guien lo di­ga: sí, el mo­tor de ren­de­ring y el toolkit son enor­mes. Lo que es­cri­bí es el "ch­ro­me" al­re­de­dor de eso, igual que ha­cen Aro­ra, Rekon­q, Ga­leo­n, Epi­phan­y, y mu­chos otros bro­w­ser­s.

Es un ch­ro­me sim­ple y mi­ni­ma­lis­ta, pe­ro fun­cio­na bas­tan­te bien, creo yo.

Aquí es­tá el de­mo (bu­gg­y):

Mas o me­nos ha­ce lo que es­pe­ra­ba que se pue­die­ra lo­gra­r, pe­ro le fal­tan arre­glo­s.

Pa­ra ver el có­di­go, va­yan a su ho­me pa­ge: http://­de­vi­cen­zo­.­google­co­de.­com

¿Cuanto browser entra en 128 líneas de código?

Lo que me gus­ta­ba de ese bro­w­ser de 42 lí­neas era que no era el ejem­plo tí­pi­co, don­de me­ten una vis­ta de We­bkit en una ven­ta­na, car­gan una pá­gi­na y te tra­tan de con­ven­cer de que son unos ba­na­na­s. Esa ver­sión son 7 lí­nea­s:

import sys
from PyQt4 import QtGui,QtCore,QtWebKit
app=QtGui.QApplication(sys.argv)
wb=QtWebKit.QWebView()
wb.setUrl(QtCore.QUrl('http://www.python.org'))
wb.show()
sys.exit(app.exec_())

O 6 si lo so­por­ta­ra un po­co más feo.

¡Pe­ro igua­l, el de 42 se veía úti­l!

This 42-line web browser, courtesy of #python and #qt -- http... on Twitpic

Esos bo­to­nes que se ven fun­cio­na­ban co­rrec­ta­men­te, ha­bi­li­tan­do y des­ha­bi­li­tan­do­se en el mo­men­to co­rrec­to, la en­tra­da de URL cam­bia­ba cuan­do ha­cías cli­ck en un li­nk, y otras co­si­tas así.

Ahí de­ci­dí em­pe­zar un pe­que­ño pro­yec­to in­ter­mi­ten­te de co­de gol­f: me­ter el me­jor bro­w­ser que pue­da en 128 lí­neas de có­di­go (sin con­tar co­men­ta­rios ni blan­co­s), usan­do so­lo Py­Q­t4.

Eso tie­ne un pro­pó­si­to úti­l: siem­pre sos­pe­ché que si uno asu­me Py­Qt co­mo par­te del sis­te­ma ba­se, la ma­yo­ría de las apli­ca­cio­nes en­tra­rían en diske­ttes. Es­ta en­tra unas 500 ve­ces en uno de 1.44MB (¡a­sí que po­dés usar los de 360 de co­m­mo­do­re sin du­pli­disk!)

has­ta aho­ra van 50 lí­nea­s, y tie­ne los si­guien­tes fea­tu­res:

  • Zoom in (C­­tr­­l++)

  • Zoom out (C­­tr­­l+-)

  • Re­set Zoom (C­­tr­­l+=)

  • Bus­­car (C­­tr­­l+­­F)

  • Es­­co­n­­der bús­­que­­da (Es­­c)

  • Bo­­­to­­­nes de atrá­s/a­­de­­lan­­te y re­­ca­r­­gar

  • En­­tra­­da de URL que coi­n­­ci­­de con la pá­­gi­­na + au­­to­­­co­m­­ple­­ta­­do des­­de la his­­to­­­ria + arre­­gla la URL pues­­ta a ma­no (a­­gre­­ga http://, esas co­­sas)

  • Plu­­gins (i­n­­cluí­­do fla­s­h, que hay que ba­­jar apa­r­­te ;-)

  • El tí­­tu­­lo de la ven­­ta­­na mues­­tra el tí­­tu­­lo de la pá­­gi­­na (sin pro­­­pa­­gan­­da del bro­­w­se­­r)

  • Ba­­rra de pro­­­gre­­so pa­­ra la ca­r­­ga de la pá­­gi­­na

  • Ba­­rra de es­­ta­­do que mues­­tra el des­­tino de los li­nks cuan­­do pa­sas el mou­­se

  • To­­­ma una URL en la lí­­nea de co­­­man­­do (o abre http://­­p­­y­­tho­­n.org

  • Mu­l­­ti­­pla­­ta­­fo­r­­ma (fun­­cio­­­na do­n­­de fun­­cio­­­na QtWe­­bKi­­t)

Fal­tan ta­bs y so­por­te de pro­x­y. Es­pe­ro que lle­ven unas 40 lí­neas má­s, pe­ro creo que ya es el más ca­paz de to­dos es­tos bro­w­sers de ejem­plo.

El có­di­go­... no es tan te­rri­ble. Uso mu­chos lamb­da­s, y los ar­gu­men­tos ke­yword de Py­Qt pa­ra co­nec­tar se­ña­le­s, que ha­cen que al­gu­nas lí­neas sean muy lar­ga­s, pe­ro no muy di­fí­ci­le­s. Se po­dría achi­car bas­tan­te to­da­vía!

Aquí es­tá en ac­ció­n:

Y aquí es­tá el có­di­go:

#!/usr/bin/env python
"A web browser that will never exceed 128 lines of code. (not counting blanks)"

import sys
from PyQt4 import QtGui,QtCore,QtWebKit

class MainWindow(QtGui.QMainWindow):
    def __init__(self, url):
        QtGui.QMainWindow.__init__(self)
        self.sb=self.statusBar()

        self.pbar = QtGui.QProgressBar()
        self.pbar.setMaximumWidth(120)
        self.wb=QtWebKit.QWebView(loadProgress = self.pbar.setValue, loadFinished = self.pbar.hide, loadStarted = self.pbar.show, titleChanged = self.setWindowTitle)
        self.setCentralWidget(self.wb)

        self.tb=self.addToolBar("Main Toolbar")
        for a in (QtWebKit.QWebPage.Back, QtWebKit.QWebPage.Forward, QtWebKit.QWebPage.Reload):
            self.tb.addAction(self.wb.pageAction(a))

        self.url = QtGui.QLineEdit(returnPressed = lambda:self.wb.setUrl(QtCore.QUrl.fromUserInput(self.url.text())))
        self.tb.addWidget(self.url)

        self.wb.urlChanged.connect(lambda u: self.url.setText(u.toString()))
        self.wb.urlChanged.connect(lambda: self.url.setCompleter(QtGui.QCompleter(QtCore.QStringList([QtCore.QString(i.url().toString()) for i in self.wb.history().items()]), caseSensitivity = QtCore.Qt.CaseInsensitive)))

        self.wb.statusBarMessage.connect(self.sb.showMessage)
        self.wb.page().linkHovered.connect(lambda l: self.sb.showMessage(l, 3000))

        self.search = QtGui.QLineEdit(returnPressed = lambda: self.wb.findText(self.search.text()))
        self.search.hide()
        self.showSearch = QtGui.QShortcut("Ctrl+F", self, activated = lambda: (self.search.show() , self.search.setFocus()))
        self.hideSearch = QtGui.QShortcut("Esc", self, activated = lambda: (self.search.hide(), self.wb.setFocus()))

        self.quit = QtGui.QShortcut("Ctrl+Q", self, activated = self.close)
        self.zoomIn = QtGui.QShortcut("Ctrl++", self, activated = lambda: self.wb.setZoomFactor(self.wb.zoomFactor()+.2))
        self.zoomOut = QtGui.QShortcut("Ctrl+-", self, activated = lambda: self.wb.setZoomFactor(self.wb.zoomFactor()-.2))
        self.zoomOne = QtGui.QShortcut("Ctrl+=", self, activated = lambda: self.wb.setZoomFactor(1))
        self.wb.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)

        self.sb.addPermanentWidget(self.search)
        self.sb.addPermanentWidget(self.pbar)
        self.wb.load(url)


if __name__ == "__main__":
    app=QtGui.QApplication(sys.argv)
    if len(sys.argv) > 1:
        url = QtCore.QUrl.fromUserInput(sys.argv[1])
    else:
        url = QtCore.QUrl('http://www.python.org')
    wb=MainWindow(url)
    wb.show()
    sys.exit(app.exec_())

Contents © 2000-2023 Roberto Alsina