Results 1 to 6 of 6

Thread: QMetaType dynamic object creation and initialization

  1. #1
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default QMetaType dynamic object creation and initialization

    I am implementing an XML-based format to save and restore QSettings - basically using QSettings::registerFormat() with my own ReadFunc() and WriteFunc() methods that use QXmlStreamReader and QXmlStreamWriter respectively.

    I have writing working just fine. I also have *most* of the reading part working as well. When I write a QSettings value out, I store the string representation of the value along with the string representation of the value's metatype. For example, the QColor "red" gets written to XML as the element:

    <Color value="#ff0000" type="QColor"/>

    When I read it back in, I retrieve the type name and use QMetaType::nameToType() to get an integer QMetaType type id value. I can then use QMetaType::create() with this type value.

    So, what I get is a void * pointer to some memory that actually contains a QColor instance. I don't actually know it is a QColor except indirectly through the type id.

    I could conceivably implement a gigantic switch() statement that checked the type id against every QMetaType::Type constant, and cast the result of QMetaType::create() appropriately, but this seems like a stupid way to do it. I'd have to include the header files for almost every fundamental type in Qt.

    It looks like the QMetaType::load() method might be a generic way that would work for this. If I used QMetaType::save() to write my values to strings that get saved to the XML file and QMetaType::load() to retrieve them from the strings that are read in, is that the way to do it?

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QMetaType dynamic object creation and initialization

    I think using load() just converts one problem to another one as you'd have to store binary (written by QDataStream) representations of your types instead of the nice xml ones. In my opinion you either need a "gigantic switch" to save and load a given meta type id from/to a given XML node or you can use inheritance to expose an interface for reading and writing such nodes and implement that interface for any meta type id you want to handle (which is basically what QMetaType::load() uses as well).
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  3. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QMetaType dynamic object creation and initialization

    Actually, after much head beating and hair pulling, I figured out how to do what I want:

    - use Q_DECLARE_METATYPE() for each custom class
    - implement the operator>>() and operator<<() QDataStream operators for the class
    - use qRegisterStreamOperatorsForMetaType() before attempting to serialize the class

    After doing this, you can then convert between QVariant and the custom class using QVariant::value<T>() and QVariant::fromValue<T>().

    Most of my head-banging was due to a stupid coding error. Instead of writing stream >> variant, I had written variant << stream, which of course gives all sorts of nonsense compilation errors.

    The essential code to serialize QSettings in XML boils down to this. I use the XML element <Settings> as the root document element for proper XML. Sorry for the formatting; cut and paste into CODE blocks does a terrible job when the copied code is indented with tabs.

    Qt Code:
    1. // readSettings() and writeSettings() are static members of a non-instantiable XMLSettings class, and are passed
    2. // in the call QSettings::registerFormat()
    3.  
    4. bool XMLSettings::readSettings( QIODevice & device, QSettings::SettingsMap & map )
    5. {
    6. bool bResult = false;
    7.  
    8.  
    9. QXmlStreamReader reader( &device );
    10. while ( !reader.atEnd() )
    11. {
    12. QXmlStreamReader::TokenType token = reader.readNext();
    13. switch( token )
    14. {
    15. case QXmlStreamReader::StartElement:
    16. if ( reader.name() != "Settings" )
    17. {
    18. QByteArray bytes( reader.name().toLatin1() );
    19. path.push_back( QString( bytes ) );
    20. QXmlStreamAttributes attributes = reader.attributes();
    21. if ( !attributes.isEmpty() )
    22. {
    23. QStringRef value = attributes.value( "value" );
    24. if ( !value.isEmpty() )
    25. {
    26. QByteArray valueBytes = QByteArray::fromBase64( value.toLatin1() );
    27. QDataStream stream( valueBytes );
    28. QVariant var;
    29. stream >> var;
    30.  
    31. // Adds an entry of the form Group1/Subgroup1/Subsubgroup1 = value
    32. map[ path.join( '/') ] = var;
    33. }
    34. }
    35. }
    36. break;
    37.  
    38. case QXmlStreamReader::EndElement:
    39. if ( !path.isEmpty() )
    40. path.removeLast();
    41. break;
    42.  
    43. default:
    44. break;
    45. }
    46. }
    47.  
    48. bResult = !map.isEmpty();
    49. return bResult;
    50. }
    51.  
    52. struct TreeNode
    53. {
    54. TreeNode( const QString & key = QString(), QVariant & value = QVariant(), TreeNode * pParent = 0 )
    55. : mKey( key )
    56. , mValue( value )
    57. , mpParent( pParent )
    58. {
    59. }
    60.  
    61. ~TreeNode()
    62. {
    63. QList<TreeNode *>::iterator it = mChildren.begin();
    64. QList<TreeNode *>::iterator eIt = mChildren.end();
    65. while( it != eIt )
    66. {
    67. delete *it++;
    68. }
    69. }
    70.  
    71. TreeNode * addChild( const QString & key, QVariant & value = QVariant() )
    72. {
    73. TreeNode * pChild = new TreeNode( key, value, this );
    74. mChildren.push_back( pChild );
    75.  
    76. return pChild;
    77. }
    78.  
    79. TreeNode * findChild( const QString & key ) const
    80. {
    81. TreeNode * pChild = 0;
    82. QList<TreeNode *>::const_iterator it = mChildren.begin();
    83. QList<TreeNode *>::const_iterator eIt = mChildren.end();
    84. while( it != eIt && !pChild )
    85. {
    86. TreeNode * pNode = *it++;
    87. if ( pNode->mKey == key )
    88. pChild = pNode;
    89. }
    90. return pChild;
    91. }
    92.  
    93. TreeNode * mpParent;
    94. QList< TreeNode * > mChildren;
    95.  
    96. QString mKey;
    97. QVariant mValue;
    98. };
    99.  
    100. static void writeTree( QXmlStreamWriter & writer, TreeNode * pNode )
    101. {
    102. if ( pNode )
    103. {
    104. writer.writeStartElement( pNode->mKey );
    105. if ( !pNode->mValue.isNull() )
    106. {
    107. QByteArray byteArray;
    108. QBuffer buffer( &byteArray );
    109. buffer.open( QIODevice::WriteOnly );
    110.  
    111. QDataStream valueStream( &buffer );
    112. valueStream << pNode->mValue;
    113. buffer.close();
    114.  
    115. QString value( byteArray.toBase64() );
    116. writer.writeAttribute( "value", value );
    117. writer.writeAttribute( "type", pNode->mValue.typeName() );
    118. }
    119.  
    120. QList<TreeNode *>::const_iterator it = pNode->mChildren.begin();
    121. QList<TreeNode *>::const_iterator eIt = pNode->mChildren.end();
    122. while( it != eIt )
    123. {
    124. writeTree( writer, *it++ );
    125. }
    126. writer.writeEndElement();
    127. }
    128. }
    129.  
    130. bool CSAQtXMLSettings::writeSettings( QIODevice & device, const QSettings::SettingsMap & map )
    131. {
    132. bool bResult = false;
    133.  
    134. TreeNode * pRootNode = new TreeNode( "Settings" );
    135.  
    136. QSettings::SettingsMap::const_iterator it = map.begin();
    137. QSettings::SettingsMap::const_iterator eIt = map.end();
    138. while ( it != eIt )
    139. {
    140. const QString & key = it.key();
    141. QVariant value = it.value();
    142.  
    143. qDebug() << "Key: " << key;
    144.  
    145. TreeNode * pTarget = pRootNode;
    146. TreeNode * pChild = 0;
    147. QString lastKey;
    148.  
    149. QStringList path = key.split( '/' );
    150.  
    151. QStringList::const_iterator lIt = path.begin();
    152. QStringList::const_iterator lEIt = path.end();
    153. while ( lIt != lEIt )
    154. {
    155. const QString & subPath = *lIt++;
    156. pChild = pTarget->findChild( subPath );
    157. if ( !pChild )
    158. {
    159. if ( lIt != lEIt )
    160. pTarget = pTarget->addChild( subPath );
    161. else
    162. lastKey = subPath;
    163. }
    164. else
    165. {
    166. pTarget = pChild;
    167. }
    168. }
    169.  
    170. pTarget->addChild( lastKey, value );
    171. it++;
    172. }
    173.  
    174. if ( pRootNode->mChildren.size() )
    175. {
    176. bResult = true;
    177.  
    178. QXmlStreamWriter writer( &device );
    179. writer.setAutoFormatting( true );
    180. writer.writeStartDocument();
    181. writeTree( writer, pRootNode );
    182. writer.writeEndDocument();
    183. }
    184.  
    185. delete pRootNode;
    186. return bResult;
    187. }
    To copy to clipboard, switch view to plain text mode 

    QSettings stores key / value pairs in the QSettingsMap. The hierarchy of groups within groups is accomplished by concatenating group-level keys using a "/". When writing the QSettings out to XML, the concatenated key is split into the group level parts, and each group name becomes an XML element in the XML hierarchy. Likewise, when reading in the XML hierarchy, the QStringList "path" is used as a stack to assemble the compound keys for each QSettings value.

    Note that the "type" attributes are unused when reading in the XML. Since the actual values are written out in base64, I use this for debugging purposes to ensure that the code is translating and creating the QVariant instances correctly when reading.

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QMetaType dynamic object creation and initialization

    Cool stuff!

    One question though: any reason for converting the string to bytes just to convert back again?
    Qt Code:
    1. QByteArray bytes( reader.name().toLatin1() );
    2. path.push_back( QString( bytes ) );
    To copy to clipboard, switch view to plain text mode 

    And this
    Qt Code:
    1. QList<TreeNode *>::iterator it = mChildren.begin();
    2. QList<TreeNode *>::iterator eIt = mChildren.end();
    3. while( it != eIt )
    4. {
    5. delete *it++;
    6. }
    To copy to clipboard, switch view to plain text mode 
    can probably be shortened like this
    Qt Code:
    1. qDeleteAll(mChildren);
    To copy to clipboard, switch view to plain text mode 

    Cheers,
    _

  5. #5
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QMetaType dynamic object creation and initialization

    Cool stuff!
    Thanks. Learned a lot about QVariant and QMetaType in the process. It is unfortunate that QSettings::SettingsMap is not accessible except in the read / write methods, because if it was then you could easily convert from one QSettings format to another simply by assigning the map. The only way to do it now is to iterate over QSettings::allKeys() and copy the keys and values from one QSettings instance to another.

    One question though: any reason for converting the string to bytes just to convert back again?
    No - that's probably junk code left over from when I was trying to get it all working. QXmlStreamReader returns QStringRef rather than QString for strings, so I should change this to:

    Qt Code:
    1. path.push_back( reader.name().toString() );
    To copy to clipboard, switch view to plain text mode 

    qDeleteAll(mChildren);
    Yeah, although I'm guessing that qDeleteAll() does the essentially same thing. I get shy about too much Qt-ness in my code; I generally stay away from Qt containers in favor of STL (for even more portability) and likewise prefer to use STL-style iteration over containers for the same reason. Not that this particular piece of code would likely ever be used outside of a Qt context, but I like to be habitual about my coding conventions.

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QMetaType dynamic object creation and initialization

    AFAIK qDeleteAll works with non-qt containers as well:

    Qt Code:
    1. template <typename ForwardIterator>
    2. Q_OUTOFLINE_TEMPLATE void qDeleteAll(ForwardIterator begin, ForwardIterator end)
    3. {
    4. while (begin != end) {
    5. delete *begin;
    6. ++begin;
    7. }
    8. }
    To copy to clipboard, switch view to plain text mode 
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


Similar Threads

  1. best object creation method
    By nuliknol in forum Qt Programming
    Replies: 2
    Last Post: 12th June 2014, 08:53
  2. QThread and New Object Creation
    By pratham_shah in forum Qt Programming
    Replies: 6
    Last Post: 26th April 2013, 11:11
  3. Cancelling an Object Creation
    By bbad68 in forum Newbie
    Replies: 1
    Last Post: 4th March 2010, 20:22
  4. Replies: 1
    Last Post: 2nd March 2009, 19:32
  5. Multiple form object creation
    By kpmsivachand in forum Qt Programming
    Replies: 2
    Last Post: 3rd February 2009, 01:09

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.