Sunday, October 7, 2007

KSignal - A Signal and Slot (Observer Design Pattern) implementation

Signal and slots is a concept developed from Qt. It is basically a generalized implementation of the Observer pattern. The purpose of the KjellKod signal-n-slot is to have the power of Observer pattern - but made with generic function callback. The most famous implementations of Signals and Slots are made by Qt and Boost. My own implementation of signals and slots (KSignal) were made in 2006-2007 when I wanted to learn more about C++ generic function callbacks. Now it's a fully functional library that is in use in multiple projects.


My own signal-n-slot definition
The KjellKod signal-slot mechanism is a C++, cross platform compatible implementation of the Observer design pattern. Signals are basically notifications of an (observable) event. Signals are connected to Slots which each store a function callback to an object. A signal can be connected to many slots and all slots/receivers are notified when the signal is emitted.

A signal can be just a notification, or it can pass along information. This make's it very handy when creating loosely coupled software systems.

The only requirement on an objects function callback that is to be stored within a slot is that it must be able to receive the same argument(s) as the publishing signal is sending. I.e. If it is a void signal, then the slot (stored callback function) must have a zero argument list. Likewise, if the signal sends out an argument, the receiving function must have that argument type, and only that argument in its argument list. If this requirement is not adhered to, the compiler will generate an error message. I.e. signal/slot is typesafe.

Modified below: 2010-08-12
Thanks to " andy_t_roo" (Andrew)  comment below which directed me to FastDelegate which in its turn made me look at GotW 83 where Herb Sutter gives an excellent (as always) example of how to encapsulate generic callbacks.

Back when I made KSignals version 1 (dynamic memory) and version 2 (static memory for embedded systems) I had not read GotW_83: Generic Callbacks  but I'm still kind of happy that I unbeknowst of much still managed to on my own come up with something very similar, although lacking some of the finer points Herb makes.

Either way I strongly recommend you to read Herbs Gotw article since it's very, very easy read and explains better than any other function callback or function pointer text that I've read how to setup the basics that are needed for Signals and Slots. If you think this is something good, then please go ahead and use my versions and tailor make them as you please (which should be easy since they're only few lines with bare bone code)


    Monday, February 12, 2007

    QTextEdit with drag-n-drop (cut-n-paste) of text images and links

    I am right now waiting to get security clearance for a project and while that is taking place I have some extra time on my hands to indulge in Qt and some widget programming.

    Step 1 in a 4-fun project was to make a simple Text Editor, preferably in Rich Text with embedded pictures or in second hand with html

    Using QTextEdit and following a Qt example it was easy to get started. The example is not fully functional so some minor glitches had to be fixed but mostly it was just great seeing how the Trolls had done it and then redo what needed to be done differently to suit my needs.

    Now one thing bugged me though. The QTextEdit comes already with drag-n-drop functionality (text, images etc) BUT it was not drag-cut-n-paste/drop that you would expect instead it was drag-copy-n-paste/drop. Do you understand what I mean here?

    Anyhow. By subclassing QTextEdit it was possible to solve this - even though I racked my brains and had too much trial and error for my taste before it worked out. The Trolls HAD some information about how to do it
    but not so much.

    I.e. in short you need to implement two functions of your QTextEdit subclass. I call my subclass simply TextEdit
    1.
    virtual void dragEnterEvent(QDragEnterEvent *e);
    In the implementation you simply put
    void TextEdit::dragEnterEvent(QDragEnterEvent *e)
    {
    e->acceptProposedAction();
    }
    acceptProposedAction() simply agrees to make the drop action the (in Qt) defined for this QDragEnterEvent.

    2. virtual void dropEvent(QDropEvent *e);
    This tells your subclass exactly what to do when reciving a QDropEvent what we are interested in is to do a cut-n-paste of the selected area into the newly chosen (dragged) position.
    In the implemantation (with lots of comments just for explaining :)

    NOTE (Feb 18:) first I wrote this function as in the bottom of this entry, but ended up with inconsistent cursor behaviour so with the help of J-P at QtCentre.org I ended up with a clean and simple solution for this problem. The whole discussion you can find at : QCentre QTextEdit::dropEvent discussion

    Working solution from J-P
    void TextEdit::dropEvent(QDropEvent *event)
    {
    QDropEvent drop(event->pos(),Qt::MoveAction, event->mimeData(), event->mouseButtons(), Qt::ShiftModifier, event->type());
    // Qt::ShiftModifier makes it into CUT instead of COPY

    QTextEdit::dropEvent(&drop); // Call the parent function w/ the modified event

    }


    My original and non-working solution
    void TextEdit::dropEvent(QDropEvent *event)
    {
    QTextCursor cursor = textCursor(); /*starting cursor */
    event->acceptProposedAction(); /*Accept the incoming event*/

    /*
    Cursor for the selected position (drag-n-drop position)*/
    QTextCursor insertionCursor = cursorForPosition(event->pos());


    if (event->dropAction() == Qt::MoveAction
    event->dropAction() == Qt::CopyAction)
    {
    /* Start atomic operation (undo/redo) */
    insertionCursor.beginEditBlock();

    /* Extract text before removing selected area */
    QTextDocumentFragment text = cursor.selection();

    /* Remove from original position */
    cursor.removeSelectedText();

    /* Insert dragged text/image/link */

    insertionCursor.insertFragment( text );

    /* End atomic operation (undo/redo) */
    insertionCursor.endEditBlock();

    /* The line below doesn't seem to work, Qt bug? */
    setTextCursor( insertionCursor );

    }
    }




    Friday, February 2, 2007

    Qt4 on Free IDE Visual Studio Express 2005 C++ with Qt code completion

    HowTo Setup Qt 4.2.2 With Visual C++ Express (Free) Edition

    With Qt Code Completion

    Note: An updated version of this is also published on KjellKod.cc

    1) Download and install Microsoft Visual C++ Express edition in the installation instructions they mention that you should ALSO install the SDK, don't forget to DO just that. Very Important: Don't forget to follow the instructions on that page. Since they will describe how to setup the SDK to work for this version of Visual Studio. NOTE that in these descriptions some files that may need to be deleted will be HIDDEN so using the search or 'un-hide' files might be good.

    NOTE: If you update Visual C++ express with SP1, then you must also apply some hotpatches, since bugs are introduced with the SP1. See http://support.microsoft.com/kb/930198 for more information

    2) Download Qt (if you haven't done it already) . Download also a patch for it, patch for 4.2.2 can be found here (not official?)

    3) Unpack Qt to a location that does NOT contain spaces. I.e. "C:\Program Files" is NOT a good choice. I used I:\lib\Qt\4.2.2

    4) Set System Environment Variables
    QTDIR variable as a system environment variable
    I.e. My Computer->Advanced->Environment Variables
    Variable name: QTDIR
    Variable value: I:\lib\Qt\4.2.2

    Similarly make another system environment variable QMAKESPEC to win32-msvc2005
    Note: If you want to use for example ming-gw you should do this one differently win32-msvc2005 is ONLY if you want to use the compiler & debugger that comes integrated with Visual Studio 2005 Express C++ (damn, that's a long name)

    Add also PATH to be the path to your Qt\bin directory i.e. in this case I:\lib\Qt\4.2.2\bin

    5) Now test that your system environment variables are working. Open up a prompt and type
    echo %QTDIR%
    echo %QMAKESPEC%
    Both times you should have gotten the corresponding value for that system environment variable.

    6) Unpack the Qt patch to where you installed Qt and open a cmd prompt and do cd %QTDIR%
    , run installpatch42.bat.

    7 a) Now it's time to setup the VC++ compiler and to compile Qt.
    First you need to patch the vsvars32.bat script that are found in "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools" . Copy it to a safe location in case you mess up.

    Add to the INCLUDE, LIB, and BIN lines according to following:
    PATH Original:
    @set PATH=C:\Program Files\Microsoft Visual Studio 8\Common7\IDE;C:\Program Files\Microsoft Visual Studio 8\VC\BIN;C:\Program Files\Microsoft Visual Studio 8\Common7\Tools;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\bin;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 8\VC\VCPackages;%PATH%

    PATH Changed to:
    @set PATH=C:\Program Files\Microsoft Visual Studio 8\Common7\IDE;C:\Program Files\Microsoft Visual Studio 8\VC\BIN;C:\Program Files\Microsoft Visual Studio 8\Common7\Tools;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\bin;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 8\VC\VCPackages;C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Bin;%PATH%

    INCLUDE Original:
    @set INCLUDE=C:\Program Files\Microsoft Visual Studio 8\VC\INCLUDE;%INCLUDE%

    Include Changed to:
    @set INCLUDE=C:\Program Files\Microsoft Visual Studio 8\VC\INCLUDE;C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include;%INCLUDE%

    LIB Original:
    @set LIB=C:\Program Files\Microsoft Visual Studio 8\VC\LIB;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\lib;%LIB%

    LIB Changed to:
    @set LIB=C:\Program Files\Microsoft Visual Studio 8\VC\LIB;C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\lib;C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib;%LIB%

    Run the vsvars32.bat script which sets up the system environment for the VC++ compiler.
    I.e. you are standing in %QTDIR% then you are running WITH the " signs: "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat"

    7 b) Run qconfigure.bat script as qconfigure.bat msvc2005. Answer Yes to all questions.

    7 c) Run nmake which will compile & "install" Qt. This seems more accurate though than only running qconfigure.bat - beware that it'll take some time since your whole Qt installation is compiled.

    8) Setup VC++ Environment Configuration

    8a) Include files that you need when including Qt into your source code
    Go to Tools->Options->Projects and Solutions->VC++ Directories->Include files
    Add the following either with the syntax of $(QTDIR)\... or with the full path:
    $(QTDIR)\include
    $(QTDIR)\include\Qt
    $(QTDIR)\include\QtCore
    $(QTDIR)\include\QtGui
    $(QTDIR)\include\QtNetwork
    $(QTDIR)\include\QtSvg
    $(QTDIR)\include\QtXml
    $(QTDIR)\include\Qt3Support
    $(QTDIR)\include\ActiveQt

    ... etc, for all the include directories that you might need


    8b) Library files that you need when including/building Qt & source code
    Go to Tools->Options->Projects and Solutions->VC++ Directories->Library files
    Add $(QTDIR)\lib

    8c ) Source files that you need to enable Intellisense to do Qt code completion
    Go to Tools->Options->Projects and Solutions->VC++ Directories->Source files
    Add $(QTDIR)\src

    If you had an old project than maybe you also need close down VC++ and then delete the IntelliSense file (*.ncb). Don't worry when you start the project again the file will be re-created.

    "Don't fix it unless it's broken"
    When I tried to compile my first project (completely unrelated, but it was QuickFix) I ran into some trouble due to that the wchar_t was defined in an incompatible way in the old libraries compared to Visual Studio 2005 Express. This could be rectified by setting up the QuickFix project so that the wchar_t should use the code definition rather than the VSC++ definition.
    To do this: Right-click on project (or sub-project), choose
    References->Configuration Properties->C/C++->Language->
    Treat wchar_t as a built-in type change from Yes to No

    "Create your first project - A simple Hello World in Qt"
    OK, now you're ready to try your first project. Since Qt already comes with a very easy and nice way to manage the make system we're going to use that.
    Creating a new Project as such:
    File->New Project->Visual C++ ->General->Makefile Project
    (set up Name, Location etc according to your liking)

    Create an very simple .pro file (these can also be autogenerated)
    This will be the basis for your Qt auto-updated Makefile later
    It can look something like this (whatever_project_name_you_like.pro)

    TEMPLATE += app
    CONFIG += qt warn_on
    SOURCES += main.cpp
    TARGET = whatever_project_name_you_like
    win32:debug:CONFIG += console

    Add a file also to the project:
    (Right click project -> Add new item -> C++ file (call it main.cpp)
    Make it look something like this:





    Now set up your environment to autogenerate Makefiles and compile according to your .pro file
    Configuration Properties -> NMake -> General
    Build Command Line qmake && nmake debug
    Rebuild Command Line qmake && nmake debug-clean
    Clean Command Line nmake debug-clean
    Output debug/projectname.exe (Just an example on the output)

    Try compiling it, it should work now

    Good Luck :-)

    -----------------------------------
    I moved my blog entry below here so that you folks wouldn't get side-tracked by seeing it first ;-)
    --------------------------------------
    For the last 5 years I have mostly worked with Linux - developing software using IDEs such as XEmacs and KDevelop.

    I think KDevelop is promising but still very buggy. I heard some rumour that they will come out with a cross-platform IDE soon. THAT would really be something.

    Anyhow, since I quit Park Air Systems in Norway in 2006 and started working for HiQ in Sweden I have slowly moved from Linux/Unix SW Engineering to now mostly on Windows. Since I still love to use Qt ( Trolltech )'s libraries I wanted a way to integrate them on the IDE's that I can use on Windows.

    I have tried QDevelop and Edyuk for my own project (KjellKod) at home but someone at work said that now Visual Studio comes as a FREE (for Open Source only?) IDE version. I've tried it at work and at home and after MINOR problems got it up and running AND being able to use with their Intellisense code completion some third-part code like Qt 4.2.*

    I WISH developers for other IDEs would look at Visual Studio Express 2005. It is CLEAN it is EASY to configure AND it does not CRASH (hint to KDevelop poeple)...

    Without wanting to start a Flame War I thought I would post how to set it up and what I did to fix some minor quirks that I found. Mind you that the ORIGINAL articles that I followed for a starter were "Building QT 4 with Visual C++ 2005" and "Compiling Qt4 on Windows" I have just added some little tidbits that I thought were missing ... (now WHY haven't they set up their pages to recive feedback?)


    KjellKod Goes Blogging

    First Blog EVER today. I think I will keep it short & simple.
    1st consultant job interview today for some time - it sounds like it could be a match. It's about radar technology and they need a C++ Software Engineer, we'll see what happens

    22pm: I got thrown off at the interview a little. My rhythm got messed up... But I still think I made a good impression. I will know by Monday - who knows, maybe I'll be working for SAAB for some months?