QUndoStack versus Kate’s Undo System

I’m currently using QUndoStack in a project of mine. A QUndoStack contains a list of QUndoCommands which then can be executed with QUndoStack::undo() and QUndoStack:.redo().

Now I thought when deleting an instance of the class Node, I can just hock the creation of the respective UndoDeleteNode class (derived from QUndoCommand) into the destructor, like this:

class Node {
public:
  // ...
  ~Node() {
    undoStack()->push(new UndoDeleteNode(this));
  }
};

class UndoDeleteNode : public QUndoCommand {
public:
  // ...
  void undo() {
    // create node again
  }
  void redo() {
    // delete node again
  }
};

Now the problem with this code is, that executing UndoDeleteNode::redo() will delete the node through the undo/redo system. In this case, the destructor ~Node() will create another UndoDeleteNode item. So what I was looking for a way to only create the undo item when the undo/redo system is not active, i.e.:

class Node {
public:
  // ...
  ~Node() {
    if (!undoStack()->undoOrRedoRunning()) {
      undoStack()->push(new UndoDeleteNode(this));
    } 
  }
};

I found QUndoStack::isActive(), but isActive() does something completely different. In fact, it looks as if there is no way to tell that the QUndoStack is currently operating. This finally also gets clear by reading the API docs of QUndoStack::push(QUndoCommand* cmd):

[...] Executes cmd by calling its redo() function. [...]

In other words, each QUndoCommand you push on the stack is executed immediately. This obviously is a design decision: Following this paradigm, you should not just call “delete node;” Instead, you should simply create a UndoDeleteNode() instance and push it to the stack. The undo command is then immediately executed by calling redo().

This design has the advantage that developers are forced to go this route. Following this paradigm, you very easily get macro support, since all actions are undo/redo items. This is cool.

However, what I dislike about this approach is the fact that it seems you loose the nice API to simply delete the node. You cannot write “delete node;”. Instead, you have to have some helper class such as a Document, that provides a Document::deleteNode(Node* node) function. This function then just creates a new UndoDeleteNode and pushes it to the stack.

Is this how the QUndoStack is supposed to be used? Does it imply that the destructor should be protected, and UndoDeleteNode must be a friend class of Node, to avoid other classes from just calling delete Node?

In Kate, we indeed go the other way: We have a KateUndoManager::isActive(), which indicates whether the edit actions should be tracked by the undo manager or not…

I’m not yet convinced that the approach of QUndoStack is ultimately cool. To me it looks like I’m forced into some design decision I didn’t want to take. Maybe I’m missing something?

Akademy 2013

So Akademy 2013 is nearing and this time it takes place at Bilbao, Spain. As it happens, Bilbao is located quite near to the beach as well as very close to the mountains. So I stumbled over a page with several hiking trails starting in Bilbao ranging from easy to hard. For instance, this route starts in a nearby village and then goes from about 65 meters in height up to 1000 (this is the point where you stop to hack on KDE, of course), and then down to 30 in Bilbao again. Anyone? :-)

PS: Maybe there are any locals who know the place around and can recommend a hiking track?

 

How to convert pdf to svg

In a project I’m currently working on I need to display the result of TeX code. So I thought it would be nice and accurate to compile the TeX code to produce a pdf, and then use libpoppler-qt4 to draw the pdf. This works in principal, but there is a licensing problem: libpoppler is GPL, and I want to use it in a LGPL library.

So I searched the net and found dvipng, which converts a dvi file to png. It even supports transparent backgrounds. So I could convert the dvi file to png through QProcess+dvipng, and then display the png file. This works, but whenever you scale the canvas the result looks ugly since png is not a vector graphic.

Next, I found pdf2svg that converts a pdf file into the scalable vector graphics format svg. In theory, I then can use QSvgRenderer to load and render the SVG on-the-fly using QSvgRenderer::render(QPainter*, …). So I first tested to convert the pdf to svg with this tool and then view it in InkScape. The result was perfect: InkScape renders the svg file exactly like Okular renders the pdf file:

So I looked into the source code of pdf2svg and saw that it uses libpoppler with Cairo. This reflects an additional dependency on cairo, so I went ahead and converted the cairo based code to Qt, using libpopper-qt4. The code basically boils down to:

// create poppler pdf document
Poppler::Document *document = Poppler::Document::load(args[1]);
document->setRenderBackend(Poppler::Document::ArthurBackend);
document->setRenderHint(Poppler::Document::Antialiasing, true);
document->setRenderHint(Poppler::Document::TextAntialiasing, true);
document->setRenderHint(Poppler::Document::TextHinting, true);
document->setRenderHint(Poppler::Document::TextSlightHinting, true);
document->setPaperColor(Qt::transparent);
// prepare QSvgGenerator as QPaintDevice
QSvgGenerator svgGenerator;
svgGenerator.setResolution(72); // resolution in dpi
svgGenerator.setFileName("out.svg");
svgGenerator.setSize(document->page(0)->pageSize());

// perform painting
QPainter painter(&svgGenerator);
document->page(0)->renderToPainter(&painter);
painter.end();

This code does indeed generate the svg code for the pdf file. However, the result is surprisingly ugly and wrong:

First, the pen seems too thick, and second the character ‘d’ is wrong on the left. I’ve tried changing the resolution (setResolution()) and also the page size (setSize()) of QSvgGenerator, always getting the same result. Searching the net reveals that QSvgGenerator seems to have quite some glitches with respect to WYSIWYG rendering. I tried to use QSvgGenerator before in 2009 and came out with unusable results. Maybe I’m missing something?

Update 1: It’s not QSvgGenerator that is to blame here. It’s indeed the Arthur backend of libpoppler. I send a patch to the poppler developers that fixes the issue with too bold glyphs. The pahts are still too inaccurate, though.

Update2: Using dvisvgm with the option –no-fonts results in an SVG file that QSvgRenderer renders correctly. So this is the current solution to get a TeX document rendered via SVG in Qt.

A rich python console and more in Kate editor

I have done some improvements in the plugins: python_console_ipython, python_autocomplete, python_utils, js_utils, xml_pretty and django_utils. These plugins I added a month and a half ago (except python_console_ipython) to the kate repository. I have done two improvements and a new feature:

  • Now they work with Python2 and Python3 (except python_autocomplete, this only works with Python2, due pysmell dependence)
  • Now they have a configuration page (except python_autocomplete, this should not have configuration parameters)

Page Config Plugins

The new feature is the integration of the python_autocomplete and python_console_ipython plugins with the project plugin. The behaviour of these plugins depends of the loaded project. That is to say, the python_autocomplete plugin autocompletes with our project modules. Currently the kate community is working to add a new python autocomplete  using jedi (this will work with Python2 and Python3).

Python Autocomplete (with our modules)

And the python_console_ipython plugin has something that we can name the project console. A ipython shell with the python path of our project and with the environments variables that the project plugin needs.

IPython Console (converted in a django shell)

To use this we need to add in the project file (.kateproject) a new attribute named “python”, with this structure:

 {
  "name": "MyProject",
  "files": [ { "git": 1 } ],
  "python": {
        "extraPath": ["/python/path/1",
                      "/python/path/2",
                      "/python/path/n"
        ],
        "environs": {"key": "value"},
        "version": "3.2"
    }
 }

I am a django developer, as we say in Django we can come to have a django shell (python manage.py shell) integrated in our editor. But this feature is a generic feature not tied to Django. This feature can be used by other Python developers. I have added only a specific feature to the django developers. If we set in the project file (.kateproject) the attribute projectType with the value Django, the ipython shell automatically loads the models and the settings like a shell_plus does.

  {
   "name": "MyProject",
   "files": [ { "hg": 1 } ],
   "python": {
        "extraPath": ["/python/path/1",
                      "/python/path/2",
                      "/python/path/n"
        ],
        "environs": {"DJANGO_SETTINGS_MODULE": "myproject.settings"},
        "version": "2.7.3",
        "projectType": "django"
    }
  }

I hope you like it :-)