Showing posts with label qt. Show all posts
Showing posts with label qt. Show all posts

Sunday, October 27, 2013

Qt4: Cannot mix incompatible Qt library


This problem occurs every now and then when using closed-source Qt4 binaries or libraries:

bash$ LD_LIBRARY_PATH=./lib bin/TestBench
Cannot mix incompatible Qt library (version 0x40801) with this library (version 0x40803)
Aborted (core dumped)

Setting LD_LIBRARY_PATH in order to override the Qt4 library never works, even though it should.


It turns out that this error has nothing to do with the proprietary software being linked to an incompatible Qt version. Instead, the user's Qt theme (often Oxygen) is incompatible with the Qt libraries shipped with the proprietary software.


This can be solved in two ways. The permanent way is to run qtconfig-qt4 and choose another theme (e.g. Cleanlooks, which always seems to work).

The second is to pass a compatible theme to the proprietary software using the -style command-line argument:


 LD_LIBRARY_PATH=./lib bin/TestBench -style=CleanLooks

This will override the theme only for this invocation of the application.


Wednesday, November 9, 2011

JRuby and RubyGems

For reasons too tangential to cover here, it was decided to port a Ruby 1.8.7 project to JRuby (>= 1.6.0). This worked well enough, except for the fatal flaw in JRuby: using gems.

To begin with, getting *any* gem with native extensions to build in JRuby requires some manual intervention.

The reason? The RVM JRuby has its Ruby include files under cext/src/include/, instead of lib/native/include like the other RVM Rubies. This appears to be a known issue.

A symlink handily solves the problem:

bash$ cd /usr/local/rvm/rubies/jruby-head
bash$ ln -s cext/src/include lib/native/include

Making this change will allow most gems with native extensions (e.q. pg, sqlite
3, etc) to be built.


The qtbindings gem, however, still fails with an error like the following:

bash$ sudo rvm jruby-head do gem install qtbindings                                
CMake Error at /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:91 (MESSAGE):
  Could NOT find Ruby (missing: RUBY_INCLUDE_DIR)
Call Stack (most recent call first):
  /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:252 (_FPHSA_FAILURE_MESSAGE)
  cmake/modules/FindRuby.cmake:249 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
  CMakeLists.txt:18 (FIND_PACKAGE)

-- Configuring incomplete, errors occurred!
make: [build] Error 1 (ignored)
cd ext/build; make
make[1]: Entering directory `/usr/local/rvm/gems/jruby-head/gems/qtbindings-4.6.3.4/ext/build'
make[1]: *** No targets specified and no makefile found.  Stop.
make[1]: Leaving directory `/usr/local/rvm/gems/jruby-head/gems/qtbindings-4.6.3.4/ext/build'
make: *** [build] Error 2

Unpacking the gem, fixing the extconf.rb to properly find JRuby, and repacking the gem gets past these problems, but ultimately fails when compiling the actual C extension code for Qt4.

The reason? Qt4 uses heavy inspection of the stack frame in order to implement its signal/slot mechanisms, and JRuby does not provide access to this information. Until equivalent functions for rb_frame_callee() and the like are provided by JRuby, Qt4 will remain incompatible with it.


Postscript: For the curious, the following lines (or variants thereof) can be added to extconf.rb to fix the Cmake build issues:

    file.puts "-DRUBY_EXECUTABLE=/usr/local/rvm/rubies/jruby-head/bin/jruby \\"
    file.puts "-DRUBY_LIBRARY=/usr/local/rvm/rubies/jruby-head/lib/native/x86_64-Linux/libjruby-cext.so \\"
    file.puts "-DRUBY_LIB_PATH=/usr/local/rvm/rubies/jruby-head/lib/native/x86_64-Linux \\"
    file.puts "-DRUBY_INCLUDE_DIR=/usr/local/rvm/rubies/jruby-head/cext/src/include/ruby \\"
    file.puts "-DRUBY_CONFIG_INCLUDE_DIR=/usr/local/rvm/rubies/jruby-head/cext/src/include/ruby \\"
    file.puts "-DCMAKE_INSTALL_PREFIX=/usr/local/rvm/rubies/jruby-head \\"

The lines go in the 'else' block of the 'if windows' condition, where the other cmake options are defined (around line 216, or search for '-DENABLE_SMOKE').

Monday, September 26, 2011

Qt4 and Ruby1.9.x

After a long period with no Qt4 support for Ruby 1.9, the qtbindings gem has stepped in to fill the void. This project packages the qtruby4-qtruby library from the korundum project, so the API is unchanged.

Installation is straightforward:

sudo gem install qtbindings

or for rvm users:

sudo rvm gem install qtbindings

Compatible with 1.8 as well.

Thursday, March 3, 2011

Ruby, Qt4, and AbstractItemModel

There are no good examples of using QAbstractItemModel in Ruby. For most purposes, QStandardItemModel will suffice. In this particular case, which calls for a lazily-loaded tree, QStandardItemModel will not cut it.

What follows is a simple implementation. The number of columns is limited to 1, items are read-only, there is no DnD support, and the model only supports the Display role for item data. Needless to say, these features are easy enough to implement, and would only distract from the example of subclassing Qt::AbstractItemModel.

The Model

=begin rdoc
A basic tree model. The contents of all tree nodes are determined by the ModelItems, not the Model, so they may be loaded lazily.
=end
class Model < Qt::AbstractItemModel
  signals 'dataChanged(const QModelIndex &, const QModelIndex &)'

=begin rdoc
The invisible root item in the tree.
=end
  attr_reader :root

  def initialize(parent=nil)
    super
    @root = nil
  end

=begin rdoc
Load data into Model. This just creates a few fake items as an example. A full implementation would create and fill the top-level items after creating.
Note: @root is created here in order to make clearing easy. A clear() method just needs to set root to ModelItem.new('').
=end
  def load()
    @root = ModelItem.new('',nil)
    ModelItem.new('First', @root)
    ModelItem.new('Second', @root)
  end

=begin rdoc
This treats an invalid index (returned by Qt::ModelIndex.new) as the index of @root.
All other indexes have the item itself stored in the 'internalPointer' field.

See AbstractItemModel#createIndex.
=end
  def itemFromIndex(index)
    return @root if not index.valid?
    index.internalPointer
  end
 
=begin rdoc
Return the index of the parent item for 'index'.
The key here is to treat the invalid index (returned by Qt::ModelIndex.new) as the index of @root. All other (valid) indexes are generated by AbstractItemModel#createIndex. Note that the item itself is passed as the third parameter (internalPointer) to createIndex.

See ModelItem#parent and ModelItem#childRow.
=end
  def index(row, column=0, parent=Qt::ModelIndex.new)
    item = itemFromIndex(parent)
    if item
      child = item.child(row)
      return createIndex(row, column, child) if child
    end
    Qt::ModelIndex.new
  end

=begin rdoc
Return the index of the parent item for 'index'.
This is made a bit complicated by the fact that the ModelIndex must be created by AbstractItemModel.

The parent of the parent is used to obtain the 'row' of the parent. If the parent is root, the invalid Modelndex is used as usual.
=end
  def parent(index)
    return Qt::ModelIndex.new if not index.valid?

    item = itemFromIndex(index)
    parent = item.parent
    return Qt::ModelIndex.new if parent == @root

    pparent = parent.parent
    return Qt::ModelIndex.new if not pparent

    createIndex(pparent.childRow(parent), 0, parent)
  end

=begin rdoc
Return data for ModelItem. This only handles the case where Display Data (the text in the Tree) is requested.
=end
  def data(index, role)
    return Qt::Variant.new if (not index.valid?) or role != Qt::DisplayRole
    item = itemFromIndex(index)
    item ? item.data : Qt::Variant.new
  end

=begin rdoc
Set data in a ModelItem. This is just an example to show how the signal is emitted.
=end
  def data=(index, value, role)
    return false if (not index.valid?) or role != Qt::DisplayRole
    item = itemFromIndex(index)
    return false if not item
    item.data = value.to_s
    emit dataChanged(index, index)
    true
  end
    
  alias :setData :data=

=begin rdoc
Delegate rowCount to item.

See ModelItem#rowCount.
=end
  def rowCount(index)
    item = itemFromIndex(index)
    item ? item.rowCount : 0
  end

=begin rdoc
Only support 1 column
=end
  def columnCount(index)
    1
  end

=begin rdoc
All items can be enabled only.
=end
  def flags(index)
    Qt::ItemIsEnabled
  end

=begin rdoc
Don't supply any header data.
=end
  def headerData(section, orientation, role)
    Qt::Variant.new
  end
end

The ModelItem

=begin rdoc
An example of a ModelItem for use in the above Model. Note that it does not need to descend from QObject.

The ModelItem consists of a data member (the text displayed in the tree), a parent ModelItem, and an array of child ModelItems. This array corresponds directly to the Model 'rows' owned by this item.
=end
class ModelItem
  attr_accessor :data
  attr_accessor :parent
  attr_reader :children

  def initialize(data, parent=nil)
    @data = data
    @parent = parent
    @children = []
    parent.addChild(self) if parent
  end


=begin rdoc
Return the ModelItem at index 'row' in @children. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def child(row)
    @children[row]
  end


=begin rdoc
Return row of child that matches 'item'. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def childRow(item)
    @children.index(item)
  end


=begin rdoc
Return number of children. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.
=end
  def rowCount
    @children.size
  end


=begin rdoc
Used to determine if the item is expandible.
=end
  def hasChildren
    childCount > 0
  end


=begin rdoc
Add a child to this ModelItem. This puts the item into @children.
=end
  def addChild(item)
    item.parent=self
    @children << item
  end
end

This should serve as a basic implementation of an AbstractItemModel.

Realistically, ModelItem would be subclassed to represent different types of items in the data source, each of which would also (likely) be subclassed from ModelItem. This allows a browsable tree to be created for navigating data hierarchies.

Thursday, January 6, 2011

QScintilla and getSelection()

The recent Ubuntu upgrade and subsequent Ruby woes were caused, of course, by the desire to install libqscintilla-ruby, a package only available on 10.10 (and even then, only for 1.8).

For the most part, QScintilla works fine in Ruby... until one encounters methods like this:

void getCursorPosition(int *line, int *index) 

void getSelection(int *lineFrom, int *indexFrom, int *lineTo, int *indexTo)


The docs provide some hint as to the problem:

If there is a selection, *lineFrom is set to the line number in which the selection begins and *lineTo is set to the line number in which the selection ends. (They could be the same.) *indexFrom is set to the index at which the selection begins within *lineFrom, and *indexTo is set to the index at which the selection ends within *lineTo. If there is no selection, *lineFrom, *indexFrom, *lineTo and *indexTo are all set to -1.

How does one pass an integer by reference in Ruby?

The answer: one doesn't. These functions take integer arguments and return nil, making them entirely useless in Ruby.

The Python guys did it right:

line_fro, idx_fro, line_to, idx_to = getSelection

The Ruby guys, of course, were lazy, and routed all calls directly to libqscintilla.so regardless of the sanity of their argument lists.


There is a way to make things work, however, thanks to ScintillaBase.


This, the base class of the Scintilla widget, provides the following method:

long SendScintilla(unsigned int msg, unsigned long wParam=0, long lParam=0)

At the top of  the base class documentation are a bunch of enums that look promising:

...
 SCI_SETSELECTIONSTART = 2142,   
 SCI_GETSELECTIONSTART = 2143,
 SCI_SETSELECTIONEND = 2144,  
 SCI_GETSELECTIONEND = 2145, 
...


Sure enough, these turn out to be the values for the msg parameter.


It makes for short work to add the following methods to the Scintilla object:


def get_sel_start
    # SCI_GETSELECTIONSTART
    self.SendScintilla(2143), 0, 0)
end

def get_sel_end
   # SCI_GETSELECTIONEND
    self.SendScintilla(2145), 0, 0)
end

def get_current_pos
    # SCI_GETCURRENTPOS
    self.SendScintilla(2008), 0, 0)
end


Yes, the messages have to be passed by number, as the symbols for the bulk of the messages have not been defined as constants in Ruby (lazy! bad! lazy!), as can be determined by examining Qsci::ScintilaBase.constants.

Tuesday, September 8, 2009

Doxygen gotchas

Finally took an afternoon to learn doxygen and have been commenting up the latest project for the past 24 or so hours.

Doxygen is easy to get up to speed with, but there are a couple of gotchas that aren't made clear in the documentation. These are listed below in no particular order.


Standalone pages

This is the first thing one notices when mucking about with doxygen: how can the index.html (aka "Main Page") be modified?

The answer is just as quick to find: create a document with a \mainpage tag. But what document? Surely adding a header file just to create doxygen tags is a bit silly?

Indeed. Instead, create a directory (e.g. doc) and use it for standalone doxygen files. A standalone file is basically a text file consisting of only the C++ comment, e.g.:

/*!
 \mainpage The Main Page
 \section sec_1 Section 1
 ...section 1 stuff...
 \section sec_2 Section 2
 ... section 2 stuff...

<HR>
<b>\ref todo%lt;/b>
*/


Name this something like 'main.dox', and add the .dox extension to the FILE_PATTERNS definition in the project Doxyfile:

FILE_PATTERNS += *.dox

These standalone doxygen files are incredibly useful for stuff like installation instructions, HowTos, and FAQs.

They are also useful for defining groups. A file groups.dox can contain group definitions like the following:

/*!                                                                         
\defgroup outer_group "Outer Group"
This module contains outer-group stuff.

\defgroup inner_group "Inner Group"
\ingroup outer_group
These things are in a group in a group.
*/


This provides a single, easy-to-maintain place for group definitions, so that header files and such just have to use "\ingroup". It seems like pretty good practice to put most files and classes in groups -- it makes for more expedient browsing.


Global namespace

Ok, there's a nice 'Namespaces' folder in the tree pane, and what does it contain? The project namespace. What about all those singletons ill-advisably implemented as globals (purely for narrative effect)?

Turns out there is no way to list the global namespace. This seems like a huge oversight -- if there's one thing you want to know about a project, it's how many lame globals the developers used.

Adding a 'globals' page seems like a good way to circumvent it, except for one slight problem -- if you create a standalone doc with "\page globals Global Namespace" in it, doxygen creates a page in the tree called "Global Namespace" ... with its own internal(?) version of the global namespace in it. This means, basically, that globals defined in your project are not there -- it only contains stuff like (for example) qRegisterMetaType invocations. It looks like 'globals' is an undocumented, and not particularly working, doxygen 'special command'.

A workaround is to use xrefitems. Add a line like the following to doxygen (remember, 'globals" is not an allowed name):

ALIASES                +=  "globalfn=\xrefitem global \"Functions\" \"Globals\""

Now, use code like the following to document your global function:

/*! \fn void GlobalSingletonFactoryMess( bool suck_less )
       \globalfn void GlobalSingletonFactoryMess( bool suck_less )
       \param suck_less : Make singletons suck less
*/
void GlobalSingletonFactoryMess( bool );

A page called 'Globals' will appear under 'Related Pages', and will contain the function prototype, with a link to its documentation in the appropriate .h file. Of course, it's called a 'Member', but one can't have everything.


Xrefitems

Speaking of xrefitems, see that second argument in the ALIASES line above? The one that's set to "Functions", and is supposedly the header for the section that contains the xref items?

Yeh, that argument does nothing. Doxygen ignores it. Go on, try it. Set it to "Doxygen, please format my hard drive". Or have it make fun of your boss or users. It makes no difference. That text will never appear.

Multiple namespaces in header files

This one is just plain wacky. Or rather, it's just plain lazy. Of the doxygen parser.

Let's say you have a header file where you declare an interface and an abstract base class implementing that interface (never mind why, it's an example):

namespace Mine {
class MyInterface {
public:
virtual ~MyInterface() {}
virtual void doStuff() = 0;
};
} /* close namespace */

/* interface must be registered in gobal namespace */
Q_DECLARE_INTERFACE( Mine::MyInterface, "com.me.Mine.MyInterface/1.0");

namespace Mine {
class MyClass : public QObject, public MyInterface {
Q_OBJECT
Q_INTERFACES(Mine::MyInterface)
public:
MyClass( QObject *parent=0 );
virtual void doStuff();
};
}


Guess what happens? MyClass never appears in the documentation. The second namespace block leaves the doxygen parsers as befuddled as ... your favorite befuddlement simile.

The solution of course is to put the interface class in its own header file, which is no big deal ... but really, you shouldn't *have* to.


Random \example tag links

OK, there's an example in docs/examples/, that dir is safely added to the Doxyfile EXAMPLE_PATH, and the file shows up where it's supposed to in the doc tree under Examples.

Looking at the class for which it is an example, though, you see nothing -- or maybe it gets linked to a few rather arbitrary methods, or to methods outside of the class altogether.

What's going on?

Well, it turns out that doxygen decides to out-clever you, and only put a link to the example file in elements that appear in the example. Thus, if your class never directly appears as a type in the file (e.g. 'PluginManager::plugin("MyPlugin").status()' appears instead of 'Plugin p(PluginManager::plugin("MyPlugin"); p.status()'), then the documentation for your class will not be linked to the example, no matter how close to the \class tag you put the \example tag. Doxy knows best, eh? Surely you have no idea where you want your examples linked from.

The fix is to rewrite the example so that the class/function/whatever appears clearly and distinctly.

Tuesday, August 11, 2009

Using qmake without a build target

One of the greatest weaknesses of Qt's qmake utility has been its over-reliance on templates. The available templates are standard stuff for Qt projects: app, lib, and subdirs (new with Qt4, I believe). These are all well and good when one is just slapping together a bunch of Qt code, but what about when interfacing with other build systems? A command template would be quite useful.

The follow project file is a workaround. The lib template is used (although app could be used as well) with empty HEADERS and SOURCES variables. The qmake variables that specify which build tools to use are set to a NOP (no-operation) tool such as echo or true. An "extra" target is created for the command to be executed, and set to be a pre-dependency of the main target.

Background: There is a config.py in . that has sip generate all C++ code, as well as the Makefile, in the directory 'sip'. The python module (sip_test.so) is output to the same directory. This project filed is invoked, along with many others, from a subdirs project file in the parent directory.


-----------------------------------------------------
# sip_test.pro
TEMPLATE = lib
TARGET = sip_test

# Check that sip exists
unix {
!system(sip -V > /dev/null) {
error( "SIP does not exist!" )
}
}

# sip build command and makefile targets
sip.commands = python ./config.py && cd sip && make
QMAKE_EXTRA_TARGETS += sip
PRE_TARGETDEPS = sip

# compiler toolchain override
unix {
# 'true' should exist on Unix and OS X. Win32 folk can
# use TYPE.EXE or something.
NOP_TOOL = true
}

QMAKE_CC = $$NOP_TOOL
QMAKE_CXX = $$NOP_TOOL
QMAKE_LINK = $$NOP_TOOL
QMAKE_LN_SHLIB = $$NOP_TOOL

# Copy the .so created by sip.commands to the current directory
QMAKE_POST_LINK = cp sip/$${TARGET}.so .
-----------------------------------------------------

With this project file, the standard "qmake && make" command works as expected.