Skip to main content

Ralsina.Me — Roberto Alsina's website

Posts about python (old posts, page 64)

This is why Qt (and PyQt) are cool

Ale­jan­dro Dolina once wrote (and this is from mem­o­ry that's prob­a­bly 25 years old) of a round ta­ble dis­cussing "What's Tan­go?", and how af­ter two hours of dis­cussing the na­ture, char­ac­ter­is­tics and his­to­ry of tan­go, one of the mem­bers of the pan­el picked up a ban­doneón, played "El apache ar­genti­no" stood up and left with­out say­ing a word.

So, why are Qt and PyQt cool?

Au­dio play­er wid­get:

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

import sys, os
from PyQt4 import QtCore, QtGui, uic
from PyQt4.phonon import Phonon
import icons_rc

class AudioPlayer(QtGui.QWidget):
    def __init__(self, url, parent = None):

        self.url = url

        QtGui.QWidget.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding,
            QtGui.QSizePolicy.Preferred)


        self.player = Phonon.createPlayer(Phonon.MusicCategory,
            Phonon.MediaSource(url))
        self.player.setTickInterval(100)
        self.player.tick.connect(self.tock)

        self.play_pause = QtGui.QPushButton(self)
        self.play_pause.setIcon(QtGui.QIcon(':/icons/player_play.svg'))
        self.play_pause.clicked.connect(self.playClicked)
        self.player.stateChanged.connect(self.stateChanged)

        self.slider = Phonon.SeekSlider(self.player , self)

        self.status = QtGui.QLabel(self)
        self.status.setAlignment(QtCore.Qt.AlignRight |
            QtCore.Qt.AlignVCenter)

        self.download = QtGui.QPushButton("Download", self)
        self.download.clicked.connect(self.fetch)

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(self.play_pause)
        layout.addWidget(self.slider)
        layout.addWidget(self.status)
        layout.addWidget(self.download)

    def playClicked(self):
        if self.player.state() == Phonon.PlayingState:
            self.player.pause()
        else:
            self.player.play()

    def stateChanged(self, new, old):
        if new == Phonon.PlayingState:
            self.play_pause.setIcon(QtGui.QIcon(':/icons/player_pause.svg'))
        else:
            self.play_pause.setIcon(QtGui.QIcon(':/icons/player_play.svg'))

    def tock(self, time):
        time = time/1000
        h = time/3600
        m = (time-3600*h) / 60
        s = (time-3600*h-m*60)
        self.status.setText('%02d:%02d:%02d'%(h,m,s))

    def fetch(self):
        print 'Should download %s'%self.url

def main():
    app = QtGui.QApplication(sys.argv)
    window=AudioPlayer(sys.argv[1])
    window.show()
    # It's exec_ because exec is a reserved word in Python
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Video play­er wid­get:

import sys, os
from PyQt4 import QtCore, QtGui, uic
from PyQt4.phonon import Phonon
import icons_rc

class VideoPlayer(QtGui.QWidget):
    def __init__(self, url, parent = None):

        self.url = url

        QtGui.QWidget.__init__(self, parent)
        self.setSizePolicy(QtGui.QSizePolicy.Expanding,
            QtGui.QSizePolicy.Preferred)


        self.player = Phonon.VideoPlayer(Phonon.VideoCategory,self)
        self.player.load(Phonon.MediaSource(self.url))
        self.player.mediaObject().setTickInterval(100)
        self.player.mediaObject().tick.connect(self.tock)

        self.play_pause = QtGui.QPushButton(self)
        self.play_pause.setIcon(QtGui.QIcon(':/icons/player_play.svg'))
        self.play_pause.clicked.connect(self.playClicked)
        self.player.mediaObject().stateChanged.connect(self.stateChanged)

        self.slider = Phonon.SeekSlider(self.player.mediaObject() , self)

        self.status = QtGui.QLabel(self)
        self.status.setAlignment(QtCore.Qt.AlignRight |
            QtCore.Qt.AlignVCenter)

        self.download = QtGui.QPushButton("Download", self)
        self.download.clicked.connect(self.fetch)
        topLayout = QtGui.QVBoxLayout(self)
        topLayout.addWidget(self.player)
        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(self.play_pause)
        layout.addWidget(self.slider)
        layout.addWidget(self.status)
        layout.addWidget(self.download)
        topLayout.addLayout(layout)
        self.setLayout(topLayout)

    def playClicked(self):
        if self.player.mediaObject().state() == Phonon.PlayingState:
            self.player.pause()
        else:
            self.player.play()

    def stateChanged(self, new, old):
        if new == Phonon.PlayingState:
            self.play_pause.setIcon(QtGui.QIcon(':/icons/player_pause.svg'))
        else:
            self.play_pause.setIcon(QtGui.QIcon(':/icons/player_play.svg'))

    def tock(self, time):
        time = time/1000
        h = time/3600
        m = (time-3600*h) / 60
        s = (time-3600*h-m*60)
        self.status.setText('%02d:%02d:%02d'%(h,m,s))

    def fetch(self):
        print 'Should download %s'%self.url

def main():
    app = QtGui.QApplication(sys.argv)
    window=VideoPlayer(sys.argv[1])
    window.show()
    # It's exec_ because exec is a reserved word in Python
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

...

Desktop apps and clouds (with video)

I en­joy cre­at­ing desk­top ap­pli­ca­tion­s. That means I may be a mem­ber of a dy­ing breed, since web apps are go­ing to make us all ob­so­lete next week, but I do en­joy do­ing it.

The bad side of it is, of course that some­times it's much more con­ve­nient to use a web ap­pli­ca­tion. For ex­am­ple, I have aban­doned my own ba­by (uRSSus) be­cause google read­er is just eas­i­er and more con­ve­nient to use.

But then I thought... what both­ers me of uRSSus? And there are quite a few things!

  1. It's not in all com­put­ers I may use

    That means I will not ev­er be able to use it ex­­clu­­sive­­ly.

  2. It's pret­­ty use­­less with­­out an In­­ter­net con­nec­­tion (but so is google read­­er most­­ly)

  3. Since I can't use it ex­­clu­­sive­­ly, I end with feeds on uRSSus that are not on google read­­er and vicev­er­sa.

  4. It's freak­ing slow

So, I de­cid­ed to see what I could do about that with­out giv­ing up the good side of uRSSus:

  1. It looks much nicer than a web ap­p, be­­cause it looks like a desk­­top app

  2. It does things like open­ing the site in­­stead of show­ing the feed item (great for par­­tial con­­tent feed­s)

  3. I wrote it (yes, that's a fea­­ture for me. I like self­­-­­made pro­­gram­s)

So, this at­tempt at rewrit­ing the desk­top RSS read­er pro­duced this:

As you can see in the above video, this read­er syncs the sub­scrip­tion list to google read­er. It will al­so even­tu­al­ly sync your read­/un­read post­s.

It still can open full sites in­stead of feed item­s, it has/will have a heck of an off­line mode (full pages cap­tured as im­ages, for ex­am­ple), and... it's very very fast.

It's much faster than google read­er in Chromi­um, and hel­la faster than uRSSus. That was done via smarter cod­ing, so it prob­a­bly means I was brain­dead be­fore and ex­pe­ri­enced a mi­nor re­cov­ery.

The code is not fit for re­lease (for ex­am­ple, the data­base schema will change) but you can try it: http://­code.­google.­com/p/kakawana/­source/check­out

Slow-Slow and Fast-Fast (video)

My pre­vi­ous post ex­plained how to cache whole web pages as im­ages. Now see it in ac­tion. This is a light­weight RSS read­er, op­ti­mized for com­ic books (but it works for any feed) and for off­line use (but it works on­line too, of course).

Not ready for pub­lic use yet, but if you look around you can find the code some­where ;-)

Capturing a webpage as an image using Pyhon and Qt

For a small project I am do­ing I want­ed the ca­pa­bil­i­ty to see web pages off­line. So, I start­ed think­ing of a way to do that, and all so­lu­tions were an­noy­ing or im­prac­ti­cal.

So, I googled and found Cu­ty­Capt which us­es Qt and We­bKit to turn web pages in­to im­ages. Good enough for me!

Since I want­ed to use this from a PyQt ap­p, it makes sense to do the same thing Cu­ty­Capt does, but as a python mod­ule/scrip­t, so here's a quick im­ple­men­ta­tion that works for me, even if it lacks a bunch of Cu­ty­Cap­t's fea­tures.

With a lit­tle ex­tra ef­fort, it can even save as PDF or SVG, which would let you use it al­most like a re­al web page.

You just use it like this:

python  capty.py http://www.kde.org kde.png

And here's the code [down­load cap­ty.py]

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

"""This tries to do more or less the same thing as CutyCapt, but as a
python module.

This is a derived work from CutyCapt: http://cutycapt.sourceforge.net/

////////////////////////////////////////////////////////////////////
//
// CutyCapt - A Qt WebKit Web Page Rendering Capture Utility
//
// Copyright (C) 2003-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// $Id$
//
////////////////////////////////////////////////////////////////////

"""

import sys
from PyQt4 import QtCore, QtGui, QtWebKit


class Capturer(object):
    """A class to capture webpages as images"""

    def __init__(self, url, filename):
        self.url = url
        self.filename = filename
        self.saw_initial_layout = False
        self.saw_document_complete = False

    def loadFinishedSlot(self):
        self.saw_document_complete = True
        if self.saw_initial_layout and self.saw_document_complete:
            self.doCapture()

    def initialLayoutSlot(self):
        self.saw_initial_layout = True
        if self.saw_initial_layout and self.saw_document_complete:
            self.doCapture()

    def capture(self):
        """Captures url as an image to the file specified"""
        self.wb = QtWebKit.QWebPage()
        self.wb.mainFrame().setScrollBarPolicy(
            QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff)
        self.wb.mainFrame().setScrollBarPolicy(
            QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff)

        self.wb.loadFinished.connect(self.loadFinishedSlot)
        self.wb.mainFrame().initialLayoutCompleted.connect(
            self.initialLayoutSlot)

        self.wb.mainFrame().load(QtCore.QUrl(self.url))

    def doCapture(self):
        self.wb.setViewportSize(self.wb.mainFrame().contentsSize())
        img = QtGui.QImage(self.wb.viewportSize(), QtGui.QImage.Format_ARGB32)
        painter = QtGui.QPainter(img)
        self.wb.mainFrame().render(painter)
        painter.end()
        img.save(self.filename)
        QtCore.QCoreApplication.instance().quit()

if __name__ == "__main__":
    """Run a simple capture"""
    app = QtGui.QApplication(sys.argv)
    c = Capturer(sys.argv[1], sys.argv[2])
    c.capture()
    app.exec_()

Learn python! For free! With me! (in part)

I'm one of the speak­ers for the free python cour­ses in FM La Tribu, in Buenos Aires.

Ev­ery sat­ur­day you can learn some­thing from one of the best python pro­gram­mers in a thou­sand miles, or from me.

I'll be teach­ing about vir­tualen­v, build­out, nose and oth­er things on Au­gust 21, about GUI stuff on Sep­tem­ber 25 and Oc­to­ber 2, and about PyQt in Oc­to­ber 30.

This is all for free, and I hope lots of peo­ple show up!

The full sched­ule is here.


Contents © 2000-2023 Roberto Alsina