ChangeListener Class Implementation
In the constructor we first initialize the QLabel so it scales images to fit its dimensions, and so it has a context menu bar. The context menu will only have the help entry which is automatically added so there's no need to do more than initialize the menu.
ChangeListener::ChangeListener( QWidget *parent, Qt::WindowFlags flags )
: QLabel( parent, flags )
, nextIndex( 0 )
, lastContentId( QContent::InvalidId )
{
setScaledContents( true );
QSoftMenuBar::menuFor( this );
Next we'll construct a list of the images we're going to display, we use resource paths here so we don't have to worry about where the application is installed. QFileInfo::absoluteFilePath() will resolve to the actual file path so we won't actually be using the resource path when installing to the document system.
imageFiles.append( QFileInfo( ":image/Bubble.png" ) );
imageFiles.append( QFileInfo( ":image/Clouds.png" ) );
imageFiles.append( QFileInfo( ":image/Splatters.png" ) );
imageFiles.append( QFileInfo( ":image/Water.png" ) );
When we install content we'll assign it a category, before we can do that we need to ensure the category exists. We'll name our category Change Listener and put it in the Examples scope and we'll let QCategoryManager assign it an ID. We could alternatively make this a system category and assign our own ID, in which case we'd use QCategoryManager::ensureSystemCategory() here.
QCategoryManager categoryManager( "Examples" );
categoryId = categoryManager.idForLabel( "Change Listener" );
if( categoryId.isEmpty() )
categoryId = categoryManager.add( "Change Listener" );
Now we'll construct the QContentSet we'll use to listen for newly added content and connect to its changed() signal. We filter it on our Change Listener category so it only contains content we added in this application. The QContentSet should be empty at this point, if it's not then the application did not exit cleanly when it was last run and we'll need to uninstall the images it failed to clean up.
QContentSet *contentSet = new QContentSet( QContentFilter::category( categoryId ), this );
connect( contentSet, SIGNAL(changed(QContentIdList,QContent::ChangeType)),
this, SLOT(changed(QContentIdList,QContent::ChangeType)) );
for ( int i = 0; i < contentSet->count(); i++ ) {
QContent::uninstall( contentSet->contentId( i ) );
}
Finally we'll construct a QTimer and set it trigger the timeout() slot every 3 seconds.
QTimer *timer = new QTimer( this );
connect( timer, SIGNAL(timeout()), this, SLOT(timeout()) );
timer->start( 3000 );
}
The ChangeListener destructor simply uninstalls the last image installed to the document system so no content records for the images are persisted after exiting the application.
ChangeListener::~ChangeListener()
{
if( lastContentId != QContent::InvalidId )
QContent::uninstall( lastContentId );
}
The timeout() slot periodically creates a new QContent for an image and commits it to the document system. We get the name, and the absolute file path from the QFileInfo, and all of the images are of the image/png type. We assign the QContent::Data role instead of the usual QContent::Document so that the images don't appear in any of the document selectors, and assign the Change Listener category to identify it later.
void ChangeListener::timeout()
{
QFileInfo fileInfo = imageFiles.at( nextIndex );
QContent image;
image.setName( fileInfo.baseName() );
image.setFile( fileInfo.absoluteFilePath() );
image.setType( "image/png" );
image.setRole( QContent::Data );
image.setCategories( QStringList( categoryId ) );
If the image QContent is successfully committed we'll uninstall the QContent for the previous image, and record the ID of the current image.
if ( image.commit() ) {
if ( lastContentId != QContent::InvalidId ) {
QContent::uninstall( lastContentId );
}
lastContentId = image.id();
} else {
qWarning("Could not commit the new content object!! Document generator exits.");
}
nextIndex = (nextIndex + 1) % imageFiles.count();
}
The changed() slot is called whenever a change is made to content in the document system. Here we use it to identify when a QContent in the Change Listener category has been added.
void ChangeListener::changed(const QContentIdList &idList,QContent::ChangeType changeType)
{
if ( changeType == QContent::Added ) {
foreach ( QContentId id, idList ) {
QContent content( id );
if ( content.isValid() && content.categories().contains( categoryId ) ) {
Once a new QContent has been identified we want to set its name as the window title and display the image in the body of the window. To display the image we open the QContent in read-only mode using QContent::open() and load the data from the returned QIODevice into the image. The caller takes ownership of the QIODevice returned by QContent::open() so we'll need to delete it once we've closed it.
setWindowTitle( content.name() );
QIODevice *ioDevice = content.open( QIODevice::ReadOnly );
if ( ioDevice ) {
QImage image;
image.load( ioDevice,"PNG" );
setPixmap( QPixmap::fromImage( image ) );
ioDevice->close();
delete ioDevice;
We only expect a single QContent in a notfication so once we've handled one we can abort checking the rest of the content IDs.
break;
}
}
}
}
Building the Change Listener application.
To install and run the Change Listener application, carry out the following steps.
- Create a new directory (e.g. $HOME/src/changelistener) and copy all the example files to that directory.
mkdir $HOME/src/changelistener
cd $HOME/src/changelistener
cp -r <Qt-Extended-source-directory>/examples/content/changelistener/* .
chmod +w *
- Build the new application.
export QPEDIR=<Qt-Extended-build-directory>
$QPEDIR/bin/qbuild
$QPEDIR/bin/qbuild image
- Run Qt Extended.
$QPEDIR/bin/runqtopia
- Go into the list of Applications and scroll down until you find the Change Listener application.
When you run the Change Listener application, you should see a blank screen which updates with a new picture display every three seconds.