Friday, 26 February 2010

Story of Screenshot module

I haven't wrote anything about Kadu code recently, because there was nothing exciting there during last months. Bugfixing and polishing everything (yes, we created best IM ever, but it still has bugs!). This is so boring I've decided I need to learn something new, like Qt's QGraphicsView. Kadu's screenshot module was ideal target for moving to this framework (next one - for 0.8 or 0.9 - will be our buddy list).

1. Old code

To port module required reading old (legacy) code. That was not hard - it was only one big file: screenshot.cpp. As you know I don't like big files and big classes. Fortunately, the code was split into 3 classes: ShotSizeHint (small widget that displays image size in pixels and kB), ScreenShotConfigurationUiHandler (class that handles configuration widgets in configuration window) and Screenshot (class that was doing eveything else).

One thing scared me: for doing one real screenshot this module could take 4 or more (instead of storing first one in QPixmap object).

2. New classes

Big splitting began (remember: one class, one purpose!). Each extracted class deserves its own file. Global variable were moved to classes as static fields, so initialization of module looks like this: screenshot_module.cpp. This is excellent example of small and clean file that is easily understandable.


Some part of old big Screenshot class was commented as code taken from KSnapshot from KDE. And it didn't use a single field from this class. Moving these methods to their own class and making them static was an easy step. This is how it looks now: pixmap-grabber.h.


This class is a small experiment. It is the only class in screenshot module that has access to config_file global variable. Other classes does not have to how configuration variables are stored and how to access them. This removed some duplication (like reading the same value in many places of code).


This class has only one purpose - save a QPixmap like a screenshot and return path to the file. It chooses path, format, creates needed directories and so on.


This class creates and manages toolbar buttons that you can add to each chat window. It also reacts on menu click by creating new Screenshot objects and delegating all the hard work to them.


This class is as simple as others. It creates ScreenshotTaker widget, waits for screenshot or 'cancel' signal. It can also create widget for cropping image and display it until image is cropped by user. Then it paste result into chat edit window (if not cancelled and contact's max image size is bigger than screenshot size).


This is possibly the most complicated class. It behaves differently depending on screenshot mode. In full-screen modes it is not even shown, it just takes screenshot by using PixmapGrabber static methods and return them to Screenshot class. In window mode it shows itself and allows to drag an icon to any window, then it takes screenshot of that window. I'm not sure if I did it right, maybe some code should be moved to ScreenshotWidget.


The only purpose of this widget is to allow user to select an rectangle of image and crop it. It also displays subwidget with image size (in pixels and kB) and 'Crop' button.


This widgets wraps around CropImageWidget and adds full-screen behaviors to it.


This widgets displays size of image and Crop button.

3. QGraphicsView

Using QGraphicsView allowed me to easily create editable crop rectangle with eight crop handlers.


This is QGraphicsItem subclass that draws four transparent black rectangles (shadow arount crop rectangle).


This is QGraphicsWidget subclass (it can receive mouse events) that draws small white rectangle with black outline. It can be dragged by mouse - that causes crop rect to change. All these items are controlled by CropImageWidget.

4. Summary

As you can see, I was able to split screenshot module to some small classes that can be described by one or two sentences by general. This is something that makes design good - anybody can look into any of these classes and understand them easily and change them. I would be very happy if all kadu modules would be so easy to understand.

No comments:

Post a Comment