Benjamin Meyer
Qt
WebKit
Internet
Posted by Benjamin Meyer
 in Qt, WebKit, Internet
 on Monday, August 04, 2008 @ 15:01

The past few weeks I have been hacking on another feature in Qt. Going with the theme of making Qt 4.5 faster I added a cache system to QNetworkAccessManager and provided a simple QNetworkDiskCache that can be used by most applications. Under the hood the new http backend has been enhanced to support all the http rfc cache features you would expect (even behave better than FireFox in one case). Adding a cache system is very important to a lot of our customers.

- Anyone who wants to use rss/atom feeds absolutely must have a cache system in place.
- Anyone who uses QtWebKit has been looking forward to a cache system.
- Any other application that pulls down files off the network such as in XMLPatterns can use the cache to improve performance and reduce network overhead.

To store the cache QDesktopServices::storageLocation has a new enum CacheLocation that can be used to determine a proper cross platform location for where cache files can be stored. The included QNetworkDiskCache is simple and should be good enough for a lot of customers, but for those that want to write a highly optimized one for small devices or to hook into another cache system (such as MSIE’s) the interface QAbstractNetworkCache is provided so you can implement custom cache systems.

For many applications enabling the cache will be as simple as:
QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
QString location = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
diskCache->setCacheDirectory(location);
networkAccessManager->setCache(diskCache);

To see it in action you can checkout the demo browser the 4.5 snapshots where I have enabled it.

Benjamin Meyer
Qt
Posted by Benjamin Meyer
 in Qt
 on Wednesday, July 02, 2008 @ 14:56

After 4.4.0 was out I set aside some time to go though the feature requests for QTabBar & QTabWidget. After making a list of the most voted for suggestions I have implemented some features that will be in 4.5.0 that should make some developers happy.

In the past ten years the tab widget has gone from being something you see in a settings dialog to the widget that is the central widget of QMainWindow for many applications. Trying to use QTabWidget in the main window as it is in 4.4.0 has a number of shortcomings. One big issue is that if you maximize your application and move your mouse to the far right and try to scroll it wont because the mouse is actually over the 2px frame and not the scrollbar. I present for your enjoyment QTabWidget::documentMode which will only put the frame on the top with the tabs. Another very annoying problem for QTabWidget was that if you wanted to add a context menu or handle any event in the tab bar area you had to subclass both QTabBar and QTabWidget because the empty area was actually owned by QTabWidget and the tab bar widget was only as big as the tabs. Document mode will cause the tab bar widget to take up the entire space above the tab widget which will simplify a lot of code.

Looking around at what features people have implemented in their subclasses of QTabBar many have added some sort of hack to be able to either put a widget on a tab or the ability to paint a button. Doing this right, QTabBar now has a function QTabBar::setTabButton to let you put a widget on the left or right hand side of each tab. Taking this one step further there is also a convenience property
QTabBar::tabsClosable that when enabled will automatically add a close button to each tab. The close button it painted by the style using PE_IndicatorTabClose. When the close button is pressed the signal tabCloseRequested is emitted. Note that the location of the close button is also determined by the style so you can not guarantee that it will always be on the right hand side and the side with the close button should be determined by a call to the style with SH_TabBar_CloseButtonPosition. Custom styles that paint tabs themselves needs to be updated to take into account the space needed for buttons (see QStyleOptionTabV3).

When a tab is closed rather then always selecting the tab to the right there is now a
QTabBar::selectionBehaviorOnRemove property to decide which tab should be selected. The three included values are left, right and the last selected tab.

Another highly requested feature is support for the ability to move tabs. There is even a number of patches floating around out there that add many different moving schemes. A moveTab function was added, but taking it to the next level the property movable has been added which lets you drag the tabs around and as you pass over tabs they slide to their new position (i.e. animated sexiness).

Because animations are hard to describe I made a short video. In the video I first add a QTabWidget to a QMainWindow, turn on document mode, closeable tabs, and the moveable property. Then I preview it in plastique and drag around a few tabs. I start out using cleanlooks so it picks up the Gtk close button and then in plastique you can see it using the default button.

Lastly for applications running in OS X when using document mode the style paints the entire tab bar to match those tabbed applications like Safari and Terminal that are found on OS X. Below is a screenshots of a QTabWidget in a QMainWindow with a QWebView. With OS X it is very clear that the tab widget now goes to the edge of the screen.

osxstyletabbar.png

Hope you all enjoy it. Some of the patches are already in Qt main, but you can find all of these in tonight’s snapshots. Let me know if you notice any issues with the new features.

Benjamin Meyer
Qt
WebKit
Posted by Benjamin Meyer
 in Qt, WebKit
 on Monday, April 28, 2008 @ 11:15

Arora

The demo browser in Qt 4.4 has obtained a lot of attention and interest. Now that 4.4 is almost out I have forked it and started a new project called Arora where development can continue, features can be added, and anyone who wants can contribute. The source code is in a Git repository and is currently hosted on github. Along with some improvements and features there are autotest and manual tests. Git (and GitHub) makes it pretty easy for new developers to a project to fork off a branch to develop features that can be later merged back into the project. Feel free to grab the source and add the feature that you miss the most or take one of the fun items off the TODO list. To any artists out there we need an icon to match the new name. Given that this is a fork of the demo future news on Arora will be on the project’s home page or on the Arora blog.

QtWebKit & WebKit trunk

In an effort to stabilize QtWebKit before the 4.4 release QtWebKit was forked from WebKit about six months ago. This is why the acid3 test score in Qt4.4 didn’t match the latest Safari. Now that 4.4 is almost out the door work has begun on integrating the changes into WebKit trunk for 4.5. Not everything is in yet, but already a lot works and it is very exciting. I’ll let this screen shot speak for itself:

acid3.png

The remaining failing tests mostly relate to SVG Fonts. If you are interested in helping out feel free to join #webkit or #qtwebkit on freenode. Instructions on how to build Arora with QtWebKit trunk can be found in the Arora source wiki page.

Benjamin Meyer
Qt
Itemviews
Posted by Benjamin Meyer
 in Qt, Itemviews
 on Tuesday, March 25, 2008 @ 10:04

History in a web browser has a number of challenges that make it a good demonstration of Qt’s model/view. The history is used in the url completer, the history dialog, the history menu and lastly by WebKit itself for determining if you have been somewhere before. And just to add to the mix it need to be able to scale. A year of history could easily be 20,000+ entries. The History component of the demo browser is more advanced then the examples in Qt and shows off some more the cool aspects of model/view and it has been suggested to me that writing up a little description of how it works might be useful to some developers looking to use model/view in their own applications.

I have spent time working on item view and know it pretty well (I put together the Modeltest and ItemView Tips) so the history component of the demo browser was fun to write. The source code for all the classes can be found in demos/browser/history*.

The first class is the history manager who’s data structure is optimized for history, not for dealing with item views. The class loads and stores the history, emits signals when there is a new entry or one is removed and that is about it. This class should be able to stand on its own if it was pulled out and used without itemview. I wanted to keep the memory usage down as much as possible. For 20K entries it was only using around 10MB of ram when I was done.

On top of the HistoryManager is the first model, a QAbstractTableModel (HistoryModel) who’s only job is to wrap the history model. It doesn’t do anything other then implementing functions such as rowCount(), flags(), and data(), matching up the itemview functionality with the corresponding functionality in the history model.

On top of that is a QAbstractProxyModel (HistoryFilterModel) who job it is to filter out duplicates. Looking at browsers I noticed that if you visit a link twice the browsers always hide everything, but the most recent visit. A hash is used to determine if a link has been visited along with the most recent location of the visit. This model has a convenience function that is used by WebKit to determine if a link has been visited.

On top of HistoryFilterModel is another QAbstractProxyModel (CompleterModel) that adds a new row for every entry stripping the front of the url so the completer can complete on “http://trolltech.com” or “trolltech.com”. This model is used by the QCompleter in the url line edit.

For the history menu and the history dialog the browser wants to display a tree based structure where each top level node is a Date with history entries for that date as the children. So on top of HistoryFilterModel I wrote a new QAbstractProxyModel (HistoryTreeModel) that converted the table into a tree. Converting the table into a tree was a little tricky, but the modeltest was very useful for detecting problems.

Before putting the HistoryTreeModel into the dialog I wrapped it one last time with a QSortFilterProxyModel so that the user can do on the fly searching. The searching (really filtering) is very fast. This is put into the HistoryDialog that has a EditTreeView which is a normal QTreeView, but with added support for the delete key to remove one row from the model.

On top of the HistoryTreeModel there is another QAbstractProxyModel (HistoryMenuModel) that slightly modifies the tree moving the most recent history entries inside of the root rather the first date folder. To do the mapping I translate each node in the tree all the way up to the table and back again to convert to the source index. This class was just a big pain to code and took a solid day to do. Looking back adding a hack to the menu rather then writing this class seems like a reasonable option given the effort.

Both history and bookmarks show up in the top level menu so I wrote a subclass of QMenu (ModelMenu) which can take a QAbstractItemModel and on the fly the ModelMenu will populate it with actions from the model. This is pretty straight forward and works very well.

Overall the History classes show off how you can use item view to to write some pretty cool stuff that scales too. History’s classes contains a wrapper, some classes that filter out items, some classes that add new entires, modify or even move items around and lastly some views the present the data in very different ways.

Using my inkscape skills (or lack of) here is a diagram of the classes to help make better sense of the relationships.

itemview.png

Benjamin Meyer
Qt
WebKit
Internet
Posted by Benjamin Meyer
 in Qt, WebKit, Internet
 on Wednesday, March 05, 2008 @ 10:43

In 4.4 Qt is getting a new module, qtwebkit. I have been watching it develop over the past year and have been eager to play around with it in Qt. Shortly after the module was merged into the main Qt branch I began working on a small browser in my spare time. Something I could use for at least some of my daily browsing. By eating my own dogfood I could catch errors in WebKit, the new networking code, and especially spot trouble areas in the API before the release.

*Disclaimer: This is just a little demo project of mine, not a FireFox replacement.*

Startup speed
One nice feature of Qt4 over Qt3 is the startup speed. I have seen reviews of KDE4 that even mention the nice bump that KDE applications received, even application have have just been porting and no extra refactoring was done. When Zack was first working on the webkit Qt integration I noticed that his test application would launch near instantaneous when Qt was in memory already. So if WebKit can launch fast, and Qt can launch fast I was definitely going to make sure that some silly demo code didn’t make it launch slow. So even with an icon database, history, cookies, tabs, etc the demo can still launches quickly. So I think it is fast enough.

Networking
In 4.4 there are some new networking class built up around the new QNetworkAccessManager class. Check out Thiago’s recent blog entry One more piece falling into place: Network Access for some more information about all the new goodies included with it. On top of networking classes I created some gui components including a minimal download manager with the usual suspects such as a progress bar, download speed, etc. A nice little demo for the download manager in its own right. Something new that QNetworkAccessManager uses that not mentioned before is the QCookieJar that is included with Qt4. Wrapping it I added saving/loading, and the usual web browser features.

User Agent
Once the browser was in good enough shape to be use every day I setup Gnome and KDE to choose the demo browser as my preferred browser. So anytime I clicked on links from e-mail or from #qt on irc the demo browser would be used and in the server logs a new user agent would show up for the mysterious demobrowser/0.1. The default qtwebkit user-agent is constructed from QCoreApplication::applicationName and the new QCoreApplication::applicationVersion properties. So if you integrate QtWebKit in your application it will automatically include the application name and version in the user-agent. This is of course configurable. Because it is just a demo and for simplicity sake the demo behaves like a single application, when launched it will contact the already running browser and tell it to open the url from the command line arguments if there is one (configurable to open in new window or tab of course).

Session Managment
Developing the demo I knew that I typically leave my browser open for days at a time and loosing your open tabs (or anything really) in a crash is never good. Also while developing the demo I would want to quickly restart it as changes were made. With that in mind the demo contains is a little watch dog class that makes sure that at most you will only loose the up to the last three seconds in the event of a crash. Not just tabs, but history, cookies, and everything else is protected. Restarting the browser selecting History/Restore Last session will quickly re-open all the top level windows and their tabs from last time.

x-qt-plugin
QtWebKit include a plugin framework and QWebPage has extra built in support for one specific plugin type, x-qt-plugin which the demobrowser implements (see QWebPage::createPlugin) The demo browser lets you add any Qt widget into the webpage. Just like with QScript, signals, slots, properties are all fully accessible from JavaScript. You could even embed QWebView and make a browser in the browser if you wanted to. With the demo browser if you load up a webpage with the following code you will find a loading QProgressbar.
<object type="application/x-qt-plugin" classid="QProgressBar" name="progressbar" height=30></object>
<script>
function display(){
if (++document.progressbar.value != 100)
setTimeout("display()", 50)
}
display();
</script>

More than for a browser
Already there have been Qt developers blogging about their interesting QtWebKit projects such as Amarok and KDE Plasmoids. I am looking forward to see what everyone does with QtWebKit.

FAQ:
The demo can be found in the demos/browser directory of the Qt package. The browser in the 4.4 snapshots has more then the beta including proxy configuration, in page search, improved keyboard shortcuts and some of the features mentioned here.

  • This is only a demo, I tried to not do anything that couldn’t be done with Qt easily, so adding bunzip, tar, and bittorrent support to the download manager wasn’t on my todo. There are plenty of neat things to do first such as bookmarks.
  • Although plugin support was added to QtWebKit for 4.4, the official Adobe flash plugin support will have to wait for 4.5. Or more to the point the netscape plugin support wont be added until 4.5
  • gmail currently does not work.
  • There are no known crashes so please report any that you find.
  • I plan on continuing to develop this demo, for example I have bookmarks mostly done in a branch and am am working on adding more autotests before merging.
  • Many cookie issues can be worked around by turning on “Accept All Cookies” in the preference, the cookiejar has a few fixes that are not in yet.
  • Feel free to check out the code for the demo. The total size is quiet small and I tried to keep the code clean.

A blog about a browser is only complete with a screenshot so here it is running on Windows (Vista), Linux (Gnome), and OS X (Tiger)
demobrowser.png
Having a cross platform browser who’s code base is small and completely open kicks ass.

A big thanks to Jen, Jens, Holger, Morten, Thiago, Simon, Jesper, Thomas, Paul, and everyone else who gave feedback/patches.

Benjamin Meyer
Qt
KDE
Posted by Benjamin Meyer
 in Qt, KDE
 on Tuesday, February 26, 2008 @ 12:25

One small feature that I have wanted Qt to have when writing applications was a way to get some common directories. Where is the desktop, the music directory, or even the location where I should store my data files? On Mac, Windows and Linux these have always been different. A big problem in adding this feature has been Linux with the lack of any sort of standard, but this past year with the xdg-user-dirs spec released and with distributions like Ubuntu and others adopting it I decided it was a good time to revisit the problem. In Qt 4.4 QDesktopServices got a new function, storageLocation that returns the default system directory where files of a certain type belong. The currently supported types are: Desktop, Documents, Fonts, Applications, Music, Movies, Pictures, Temp, Home, and Data. Note that storage location can return an empty string or even a directory that currently doesn’t exists such as where the Music directory should be, but the user deleted it.

In a media player you could set the initial directory of the file dialog to open the music directory.

    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                     QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
                                                     tr("Media Files (*.ogg *.mp3 *.flac *.wav)"));

Or when looking to store some application data, say palettes for an image editor.

    QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
    // embedded platforms currently don't define this location and you don't want to write to /foo
    if (directory.isEmpty())
        directory = QDir::homePath() + "/." + QCoreApplication::applicationName();
    // the first time I am saving data
    if (!QFile::exists(directory)) {
        QDir dir;
        dir.mkpath(directory);
    }
    // save my data
    QFile file(directory + "/custompalettes.dat");
    ...
Benjamin Meyer
Qt
KDE
Posted by Benjamin Meyer
 in Qt, KDE
 on Friday, February 22, 2008 @ 13:46

Finishing off IPC features in 4.4, two small classes that have been added are QLocalServer and QLocalSocket. On Windows this is a named pipe and on Unix this is a local domain socket. The API is almost identical to QAbstractSocket so switching from a local socket to a tcp socket and vice versa is very easy if you want to. For those KDE applications that are currently using KLocalServer/KLocalSocket with minor function name changes most of them should be able to switch and then they will get another feature working on Windows. If you have a Qt 4.4 snapshot in the examples/ipc/ directory you will find two new examples that are ports of the fortune server and client.
qlocalserversocket.png

Benjamin Meyer
Qt
Posted by Benjamin Meyer
 in Qt
 on Monday, October 15, 2007 @ 08:39

Traditionally when you wanted to map a file into memory on Unix this was done using mmap and on Windows with CreateFileMapping. Continuing the tradition of simple, consistent API’s, in 4.4 QFile will have two new functions: map() and unmap() that provide the ability to map files into memory.

An quick example:

QFile file("foo");
file.open(QFile::ReadOnly);
uchar *memory = file.map(0, file.size());
if (memory) {
// have some fun with the data
file.unmap();
}

You can check out more in the
map and unmap documentation.

Benjamin Meyer
Qt
KDE
Posted by Benjamin Meyer
 in Qt, KDE
 on Friday, August 10, 2007 @ 07:23

One of Qt’s solutions is a class called QtSharedMemory. It lets you share and lock memory between two processes on the same system. Originally written for Qt3 and then ported to Qt4 I set aside some time and created the successors of this class which will be part of Qt 4.4. The locking capability found in QtSharedMemory is useful for cases other then just shared memory and it was taken out and made into its own class. A better API and a nice example the code is in 4.4 and can be seen in the snapshots today.

Here is a “Hello world” shared memory example.
First process A would do the following:

QSharedMemory sharedMemory("foobar");
sharedMemory.create(1024);
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
char *text = "hello world";
memcpy(to, text, strlen(text)+1);
sharedMemory.unlock();

And then until process A destroyed its sharedMemory variable process B can access the “hello world” data.

QSharedMemory sharedMemory("foobar");
sharedMemory.attach();
sharedMemory.lock();
char *to = (char*)sharedMemory.data();
qDebug() << to;
sharedMemory.unlock();

QSharedMemory uses the second new class, QSystemSemaphore to provide system wide locking. This is useful when you have different processes accessing the same data. If you are using threads you should use QSemaphore which is much lighter and has more capabilities. Check out the shared memory example documentation which walks through more detailed usage of shared memory.

Due to the different ways that every platform implement these features there are a few exceptions to be away of and they are documented in the class description (for example on HP-UX only one attach to a shared memory segment is allowed per process). QtSharedMemory and QSharedMemory and incompatible to prevent accidentally using them together and it is advisable to stick with one or the other.

The API for these classes has gone through a number of revisions. Check out the class API’s and example and let us know what you think.

Documentation links:
SharedMemory
SystemSemaphore
SharedMemory Example

Benjamin Meyer
Qt
KDE
Posted by Benjamin Meyer
 in Qt, KDE
 on Thursday, March 29, 2007 @ 09:09

Here’s a follow up on the recent blog post which presented a new filedialog interface. Listening to the feedback it has been changed back to look very similar to the one in 4.2. The big differences are that the Save As collapsing functionality has been remove and the line edit is back and gets focus by default. The saving has been fixes so adding folders to the sidebar will be saved, along with history. Note that the painting issue on the cancel button is being looked into already and will be fixed shortly (it only occurs in Plastique style).

Feel free to provide feedback on what you think of this change.

Open dialog
Open Dialog

Save dialog
Save Dialog

P.S. A little hidden feature that has been in the file dialog sense at least 4.2 is that if you type “..” in the line edit and hit enter it will move to the parent directory. In 4.3 that has been extended to the completer so you can now type ../foo/bar and the completer will understand the relative path.



© 2008 Nokia Corporation and/or its subsidiaries. Nokia, Qt and their respective logos are trademarks of Nokia Corporation in Finland and/or other countries worldwide.
All other trademarks are property of their respective owners.