El Conde de Montecristo
![]() |
Review:Finally finished. This is one *long* book (I read the unabridged version), and as usual, it's hard to read such an old book without the writing style making it somewhat harder. |
![]() |
Review:Finally finished. This is one *long* book (I read the unabridged version), and as usual, it's hard to read such an old book without the writing style making it somewhat harder. |
It seems it was only a few days ago that I started this project. Oh, wait, yes, it was just a few days ago!
If you don't want to read that again, the idea is to see just how much code is needed to turn Qt's WebKit engine into a fully-fledged browser.
To do that, I set myself a completely arbitrary limit: 128 lines of code.
So, as of now, I declare it feature-complete.
The new features are:
Tabbed browsing (you can add/remove tabs)
Bookmarks (you can add/remove them, and choose them from a drop-down menu)
This is what already worked:
Zoom in (Ctrl++)
Zoom out (Ctrl+-)
Reset Zoom (Ctrl+=)
Find (Ctrl+F)
Hide find (Esc)
Buttons for back/forward and reload
URL entry that matches the page + autocomplete from history + smart entry (adds http://, that kind of thing)
Plugins support (including flash)
The window title shows the page title (without browser advertising ;-)
Progress bar for page loading
Statusbar that shows hovered links URL
Takes a URL on the command line, or opens http://python.org
Multiplatform (works in any place QtWebKit works)
So... how much code was needed for this? 87 LINES OF CODE
Or if you want the PEP8-compliant version, 115 LINES OF CODE.
Before anyone says it: yes, I know the rendering engine and the toolkit are huge. What I wrote is just the chrome around them, just like Arora, Rekonq, Galeon, Epiphany and a bunch of others do.
It's simple, minimalistic chrome, but it works pretty good, IMVHO.
Here it is in (buggy) action:
It's more or less feature-complete for what I expected to be achievable, but it still needs some fixes.
You can see the code at it's own home page: http://devicenzo.googlecode.com
UPDATE: If you read this and all you can say is "oh, he's just embedding WebKit", I have two things to tell you:
Duh! Of course the 128 lines don't include the rendering engine, or the TCP implementation, or the GUI toolkit. This is about the rest of the browser, the part around the web rendering engine. You know, just like Arora, Rekonq, Epiphany, and everyone else that embeds webkit or mozilla does it? If you didn't get that before this explanation... facepalm.
Get your favourite webkit fork and try to do this much with the same amount of code. I dare you! I double dog dare you!
Now back to the original article
Today, because of a IRC chat, I tried to find a 42-line web browser I had written a while ago. Sadly, the pastebin where I posted it was dead, so I learned a lesson: It's not a good idea to trust a pastebin as code repository
What I liked about that 42-line browser was that it was not the typical example, where someone dumps a Webkit view in a window, loads a page and tries to convince you he's cool. That one is only 7 lines of code:
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_())
And if I wanted to make the code uglier, it could be done in 6.
But anyway, that 42-line browser actually looked useful!
Those buttons you see actually worked correctly, enabling and disabling at the right moment, the URL entry changed when you clicked on links, and some other bits.
So, I have decided to start a small, intermittent project of code golf: put as much browser as I can in 128 lines of code (not counting comments or blanks), starting with PyQt4.
This has a useful purpose: I always suspected that if you assumed PyQt was part of the base system, most apps would fit in floppies again. This one fits on a 1.44MB floppy some 500 times (so you could use 360KB commodore floppies if you prefer!).
So far, I am at about 50 lines, and it has the following features:
Zoom in (Ctrl++)
Zoom out (Ctrl+-)
Reset Zoom (Ctrl+=)
Find (Ctrl+F)
Hide find (Esc)
Buttons for back/forward and reload
URL entry that matches the page + autocomplete from history + smart entry (adds http://, that kind of thing)
Plugins support (including flash)
The window title shows the page title (without browser advertising ;-)
Progress bar for page loading
Statusbar that shows hovered links URL
Takes a URL on the command line, or opens http://python.org
Multiplatform (works in any place QtWebKit works)
Missing are tabs and proxy support. I expect those will take another 40 lines or so, but I think it's probably the most featureful of these toy browsers.
The code... it's not all that hard. I am using lambda a lot, and I am using PyQt's keyword arguments for signal connection which makes lines long, but not hard. It could be made much smaller!
Here it is in action:
And here's the code:
#!/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_())
Wearing the software engineer's hat: Code is the most trivial and the least important part of a feature.
—Michael Iatrou
Michael tweeted that, I replied, he replied, but what the heck, I think sometimes things can be better explained in more than 140 characters, thus this post [1].
So, why the mall and the outhouse? Because when we talk about software and code and features, we are not all talking about the same thing. Imagine if I told you that bricks are trivial. After all, they have existed in their current form for thousands of years, they are pretty simple to manufacture, and have no interesting features, really, except a certain resistence.
Now, suppose you are building an outhouse. Since you are a funny guy, you want to build an actual brick outhouse so you will use bricks to do it.
Now, since bricks are so boring, you may feel compelled to believe bricks are the least important part of your edifice, and that the overall design is more important. Should you carve a moon-shaped hole in the door? How deep should the latrine be?
However, that position is fatally flawed, since if you ignore those trivial, boring bricks, all you have is shit in a hole in the ground. That is because you are considering the bricks as just a mean to your end. You only care about the bricks insofar as they help you realize your grand outhouse vision. I am here to tell you that you are wrong.
The first way in which you are wrong is in that artificial separation between means and ends. Everyone is familiar with the ethical conundrum about whether the ends justify the means, but that's garbage. That'swhat you say when you try to convince yourself that doing things haphazardly is ok, because what you do is just the means to whatever other thing is the end. Life is not so easily divided into things that matter and things that don't.
Your work, your creation is not just some ideal isolated end towards which you travel across a sea of dirty means, trying to keep your silver armour clean. It's one whole thing. You are creating the means, you are creating your goal, you are responsible for both, and if you use shoddy bricks, your outhouse should shame you.
In the same way, if you do crappy code, your feature is demeaned. It may even work, but you will know it's built out of crap. You will know you will have to fix and maintain that crap for years to come, or, if you are lucky, ruin your karma by dumping it on the head of some poor sucker who follows your steps.
I am pretty much a materialist. If you remove the code, you don't have a feature, or software, you have a concept, maybe an idea, perhaps a design (or maybe not) but certainly not software, just like you don't have a brick outhouse without piling some damn bricks one on top of the other.
I always say, when I see someone calling himself a software engineer, that I am merely a software carpenter. I know my tools, I care about them, I use them as well as I can according to my lights [2] and I try to produce as good a piece of furniture as I can with what I am given.
This tends to produce humble software, but it's software that has one redeeming feature: it knows what it should do, and does it as well as I can make it. For example, I wrote rst2pdf. It's a program that takes some sort of text, and produces PDF files. It does that as well as I could manage. It does nothing else. It works well or not, but it is what it is, it has a purpose, a description and a goal, and I have tried to achieve that goal without embarrasing myself.
My programs are outhouses, made of carefully selected and considered bricks. They are not fancy, but they are what they are and you know it just by looking at them. And if you ever need an outhouse, well, an outhouse is what you should get.
Also, people tend to do weird stuff with them I never expected, but that's just the luck of the analogy.
But why did I mention malls in the title? Because malls are not outhouses. Malls are not done with a goal by themselves beyond making money for its builders. The actual function of a piece of mall is not even known when it's being built. Will this be a McDonalds, or will it be a comic book store? Who knows!
A mall is built quickly with whatever makes sense moneywise, and it should look bland and recognisable, to not scare the herd. It's a building made for pedestrians, but it's intended to confuse them and make the path form A to B as long and meandering as possible. The premises on which its design is based are all askew, corrupted and self-contradicting.
They also give builders a chance to make lots of money. Or to lose lots of money.
Nowadays, we live in an age of mall software. People build startups, get financing, build crappy software and sometimes they hit it big (Twitter, Facebook) or, more likely, fade into obscurity leaving behind nothing at all, except money lost and sad programmers who spent nights coding stuff noone will ever see or use, and not much else.
Far from me saying startups are not a noble or worthy endeavour. They are! It's just that people who work on them should realize that they are not building software. That's why code doesn't look important to them, because they are actually selling eyeballs to advertisers, or collected personal data from their users to whoever buys that, or captive public for game developers, or whatever your business model says (if you have one!).
They are building malls, where the value is not in the building, which is pretty ghastly and useless by itself, but on the people in it, those who rent space in the mall, those who will use the mall, the software, the social network, whatever it is you are building.
Twitter is not software, Facebook is not software. If they were, identi.ca and diaspora would be bigger! What they are is people in one place, like a mall is not a real building, but a collection of people under a roof.
So, there is nothing wrong with building malls. Just remember that your ends and your means are one and a whole, that code is important, that without code Facebook and Twitter don't work, and that without people they are a badland, and know what you are doing.
Because the only hard thing in life is knowing what you want to do. The rest is the easy part. And because malls without toilets suck.
![]() |
Review:The prose is not easy to read, because of the book's age, but it's what it is, and it's a classic tale. |