Skip to main content

Ralsina.Me — Roberto Alsina's website

PyBrowser: a nicer QTextBrowser

Work­ing on some of my per­son­al projects (specif­i­cal­ly KRsN and Not­ty), I have need­ed to make QTextBrows­er act a lit­tle more like a men­sch in a few ways:

  • Since it un­der­stands a lot of HTM­L, why not make it open im­ages ref­er­enced in the tex­t, and fol­low links?
  • Since it can show what is pret­ty much a web page, why not make it use HTTP and what­ev­er as need­ed?
  • Make it print. The helpview­er ex­am­ple has all the need­ed code. So, I stole it.

To achieve these goal­s, I had to dig in­to QMime­Source­Fac­to­ry and QDragOb­jec­t, and it ain't all that pret­ty :-)

Click here for the python file (it's in the pub­lic do­main, do as you wish with it).

Cur­rent prob­lem­s:

  • Slow
  • Very slow
  • Does­n't give close to enough feed­back
  • Should have a cache (it's triv­ial)

If any­one who has bet­ter python kung­fu can help me, I'm a hap­py guy.

The re­sult, in 143 lines of python, count­ing blanks and lit­tle de­mo prog­gie is this:

Source code for pybrowser.py
from qt import *
import urlparse
import traceback
from urllib import urlopen
import time


class  Mime­Fac­to­ry(QMimeSourceFactory):

        current=None

        def  __init__(self,browser):
                QMimeSourceFactory.__init__(self)
                self.browser=browser

        def  da­ta(self,name,context=None):
                if context:
                        return self.data(self.makeAbsolute(name,context))
                res=None
                (local,type,subtype)=self.fetchURL(str(name))
                if type=="image":
                        i=QImage()
                        #If image loading fails, return empty image
                        if not i.loadFromData(local,None):
                                i.create(10,10,8)
                        res=QImageDrag(i)
                else: #Assume it's text
                        res=QTextDrag(local)
                        if subtype=="html":
                                res.setSubtype("html")
                        else:
                                res.setSubtype("plain")
                self.current=res
                return self.current

        def  make­Ab­so­lute(self,name,context):
                return urlparse.urljoin(str(context),str(name))

        def  fetchURL(self,url):
                local=""
                type="text"
                subtype="plain"
                try:
                        remote=urlopen(url)
                        info=remote.info()
                        (type,subtype)=info.gettype().split("/")
                        local=remote.read()
                        l=info.getheader('content-length')
                        if not l:
                                l=0
                        else:
                                l=int(l)
                        print 'size',l

                        p=0
                        c=0
                        bsize=1024
                        while True:
                                self.browser.emit(PYSIGNAL('status'),("Downloading: "\
+url+"(%sKB)"%str(p/1024),)) self.browser.emit(PYSIGNAL('size'),(p,)) if l: self.browser.emit(PYSIGNAL('percent'),((p*100)/l,)) qApp.processEvents() #Lame attempt at making this report about once a second stamp=time.time() c=c+1 buf=remote.read(int(bsize)) delta=time.time()-stamp if delta <.5: #Data coming quickly, get more on each loop bsize=min(bsize*1.25,10000) elif delta >.75: #Data coming slowly, get less on each loop bsize=max(bsize*.75,100) #End of file if buf=='': self.browser.emit(PYSIGNAL('percent'),(100,)) break p=p+len(buf) local=local+buf except: traceback.print_exc() return (local,type,subtype) def print­Page(self): printer=QPrinter(QPrinter.HighResolution) printer.setFullPage(True) if printer.setup(self): p=QPainter(printer) if not p.isActive(): # starting printing failed return; metrics=QPaintDeviceMetrics(p.device()) dpiy = metrics.logicalDpiY() margin = int(((2/2.54)*dpiy)) # 2 cm margins body=QRect(margin, margin, metrics.width() - 2*margin, metrics.height()-2*margin) richText=QSimpleRichText( self.viewer.text(), QFont(), self.viewer.context(), self.viewer.styleSheet(), self.viewer.mimeSourceFactory(), body.height() ) richText.setWidth( p, body.width() ) view=QRect(body) page = 1 while True: richText.draw(p,body.left(),body.top(),view,self.colorGroup()) view.moveBy(0,body.height()) p.translate(0,-body.height()) p.drawText(view.right()-p.fontMetrics().width(str(page)), view.bottom()+p.fontMetrics().ascent()+5, str(page)); if view.top() >= richText.height(): break printer.newPage() page+=1 p.end() class QBrows­er(QTextBrowser): def __init__(self,parent=None): QTextBrowser.__init__(self,parent) self.mf=MimeFactory(self) self.setMimeSourceFactory(self.mf) def print­it(arg): print str(arg) if __name__=="__main__": from sys import argv app=QApplication(argv) w=QBrowser() w.connect(w,PYSIGNAL('percent'),printit) w.connect(w,PYSIGNAL('size'),printit) w.connect(w,PYSIGNAL('status'),printit) w.show() w.setSource(argv[1]) app.connect(app, SIGNAL("lastWindowClosed()") , app, SLOT("quit()")) app.exec_loop()
murdoch ravlin / 2007-03-25 17:53:

the
def printPage(self):

does nothing just delete it.

Roberto Alsina / 2007-03-25 19:13:

What do you mean it does nothing? I haven't looked at this code in three years or so, but I'm pretty sure it did something back then :-)

Fred / 2011-09-13 23:21:

Reasonably exciting post condition I'm not positive we agree also a talented deal in relation to largely of it. If you complete own selected groovy points I strength add up. Concerning several focus reverse phone lookup resembling so as to talk a propos a change cellular phone lookup lookup I heard in relation to. Stipulation I'm not positive stipulation you require with the intention of checked it not by home so with the purpose of you be capable of carry out a reverse telephone lookup today with the aim of unearth not on home every kinds of bits and pieces a propos one plus anything.

John / 2011-09-13 23:22:

Entirely interesting post but I'm not persuaded we agree also to a great extent as regards generally of it. Stipulation you carry out own some great points I force add together. On uncommon issue reverse phone lookup resembling to talk a propos a repeal telephone lookup search I heard on the topic of. If I'm not definite condition you want to checkered it not in so to facilitate you be capable of carry out a reverse mobile phone lookup at the moment with the purpose of come across elsewhere both kinds of material on the matter of part in addition to anything.

employment background check / 2011-12-27 23:23:


Hi very nice article


Contents © 2000-2023 Roberto Alsina