tag:blogger.com,1999:blog-80655954361408117892024-03-16T00:08:24.289-07:00Daniel's Computer BlogOn computer science, software architecture, user-interface design, hardware, video-games and other random thoughts.Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-8065595436140811789.post-86065539889120170242009-12-24T01:01:00.000-08:002009-12-24T02:11:12.448-08:00Qt-widget to visually select the corner of a screen (for popup-windows)<p>I recently had to create a popup-window that displays active tasks for a user. Because I am frequently annoyed by popups showing at exactly the wrong monitor or position, I included a feature to change the monitor and that monitor's corner that will be used as the popup-position. I'm using QDesktopWidget to find out the available monitors and calculate the correct position for the popup. In the first version of this popup-window, the user had to select the position and monitor using a standard menu. Because that's not very intuitive, I decided to create a widget that displays the available screens in a miniature-preview. The corners of the screens are highlighted and can be selected with the mouse. When the mouse hovers a corner of the preview, a small popup-like window is displayed at that corner's position of the real monitor.</p><br /><p>Here are two screenshots:</p><br /><p><img alt="Screenshot Popup Position Selector on Windows" src="http://viming.de/perm/PopupPositionSelectorWin.png"/></p><br /><p><img alt="Screenshot Popup Position Selector on Ubuntu Linux" src="http://viming.de/perm/PopupPositionSelectorLinux.png"/></p><br /><p>You can view and download the source in my <a href="http://github.com/daniel-kun/popuppositionselector">github-repository</a> and you can download a sample-application for Windows <a href="http://viming.de/perm/PopupPositionSelector.zip">here</a>.Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com5tag:blogger.com,1999:blog-8065595436140811789.post-88680507381706017192009-10-04T15:05:00.000-07:002009-10-04T15:31:32.350-07:00Shrinky Pre-Release<p>I had this idea in mind for a long time now. People are often asking me how they can resize images to a smaller file-size, most often because they want to send them via e-mail and don't want to wait 30 minutes for the e-mail to be sent. You can use Microsoft Paint which ships with every Windows for this and it's actually relatively easy to explain how. But most people can't remember the procedure and are lost when they have to do this the next time. So I came up with the idea to write an <b>extremely</b> easy application that can do this and is optimized for high usability and an easy interface. And here's my shot at it: Shrinky (doesn't that sound cute?).</p><br /><p><img src="http://viming.de/perm/ShrinkyPreReleaseAnnouncement.png" alt="Shrinky pre-release announcement"/></p><br /><p>The only thing you have to do when loading Shrinky is clicking on the logo above, select the image-file you want to shrink and then hit "Shrink it!". This will create a file that's about 150-200kb in size, which is quite handy for uploading and sending via e-mail. A new file with the same file-name as the original, with a "-small" appended, will be created. You can tell Shrinky how to resize the image by clicking on "I want to decide myself".</p><br /><p><img src="http://viming.de/perm/ShrinkyPreReleaseAnnouncementSettings.png" alt="Shrinky settings"/></p><br /><p>You can shrink images in three different ways:<br /><ul><li>First, you can enter a file-size you want to have. This can be anything from "100kb" to "1,5 mb" (this is currently the only thing that does not work reliably, because floating-point values are not handled correctly). Shrinky will then try to create a file that matches this file-size as good as possible.</li><br /><li>The second way to shrink a file is to enter the width and height directly. This can become handy when resizing an image so it fits in a certain space, such as on a webpage or something.</li><br /><li>And the third way is by entering a percentage of the original resolution. Good for situations where you want to, for example, make an image half as big as it currently is.</li></ul><br />When you are done setting up Shrinky the way you want, just hit "Shrink it".</p><br /><p>Shrinky is completely written in C++ with Qt and is platform-independent. But obviously, since it's aimed at pc-beginners, the most important platform is Windows. That's why I've created binaries for Windows only (I don't have access to a Mac, anyways). See below for a download-link. You can access the source-code via the <a href="http://github.com/daniel-kun/Shrinky">public git repository on github</a>. Shrinky's currently translated to English and German only. Japanese will surely follow and some nice folks in #qt on freenode already offered translations, too. Man, open source is really nice. ;-)</p><br /><p>There's still much to do to make Shrinky a polished product and publicly release it. Here's my todo-list for this project:<br /><ul><li>Shell-integration for Windows. Easily shrink images via right-click. That's especially important in the file-selection dialogs. With a tight integration, you can easily shrink images while selecting them in your e-mail application.</li><br /><li>I need to write an installer for Windows, because extracting a ZIP-archive is not that easy for the target audience I am aiming at.</li><br /><li>Processing of multiple files would be neat, too.</li><br /><li>A web-page including a donation-facilty via paypal. Albeit being open-source, I'd like to make a few bucks with this application.</li><br /></ul><br />You can download the pre-release of Shrinky <a href="http://viming.de/perm/ShrinkyPreRelease.zip">here</a>. Just extract the zip-archive anywhere on your hard-drive (or usb-stick, for that matter) and run Shrinky.exe. You can post anything regarding your experience with Shrinky as a comment on this blog-post!</p>Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com1tag:blogger.com,1999:blog-8065595436140811789.post-10530238068708239372009-08-13T00:21:00.000-07:002009-08-13T07:45:48.542-07:00Shrinking Qt widgets to minimum needed size<p>Shrinking a Qt widgets to the minimum size they can become because of size constraints or child widgets is not a difficult task. In fact, it's simply one call to resize(0, 0) that does just that. Because even resizes by the programmer are bound to the usual constraints, this resizes to the smallest possible size.</p><br /><p>But when trying this in practical situations, it might become tricky. Shrinking your widgets means it becomes smaller. A widget (or dialog, or window) usually becomes smaller after removing or changing stuff. You will notice, however, that after removing a widget, a call to resize(0, 0) will <b>not</b> resize your widget to the size it would occupy without the removed widget, but instead it behaves like the widget was still there. This is because the widget is not completely removed until the next processing of the message loop. There are two ways to make certain that the message loop is being processed. One is calling <i>QApplication::processEvents()</i>. The other is using a <i>QTimer::singleShot()</i> and doing the task you want to do in the provided slot. I've tried both in regards to shrinking the widget and noticed that using a <i>singleShot</i> is more flicker-free than calling <i>processEvents()</i>, at least on the Windows platform.</p><br /><p>I've created a demo-application to visualize the problem and check which method works better on a certain platform. Here are some screenshots:<br /><p><img alt="Initial window" src="http://viming.de/perm/WidgetResizeInitial.png"/><br />This is the window after opening the application. You can choose between directly shrinking the widget after removing the last button, using a <i>singleShot()</i> and using <i>processEvents()</i>.</p><br /><p><img alt="Added two buttons" src="http://viming.de/perm/WidgetResizeWithButtons.png"/><br />Two buttons have been added, the dialog has automatically grown.</p><br /><p><img alt="Removed button, shrinked directly" src="http://viming.de/perm/WidgetResizeDirectShrink.png"/><br />The last button has been removed, <i>resize(0, 0)</i> has been called directly after removing the widget. You can see that the size is not appropriate, the space the button took is still there. Notice the wider gaps between the widgets.</p><br /><p><img alt="Removed button, shrinked in singleShot" src="http://viming.de/perm/WidgetResizeTimedShrink.png"/><br />The last button has been removed, <i>resize(0, 0)</i> has been called in a slot that was activated by a <i>QTimer</i> using a <i>singleShot()</i>. The size of the window is correct, no flicker was visible.</p><br /><p><img alt="Removed button, called processEvents first" src="http://viming.de/perm/WidgetResizeProcessEvents.png"/><br />The last button has been removed, <i>QApplication::processEvents()</i> has been called before calling <i>resize(0, 0)</i>. The size of the window is correct, but there was flicker visible.</p><br /><p>You can download the source-code of the demo-appliction <a href="http://viming.de/perm/WidgetResize.zip">here</a> and try it out yourself.</p>Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com9tag:blogger.com,1999:blog-8065595436140811789.post-59858563740812982892009-07-16T05:16:00.000-07:002009-08-13T07:46:06.045-07:00Background animation for Qt-Widgets<p>Sometimes you want to load data from a specific datasource (e.g. a database or the internet), but let the user work on with your application while it is loading. When doing this, it is essential that the user can see that data is being loaded and immediately notice when loading has finished.<p><br /><p>In my situation it is a table view that gets populated with entries and then validates every entry, which can take quite some time. So I wanted to show a "loading" animation in the background of the table, since this is unintrusive, but still easily notable. I created an animation using <a href="http://www.ajaxload.info">ajaxload</a> and saved it as Load.gif in my application's directory.</p><br /><h3>Enabling GIF-support in Qt</h3><br /><p>The first hurdle you have to take is getting gif-support in your Qt installation. In order to do so, you need to pass -qt-gif to configure and have the qgif library (libqtgif.so or qtgif.dll) in the subdirectory imageformats in the Qt plug-in directory. I'm not getting into more detail here, since it's not the main topic of this post.</p><br /><h3>Drawing directly on your widget</h3><br /><p>Next you want to be able to draw directly on the background of your widget. You do this using the <i>paintEvent()</i> and a QPainter. Here's a very minimal example how to do it:</p><br />Header:<br /><pre>#include <QWidget><br /><br />class TsTestWidget: public QWidget<br />{<br /> Q_OBJECT<br />public:<br /> virtual void paintEvent(QPaintEvent *event);<br />};</pre><br />Implementation:<br /><pre>#include "Test.h"<br /><br />#include <QApplication><br />#include <QPainter><br /><br />void TsTestWidget::paintEvent(QPaintEvent *)<br />{<br /> QPainter painter(this);<br /> painter.drawRect(10, 10, 50, 50);<br />}<br /><br /><br />int main(int argc, char *argv[])<br />{<br /> QApplication app(argc, argv);<br /><br /> TsTestWidget test;<br /> test.resize(100, 100);<br /> test.show();<br /><br /> app.exec();<br />}</pre><br />Result:<br /><p><img src="http://viming.de/perm/background_anim/Step1.png"/></p><br /><h3>Animating the background</h3><br /><p>The second step is animating this gif. Qt has the <i>QMovie</i> class for this. QMovie is not a widget, it's just managing the animation's frames and timers and provides you the current frame's pixmap. It's <i>frameChanged(int)</i> signal is emitted whenever the next frame should be displayed, usually using a QLabel. In our case we don't use a QLabel to display it, but directly paint it on the widget as we did in the previous step. The tricky part is that paintEvent() is only called when an external event happend that requires repainting of the widget. So we use the frameChanged(int) signal and repaint the widget every time the current frame has changed. Here's a simple example:</p><br />Header:<br /><pre>#include <QWidget><br />#include <QMovie><br /><br />class TsTestWidget: public QWidget<br />{<br /> Q_OBJECT<br />public:<br /> TsTestWidget();<br /> virtual void paintEvent(QPaintEvent *event);<br />private:<br /> QMovie m_movie;<br />private slots:<br /> void paintNewFrame(int);<br />};</pre><br />Implementation:<br /><pre>#include "Test.h"<br /><br />#include <QApplication><br />#include <QPainter><br />#include <QPaintEvent><br /><br />TsTestWidget::TsTestWidget():<br /> m_movie(qApp->applicationDirPath() + "/Load.gif")<br />{<br /> connect(<br /> &m_movie,<br /> SIGNAL(frameChanged(int)),<br /> this,<br /> SLOT(paintNewFrame(int)));<br /> m_movie.start();<br />}<br /><br />void TsTestWidget::paintEvent(QPaintEvent *event)<br />{<br /> // First we extract the current frame<br /> QPixmap currentFrame = m_movie.currentPixmap();<br /><br /> QRect frameRect = currentFrame.rect();<br /><br /> // Only redraw when the frame is in the invalidated area<br /> frameRect.moveCenter(rect().center());<br /> if (frameRect.intersects(event->rect()))<br /> {<br /> QPainter painter(this);<br /> painter.drawPixmap(<br /> frameRect.left(), <br /> frameRect.top(), <br /> currentFrame);<br /> }<br />}<br /><br />void TsTestWidget::paintNewFrame(int)<br />{<br /> repaint();<br />}<br /><br />int main(int argc, char *argv[])<br />{<br /> QApplication app(argc, argv);<br /><br /> TsTestWidget test;<br /> test.resize(100, 100);<br /> test.show();<br /><br /> app.exec();<br />}</pre><br />Result:<br /><p><img src="http://viming.de/perm/background_anim/Step2.png"/></p><br /><h3>Item views are special</h3><br /><p>I first tried doing this on a <i>QTableView</i> and failed, frustrated. There is one tricky thing about <i>QAbstractItemView</i>s: You don't paint on the widgets, but on the <i>viewport()</i>, because they're derived from <i>QAbstractScrollArea</i>. Because I didn't immediately realize this, I always got a confusing error message when creating a QPainter in the paintEvent() using the <i>this</i>-pointer. That's because I need to create the painter for the <i>viewport()</i> widget. After finding this out, it became straightforward implementing the animation in the QTableView, just replacing this with viewport().</p><br /><br /><h3>More re-usable approach</h3><br /><p>So here we are, having our very own background animated widget, even supporting item views. I could stop this post right here and leave you alone with implementing it into your favorite widgets. But there' still one thing that bugs me: You have to do this for every widget you want an animated background in. When you're using QListView, QTableView and QTreeView in your application, you have to extend all three classes with this functionality, which is error-prone and, bluntly put, lame.</p><br /><p>So here's my solution how you can minimize the code you have to write for each new widget-class you want to integrate it into. We create a helper class called "TsBackgroundAnimation". This class does all the work and thanks to multiple inheritance, you just have to derive a class from your Widget and <i>TsBackgroundAnimation</i>, override paintEvent() and call TsBackgroundAnimation's <i>paintAnimation()</i>-function there. There's a a "workaround" we have to make here. Qt does not support deriving from multiple QObject-decendants, hence TsBackgroundAnimation may not derive from QObject. This means that TsBackgroundAnimation can not have slots and it's impossible for it to connect the frameChanged(int)-event to a slot that repaints the widget. So instead of QMovie a TsMovie class is used that provides a frameChanged()-event without the <i>int</i> parameter. That way TsBackgroundAnimation can connect that signal to the slot <i>repaint()</i> of the widget. This made the final implementation a bit larger, so I did not directly paste the sourcecode here. You can download the complete project from here:<p><br /><a href="http://viming.de/perm/background_anim/BackgroundAnimation.zip">BackgroundAnimation.zip</a><br/><br /><a href="http://viming.de/perm/background_anim/BackgroundAnimation.tar.bz2">BackgroundAnimation.tar.bz2</a><br/><br /><p>And here's a screenshot of the final result:<br/><br /><img src="http://viming.de/perm/background_anim/Step3.png"/></p>Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com3tag:blogger.com,1999:blog-8065595436140811789.post-77549495599848997682008-12-03T07:23:00.000-08:002009-08-13T07:48:24.540-07:00Embedding Qt Widgets into QtWebKit<p>Qt has it's awesome <a href="http://doc.trolltech.com/4.4/qtwebkit.html">built-in WebKit</a> support which makes it extremely easy to have full-featured browser/html-viewer capabilities in your application (including JavaScript!).</p><br /><p>It is possible to embedd any Qt Widget into your <a href="http://doc.trolltech.com/4.4/qwebpage.html">QWebPage</a>. The necessary steps for that are quite simple. You have to derive from QWebPage and overload the <a href="http://doc.trolltech.com/4.4/qwebpage.html#createPlugin">createPlugin()</a> function, make sure that <a href="http://doc.trolltech.com/4.4/qwebsettings.html#WebAttribute-enum">PluginsEnabled</a> is set for the QWebPage's <a href="http://doc.trolltech.com/4.4/qwebpage.html#settings">settings</a> and assign that WebPage to any QWebView.</p><br /><p>It is now possible to embed widgets that are known to Qt's <b>runtime</b> MetaType-system into a WebView. You can make a widget accessible using both, the <a href="http://doc.trolltech.com/4.4/qmetatype.html#Q_DECLARE_METATYPE">Q_DECLARE_METATYPE</a> macro and the <a href="http://doc.trolltech.com/4.4/qmetatype.html#qRegisterMetaType-2">qRegisterMetaType</a> function.</p><br /><p>To show the widget, you have to add an HTML <i>object</i>-Tag to your page, like this:<br /><pre><br /><object type="application/<b>x-qt-plugin</b>"; classid="YourClass" name="myObject" /><br /></pre>It's now visible and can even be manipulated through JavaScript. You can access it's properties and it's public slots.</p><br /><p>I've create a small demo that shows you how to do it and what is possible. It consists of a QMake-project (.pro-file), two pairs of header and implementation files for the MyWebKit/MyWebPage and MyWidget classes and a demo HTML page. It should compile and run on any supported platform (Windows, Linux, Mac). Of course only when QtWebKit is enabled in the Qt installation.</p><br /><p><h2>Step 1</h2><br />First, we should derive from the necessary QtWebKit-classes to create our own MyWebView class that always has Qt plug-ins enabled.</p><br /><p><h3>MyWebKit.h</h3><br /><pre><br />#ifndef MY_WEBKIT_H<br />#define MY_WEBKIT_H<br />#include <QWebPage><br />#include <QWebView><br /><br />// Derive from QWebPage, because a WebPage handles<br />// plugin creation<br />class MyWebPage: public QWebPage<br />{<br /> Q_OBJECT<br /> protected:<br /> QObject *createPlugin(<br /> const QString &classid, <br /> const QUrl &url, <br /> const QStringList ¶mNames, <br /> const QStringList & paramValues);<br /> public:<br /> MyWebPage(QObject *parent = 0);<br />};<br /><br />// Derive a new class from QWebView for convenience.<br />// Otherwise you'd always have to create a QWebView<br />// and a MyWebPage and assign the MyWebPage object<br />// to the QWebView. This class does that for you<br />// automatically.<br />class MyWebView: public QWebView<br />{<br /> Q_OBJECT<br /> private:<br /> MyWebPage m_page;<br /> public:<br /> MyWebView(QWidget *parent = 0);<br />};<br /><br />#endif<br /></pre></p><br /><p><h3>MyWebKit.cpp</h3><br /><pre><br />#include "MyWebKit.h"<br /><br />#include <QUiLoader><br /><br />MyWebPage::MyWebPage(QObject *parent):<br /> QWebPage(parent)<br />{<br /> // Enable plugin support<br /> settings()->setAttribute(QWebSettings::PluginsEnabled, true);<br />}<br /><br />QObject *MyWebPage::createPlugin(<br /> const QString &classid, <br /> const QUrl &url, <br /> const QStringList ¶mNames, <br /> const QStringList & paramValues)<br />{<br /> // Create the widget using QUiLoader.<br /> // This means that the widgets don't need to be registered<br /> // with the meta object system.<br /> // On the other hand, non-gui objects can't be created this<br /> // way. When we'd like to create non-visual objects in<br /> // Html to use them via JavaScript, we'd use a different<br /> // mechanism than this.<br /> QUiLoader loader;<br /> return loader.createWidget(classid, view());<br />}<br /><br />MyWebView::MyWebView(QWidget *parent):<br /> QWebView(parent),<br /> m_page(this)<br />{<br /> // Set the page of our own PageView class, MyPageView,<br /> // because only objects of this class will handle<br /> // object-tags correctly.<br /> setPage(&m_page);<br />}<br /></pre><br />It's now possible to use Qt classes using the above-mentioned object tags.</p><br /><p><h2>Step 2</h2><br />The second step is to create a class that's known by the Qt runtime meta type system. We can't directly use Qt widgets in this way, because runtime meta types need copy-<br />constructors. So we derive from a Qt widget and add a kinda dull copy-constructor to it.<br /><h3>MyWidget.h</h3><br /><pre><br />#ifndef MY_WIDGET_H<br />#define MY_WIDGET_H<br /><br />#include <QMetaType><br />#include <QCalendarWidget><br /><br />class MyCalendarWidget: public QCalendarWidget<br />{<br /> Q_OBJECT<br /> public:<br /> MyCalendarWidget(QWidget *parent = 0);<br /> // Q_DECLARE_METATYPE requires a copy-constructor<br /> MyCalendarWidget(const MyCalendarWidget ©);<br />};<br />Q_DECLARE_METATYPE(MyCalendarWidget)<br /><br /><br />#endif<br /></pre><br />We use a calendar widget because it's something that doesn't already exist in HTML and could be quite useful. Additionally, it has some few properties that we'd want to access from JavaScript.<br /></p><br /><p><h2>Step 3</h2><br />The final step is to build the HTML page that embeds the widget and executes some JavaScript on it. Here's my example:<br /><h3>Test.html</h3><br /><pre><br /><html><br /> <head><br /> <title>QtWebKit Plug-in Test</title><br /> </head><br /> <body><br /> <object type="application/x-qt-plugin" classid="MyCalendarWidget" name="calendar" height=300 width=500></object><br /> <script><br /> calendar.setGridVisible(true);<br /> calendar.setCurrentPage(1985, 5);<br /> </script><br /> </body><br /></html><br /></pre><br />The example set the gridVisible property to true and shows the month that I am born in. Of course, the possibilities seem endless! :-)<br /></p><br /><p><h2>Conclusion</h2><br />The only thing that I am still missing is connecting signals to JavaScript functions, similar to what you do with AJAX. It's possible to export non-visual objects and use them from within JavaScript, too (think of a database connection, for example).</p><br /><p>You can download the complete project, which should work out-of-the-box when you have Qt with WebKit support installed, <a href="http://viming.de/WebPlugin.zip">here</a></p>Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com15tag:blogger.com,1999:blog-8065595436140811789.post-65089254199979919182008-02-14T01:29:00.001-08:002009-08-13T07:47:31.604-07:00Qt debugging with Visual Studio 2005zbenjamin, one of the fine folks from the <a href="http://libqxt.org">Qxt</a>-project just gave me his additions to the AutoExp.dat-file that lets you debug native Qt-types (e.g. QString) far more easily. Here's the before/after-comparison:<br /><h2>Before</h2><br /><img src="http://viming.de/msvc_qt_debug_before.png"/><br /><h2>After</h2><br /><img src="http://viming.de/msvc_qt_debug_after.png"/><br /><h2>How it's done</h2><br /><p>And here's what you have to do to use it yourself:<br />First, open up the file<br /><p><pre>C:\Program\ Files\Microsoft\ Visual\ Studio\ 8\Common7\Packages\Debugger\autoexp.dat</pre><br /><div style="color: white; margin-left:15px; background-color: #740800; border: 1px solid #A30800; padding: 5px;"><strong>Important:</strong> Under Windows Vista, you need to open the file as Administrator, because it is not writeable by the user and the program-files-virtualisation will get in your way.</div><br />Then, add the following lines under the <strong>[AutoExpand]</strong>-mark:<br /><div style="border: 1px solid gray; padding: 5px; margin-top: 15px;"><div style="padding: 5px; background-color: white; border; 1px solid white; color: black;"><pre>QObject =classname=<staticMetaObject.d.stringdata,s> superclassname=<staticMetaObject.d.superdata->d.stringdata,s><br />QList<*>=size=<d->end,i><br />QLinkedList<*>=size=<d->end,i><br />QString=<d->data,su> size=<d->size,u><br />QByteArray=<d->data,s> size=<d->size,u><br />QUrl =<d->encodedOriginal.d->data,s><br />QUrlInfo =<d->name.d->data,su><br />QPoint =x=<xp> y=<yp><br />QPointF =x=<xp> y=<yp><br />QRect =x1=<x1> y1=<y1> x2=<x2> y2=<y2><br />QRectF =x=<xp> y=<yp> w=<w> h=<h><br />QSize =width=<wd> height=<ht><br />QSizeF =width=<wd> height=<ht><br />QMap<*> =size=<d->size><br />QVector<*> =size=<d->size><br />QHash<*> =size=<d->size><br />QVarLengthArray<*> =size=<s> data=<ptr><br />QFont =family=<d->request.family.d->data,su> size=<d->request.pointSize, f><br />QDomNode =name=<impl->name.d->data,su> value=<impl->value.d->data,su></pre></div></div><br />Now restart Visual Studio and you should be good to go.Daniel Albuschathttp://www.blogger.com/profile/16902431458961243153noreply@blogger.com2