Lasconic's notesDesktop, web, mobile and beyond...http://127.0.0.1:2368/Ghost 0.5Mon, 22 Apr 2019 07:29:59 GMT60Compiling Qt Android app with Docker<p><img src='https://farm2.staticflickr.com/1486/24068500601_2a4cc1b0e6_o_d.png' alt="Docker+Qt+Android" /></p> <p>Compiling a Qt app for Android requires <a href='http://doc.qt.io/qt-5/androidgs.html' >several dependencies</a>, you need to install Qt, the Android SDK, the Android NDK, ant and so on. If your app uses other Google SDKs you need to install them. Using Docker could simplify the whole setting a lot.</p> <p>While looking into Continuous Integration for our apps, I found this <a href='https://hub.docker.com/r/rabits/qt/' >almost perfect docker container</a>, so I thought I should share it here. This container contains Qt 5.4, the Android SDK and NDK. It almost perfect for my purpose but it lacks some aditionnal SDKs such as the Google Play SDK, so I created one with the SDKs I needed: <a href='https://hub.docker.com/r/lasconic/qt/' >https://hub.docker.com/r/lasconic/qt/</a></p> <h2 id="howto">How to </h2> <p>With a clean Ubuntu 14.04 server</p> <ul> <li>Install Docker. See <a href='https://docs.docker.com/engine/installation/ubuntulinux/' >the guide</a> </li> </ul> <pre><code>$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D $ sudo sh -c 'echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" &gt;&gt; /etc/apt/sources.list.d/docker.list' $ sudo apt-get update $ sudo apt-get -qq install linux-image-extra-$(uname -r) $ sudo apt-get -qq install docker-engine </code></pre> <ul> <li><p>Get your project source</p></li> <li><p>Launch the container with an interactive shell and your source mounted in a volume. Of course, we could run a build script instead of compiling the project manually.</p></li> </ul> <pre><code>$ sudo docker pull lasconic/qt:5.4-android $ sudo docker run -i -t -v /home/ubuntu/myqtproject:/source lasconic/qt:5.4-android </code></pre> <ul> <li>Compile your project (I use gradle, not ant) and create an APK</li> </ul> <pre><code>mkdir ~/build cd ~/build qmake -r /source/myproject.pro make -j2 mkdir ~/dist make install INSTALL_ROOT=~/dist androiddeployqt --input android-libmyproject.so-deployment-settings.json --output ~/dist --name myapp --deployment bundled --gradle --release </code></pre> <p>From there, you can upload the APK to Google Play. I use the <a href='https://github.com/Triple-T/gradle-play-publisher' >"Gradle Play Publisher"</a> plugin.</p>http://127.0.0.1:2368/compiling-qt-android-app-with-docker/21229785-11a2-49df-9e4e-4a24d2257cd4lasconicSun, 03 Jan 2016 17:04:09 GMTShare from a QML app on iOS and Android<p>What if you want to add a share feature to your cross platform QML app? Share on Facebook, Twitter, Email etc...</p> <h2 id="qmlside">QML side</h2> <p>We will use a custom component that will let us share a text and a URL.</p> <pre><code class="language-javascript">import QtQuick 2.3 import QtQuick.Controls 1.2 // Our import import com.lasconic 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Share Utils") //Custom component ShareUtils { id: shareUtils } Button { id: share text: "Share lasconic's blog" anchors.centerIn: parent onClicked: { //simple call shareUtils.share("Interesting blog", "http://blog.lasconic.com") } } } </code></pre> <p>The component needs to be registered in the <code>main.cpp</code> of the app.</p> <pre><code class="language-c++">qmlRegisterType&lt;ShareUtils&gt; ("com.lasconic", 1, 0, "ShareUtils"); </code></pre> <p>Now, of course, we need to implement the component on iOS and Android.</p> <h2 id="cside">C++ side</h2> <p>The main C++ class is a just a delegate for the OS specific implementation.</p> <pre><code class="language-c++">ShareUtils::ShareUtils(QQuickItem *parent) : QQuickItem(parent) { #if defined(Q_OS_IOS) _pShareUtils = new IosShareUtils(this); #elif defined(Q_OS_ANDROID) _pShareUtils = new AndroidShareUtils(this); #else _pShareUtils = new PlatformShareUtils(this); #endif } void ShareUtils::share(const QString &amp;text, const QUrl &amp;url) { _pShareUtils-&gt;share(text, url); } </code></pre> <h2 id="onandroid">On Android</h2> <h3 id="c">C++</h3> <p>We will just use JNI on the C++ side to call the Java side and the Android API. The Qt AndroidExtras package is handy.</p> <pre><code class="language-c++">void AndroidShareUtils::share(const QString &amp;text, const QUrl &amp;url) { QAndroidJniObject jsText = QAndroidJniObject::fromString(text); QAndroidJniObject jsUrl = QAndroidJniObject::fromString(url.toString()); QAndroidJniObject::callStaticMethod&lt;void&gt;("com/musescore/QShareUtils", "share", "(Ljava/lang/String;Ljava/lang/String;)V", jsText.object&lt;jstring&gt;(), jsUrl.object&lt;jstring&gt;()); } </code></pre> <p>In the pro file, we need to add <code>QT += androidextras</code> to be able to use <code>QAndroidJniObject</code>.</p> <h3 id="java">Java</h3> <p>We use QtNative to get the current activity and then it's pure Android API code. It will display all the apps that can deal with the <code>text/plain</code> mime type. We could choose which one we want to be listed, add a subject field etc... but it's beyond the scope of this article. A good intro <a href='http://android-developers.blogspot.fr/2012/02/share-with-intents.html' >here</a>.</p> <pre><code class="language-java">import org.qtproject.qt5.android.QtNative; public static void share(String text, String url) { if (QtNative.activity() == null) return; Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, text + " " + url); sendIntent.setType("text/plain"); QtNative.activity().startActivity(sendIntent); } </code></pre> <h2 id="onios">On iOS</h2> <pre><code class="language-objectivec">#import &lt;UIKit/UIKit.h&gt; #import &lt;QtGui/qpa/qplatformnativeinterface.h&gt; #import &lt;QGuiApplication&gt; #import &lt;QQuickWindow&gt; void IosShareUtils::share(const QString &amp;text, const QUrl &amp;url) { NSMutableArray *sharingItems = [NSMutableArray new]; if (!text.isEmpty()) { [sharingItems addObject:text.toNSString()]; } if (url.isValid()) { [sharingItems addObject:url.toNSURL()]; } // Get the UIViewController UIViewController *qtController = [[UIApplication sharedApplication].keyWindow rootViewController]; UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil]; [qtController presentViewController:activityController animated:YES completion:nil]; } </code></pre> <h2 id="conclusion">Conclusion</h2> <p>Et voilĂ , the QML app will be able to pass a text and a URL to the underlying operating system and the user can enjoy a native UI to share to the social media of his choice.</p> <p>Full code: <a href='https://github.com/lasconic/ShareUtils-QML' >https://github.com/lasconic/ShareUtils-QML</a></p>http://127.0.0.1:2368/share-on-ios-and-android-using-qml/4dd2a81c-55ab-4ae4-9ac9-acccba0d17c6QtQMLQt QuickAndroidiOsSharelasconicTue, 30 Sep 2014 16:11:44 GMTUse a custom icon font in QML<p>When it comes to images, mobile development is facing exactly the same problem than mobile web development. Images need to scale well, have a decent file size etc.. Responsive web design often use icon fonts to solve this problem for icons. In this case, an icon font also have the advantage of limiting HTTP requests.</p> <p>In QML, we do have the possibilities to use <a href='http://qt-project.org/doc/qt-5/qml-qtquick-borderimage.html' >BorderImage</a>, or to use SVG files. Both approaches have their merits but they also have some drawbacks. For example, currently, SVG files are not rendered correctly on retina display (<a href='https://bugreports.qt-project.org/browse/QTBUG-35271' >QTBUG-35271</a>). BorderImage are less interesting for icons since the icon itself would not scale.</p> <p>Other developers used icon fonts in QML, in particular using the Font Awesome web font.</p> <h3 id="previousworkswithfontawesome">Previous works with Font Awesome</h3> <p><a href='http://fortawesome.github.io/Font-Awesome/' >Font Awesome</a> is a popular icon font in web development. So popular than for oncen it's also used in QML. 2 years ago, markg85 described how to <a href='http://kdeblog.mageprojects.com/2012/11/20/using-fonts-awesome-in-qml/' >Use font awesome in QML</a>. Recently, Ricardo do Valle created a <a href='https://github.com/ricardodovalle/font-awesome-qml' >GitHub project</a> to demonstrate the same approach with QML in Qt 5.3.</p> <p>Here I want to quickly show how one can create his own web font and use it in QML, based on the same method.</p> <h3 id="createafontonicomoon">Create a font on Icomoon</h3> <p>Create a couple of icons in your favorite vector graphics editor, save as SVG and go to <a href='https://icomoon.io/' >Icomoon.io</a>. The website is one of the numerous options available to create a web font. It's easy to add your icons and then create a font. You can customize codepoints, font name and so on.</p> <h3 id="addthefonttoaqmlapp">Add the font to a QML app</h3> <p>The QML app will use the TTF format of the font. You can grab it from the zip generated by Icomoon. We would also want to have an easy way to refer to each icon in the font to avoid the following:</p> <pre><code class="language-javascript">Label { text: "\ue601" font.family: "MyFont" } </code></pre> <p>Icomoon also provide an HTML demo file. I wrote a small python script to parse it and extract a javascript file to map the codepoints with human friendly names. The script takes the path to demo.html as a command line parameter.</p> <pre><code class="language-python">import requests import re import sys from bs4 import BeautifulSoup f = open(sys.argv[1],"r") data = f.read() soup = BeautifulSoup(data) glyphs = soup.find_all('div', class_="glyph") print ".pragma library" print "var Icon = {" for g in glyphs: ##t = g.text.strip() pbs = g.find_all('div', class_="pbs") for pb in pbs: variable = pb.text.strip() variable = re.sub(r'(?!^)-([a-zA-Z])', lambda m: m.group(1).upper(), variable[10:]) #print variable input = g.find_all('input', class_="unit size1of2") for i in input: charcode = "\\u" + i['value'] print " " + variable + " " * (20 - len(variable)) + ": \"" + charcode + "\"," print "}" </code></pre> <p>The output of this script is a javascript file, <code>MyFont.js</code> to include in a QML app. And we can then write:</p> <pre><code class="language-javascript">import "MyFont.js" as MyFont Label { text: MyFont.Icon.logo // name of the icon font.family: "MyFont" } </code></pre> <h3 id="conclusion">Conclusion</h3> <p>This is it. Comments or questions? <a href='https://twitter.com/lasconic' >@lasconic</a></p>http://127.0.0.1:2368/use-a-custom-icon-font-in-qml/46555739-723f-4d0f-b2cc-21ec4a4b97f5QtQMLQt QuickDesignlasconicMon, 22 Sep 2014 08:39:39 GMTA customized busy indicator in Qt Quick<p>For the <a href='http://musescore.com/apps' >MuseScore apps</a>, I needed an easily customizable <a href='http://qt-project.org/doc/qt-5/qml-qtquick-controls-busyindicator.html' ><code>BusyIndicator</code></a>. The designer came up with something similar to <a href='http://fgnass.github.io/spin.js/' >spin.js</a> but of course, we needed it in QML. </p> <p>Here is spin.js in action: <br /> <img src='http://127.0.0.1:2368/content/images/2014/Sep/spinjs.gif' alt="" /></p> <p>And here is the QML version. <br /> Since we can't run QML in the browser (<a href='https://gitorious.org/qmlweb/pages/Home' >yet?</a>), I made a short video. Of course, you can just <a href='https://github.com/lasconic/CustomBusyIndicator-QML' >download the source code</a> and run it yourself.</p> <iframe width="560" height="315" src='http://www.youtube.com/embed/pn3dWPNeA2M' frameborder="0" allowfullscreen> </iframe> <h3 id="busyindicatorstyle">BusyIndicatorStyle</h3> <p>Qt Quick Controls can be customized with a (ControlName)Style. For a <code>BusyIndicator</code>, it's a <a href='http://qt-project.org/doc/qt-5/qml-qtquick-controls-styles-busyindicatorstyle.html' ><code>BusyIndicatorStyle</code></a>.</p> <pre><code class="language-javascript">BusyIndicatorStyle { id: style property int lines: 11 property real length: 10 // % of the width of the control property real width: 5 // % of the height of the control property real radius: 13 // % of the width of the control property real corner: 1 // between 0 and 1 property real speed: 100 // smaller is faster property real trail: 0.6 // between 0 and 1 property bool clockWise: true property real opacity: 0.7 property string color: "#7B756B" property string highlightColor: "white" property string bgColor: "black" indicator: Rectangle { color: style.bgColor visible: control.running Repeater { id: repeat model: style.lines Rectangle { property real factor: control.width / 200 color: style.color opacity: style.opacity Behavior on color { ColorAnimation { from: style.highlightColor duration: style.speed * style.lines * style.trail } } radius: style.corner * height / 2 width: style.length * factor height: style.width * factor x: control.width / 2 + style.radius * factor y: control.height / 2 - height / 2 transform: Rotation { origin.x: -style.radius * factor origin.y: height / 2 angle: index * (360 / repeat.count) } Timer { id: reset interval: style.speed * (style.clockWise ? index : style.lines - index) onTriggered: { parent.opacity = 1 parent.color = style.highlightColor reset2.start() } } Timer { id: reset2 interval: style.speed onTriggered: { parent.opacity = style.opacity parent.color = style.color } } Timer { id: globalTimer // for a complete cycle interval: style.speed * style.lines onTriggered: { reset.start() } triggeredOnStart: true repeat: true } Component.onCompleted: { globalTimer.start() } } } } } </code></pre> <p>I exposed the different customization parameters as QML properties. </p> <p>Then the core of the style is a <a href='http://qt-project.org/doc/qt-5/qml-qtquick-repeater.html' >Repeater</a>. It will draw a given number of <a href='http://qt-project.org/doc/qt-5/qml-qtquick-rectangle.html' >Rectangles</a> using the color, corner radius etc... and of course a <a href='http://qt-project.org/doc/qt-5/qml-qtquick-rotation.html' >Rotation</a>.</p> <p>To animate the spinner, I relied on 3 <a href='http://qt-project.org/doc/qt-5/qml-qtqml-timer.html' >Timers</a> per rectangle. </p> <ul> <li>A global timer fires for every cycle and start a second timer. </li> <li>This second timer will animate the color and opacity of the given rectangle with an interval depending on the index of the rectangle and start a new timer. </li> <li>This last timer will restore the state of the rectangle to default according to the speed parameters.</li> </ul> <h2 id="sourcecode">Source code</h2> <p>The code is on GitHub: <a href='https://github.com/lasconic/CustomBusyIndicator-QML' >CustomBusyIndicator</a> <br /> It could be that there is a better and more QML way of doing the animation. If so, let me know <a href='https://twitter.com/lasconic' >@lasconic</a></p> <p>Edit: For a QtWidget approach, see <a href='https://github.com/snowwlex/QtWaitingSpinner' >https://github.com/snowwlex/QtWaitingSpinner</a></p>http://127.0.0.1:2368/a-customized-busy-indicator-in-qt-quick/5d3016a8-8350-4e3d-81d8-205a190cf415QtQMLQt QuicklasconicThu, 18 Sep 2014 13:16:17 GMTHow to use Google Analytics in a Qt Quick app for Android/iOS<p>Even if there are other mobile analytics services such as <a href='http://www.flurry.com/' >Flurry</a>, Google Analytics (GA) is a popular (and free) way to get insights about marketing campaigns or app usage. In this article I will explain how to implement a simple tracking of displayed screens and events in a multiplatform Qt Quick app. </p> <p><em>tl;dr</em> : A sample app is available on GitHub: <a href='https://github.com/lasconic/GATutorial-QML' >GATutorial-QML</a>. </p> <h4 id="chooseyourway">Choose your way</h4> <p>When integrating with third party services in a Qt Quick app, there are often two ways. </p> <ul> <li>The API way</li> <li>The native SDK way</li> </ul> <p>For GA, there is a low level API called <a href='https://developers.google.com/analytics/devguides/collection/protocol/v1/' >Measurement Protocol</a>. When a low level API, the developer is required to write more code. Here, we would need to cache data when the user is offline, get device ID ourself etc... The advantage of using the low level API is that it's easy to make a cross platform app. Measurement protocol relies only on HTTP.</p> <p>GA also provides SDKs for Android and iOS. Both SDKs most likely implements the Measurement Protocol but they also provide several other features such as automatic screen views tracking (but a Qt Quick app cannot use it...), caching of hits when the device is offline, getting device ID for better tracking... To sum it up, The SDKs are doing all the heavy lifting for us. Google also provide an <a href='https://github.com/googleanalytics/google-analytics-plugin-for-unity' >SDK for Unity</a> but no Qt Quick SDK. It's a curse any Qt developer will encounter a lot... (Hopefully not for long!)</p> <p>If you are reading this, and know about a Google Analytics Qt project, even not from Google, <a href='http://twitter.com/lasconic' >let me know</a>, I'd love to be wrong.</p> <p>Our app is currently only available on iOS and Android. So, I chose the SDK way with a future fallback to Measurement Protocol if necessary.</p> <h3 id="creatingtheappandasimpleapi">Creating the app and a simple API</h3> <ul> <li><p>Obviously a new property and tracking ID is necessary. You can get one on Google Analytics website.</p></li> <li><p>Create a C++ class we can call from QML. The class has two Q_INVOKABLE functions <code>sendHit</code> and <code>sendEvent</code>. Both are just calling the native implementation that we will implement later. We also define a mother class for the native implementation <code>PlatformGoogleAnalytics</code>. Next paragraphs will describe the Android and iOS implementation. A default implementation using Measurement Protocol could be written too.</p></li> <li><p>Expose the class to QML. In main.cpp, add </p></li> </ul> <pre><code class="language-cpp">GoogleAnalytics ganalytics; ganalytics.initTracker(); engine.rootContext()-&gt;setContextProperty("gAnalytics", &amp;ganalytics); </code></pre> <ul> <li>in QML, to register an new screen view, just call</li> </ul> <pre><code class="language-javascript">gAnalytics.sendHit("myView") </code></pre> <h3 id="androidglue">Android glue</h3> <p>We have now a C++ class that we can call from QML. This class will now need to call Java and the Google Analytics SDK for Android.</p> <h5 id="setupthegoogleanalyticssdkforandroid">Setup the Google Analytics SDK for Android</h5> <ul> <li><p>Create a directory in your project named <code>android_data</code>. </p></li> <li><p>Add the following to your pro file </p></li> </ul> <pre><code class="language-javascript ">ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android_data </code></pre> <p>Qt Creator will merge the <code>android_data</code> directory with the one provided by Qt when building.</p> <ul> <li><p>Install the Google Analytics SDK for Android. You can find it in the maintenance tool of the SDK, in the extra section. Once installed, copy the <code>sdk/extras/google/google_play_services</code> directory to <code>android_data</code></p></li> <li><p>create a project.properties file in android_data and add this line</p></li> </ul> <pre><code class="language-javascript">android.library.reference.1=google-play-services_lib </code></pre> <ul> <li>copy <code>android-support-v4.jar</code> from the android SDK to "android_data/libs"</li> </ul> <p>*Copy the <code>AndroidManifest.xml</code> file from Qt ( <code>android_armv7/src/android/java/AndroidManifest.xml</code>) to the <code>android_data</code> directory in the project</p> <ul> <li>Add the following the <code>AndroidManifest.xml</code> In <code>&lt;activity&gt;</code> </li> </ul> <pre><code class="language-markup">&lt;!-- Analytics --&gt; &lt;meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/&gt; &lt;meta-data android:name="com.google.android.gms.analytics.globalConfigResource" android:resource="@xml/analytics_global_config"/&gt; &lt;!-- Analytics --&gt; </code></pre> <p>in <code>&lt;application&gt;</code> </p> <pre><code class="language-markup">&lt;!-- Used for Google Play Store Campaign Measurement--&gt;; &lt;service android:name="com.google.android.gms.analytics.CampaignTrackingService"/&gt; &lt;receiver android:name="com.google.android.gms.analytics.CampaignTrackingReceiver" android:exported="true"&gt; &lt;intent-filter&gt; &lt;action android:name="com.android.vending.INSTALL_REFERRER"/&gt; &lt;/intent-filter&gt; &lt;/receiver&gt; </code></pre> <p>This second part is not useful for tracking hits and events. It's used to track installation referrer. Read more about <a href='https://developers.google.com/analytics/devguides/collection/android/campaigns' >Campaign Measurement</a></p> <ul> <li>Add analytics configuration files, as described in <code>AndroidManifest.xml</code>, in <code>android_data/res/xml</code>. I created one for the tracker and one with global parameters. The tracker one contains the tracker ID and it needs to be change to your own value.</li> </ul> <h5 id="javaside">Java side</h5> <p>I wrote a short Java class, named QGoogleAnalytics in order to call two of the function provided by the SDK. The class is very simple and has two static public functions <code>sendHit</code> and <code>sendEvent</code>. Let's see how we can call them from C++ via JNI.</p> <h5 id="cjni">C++ JNI</h5> <p>Qt provides a very useful androidextras package to ease the writing of JNI.</p> <ul> <li>add the following to your pro file</li> </ul> <pre><code class="language-javascript">android { QT += androidextras } </code></pre> <ul> <li>The code to call a static method is easy enough. The C++ class inherits from <code>PlatformGoogleAnalytics</code>.</li> </ul> <pre><code class="language-cpp">void AndroidGoogleAnalytics::sendHit(const QString &amp;screenName) { QAndroidJniObject jsScreenName = QAndroidJniObject::fromString(screenName); QAndroidJniObject::callStaticMethod&lt;void&gt;("com/lasconic/QGoogleAnalytics", "sendHit", "(Ljava/lang/String;)V", jsScreenName.object&lt;jstring&gt;()); } </code></pre> <h3 id="iosglue">iOS glue</h3> <h4 id="setupthegoogleanalyticssdkforios">setup the Google Analytics SDK for iOS</h4> <ul> <li>Download the <a href='https://developers.google.com/analytics/devguides/collection/ios/resources' >Google Analytics SDK for iOS v3</a></li> <li>Unzip it</li> <li>Create an <code>ios</code> directory in your project and copy the <code>include</code> directory in it</li> <li>Create a <code>libs</code> directory and copy <code>libAdIdAccess.a</code> and <code>libGoogleAnalyticsServices.a</code> in it</li> <li>Modify the pro file to link these libraries and add framework dependencies.</li> </ul> <pre><code>ios { QMAKE_LFLAGS += -framework CoreData -framework AdSupport -ObjC LIBS += -L$$PWD/ios/libs -lGoogleAnalyticsServices -lAdIdAccess -lsqlite3.0 } </code></pre> <h4 id="objectivecglue">Objective-C glue</h4> <p>Objective-C can be mixed with C++ more easily than Java.</p> <ul> <li>To add an Objective-C class, modify the pro files</li> </ul> <pre><code class="language-javascript">OBJECTIVE_SOURCES += ios/iosanalytics.mm </code></pre> <ul> <li>also add a header file <code>iosanalytics.h</code></li> <li>This class inherits <code>PlatformGoogleAnalytics</code> and call the SDK directly. You need to change the GA tracking ID in this class. Qt provides a convenient <code>toNSString()</code> method for <code>QString</code>.</li> </ul> <pre><code class="language-objectivec">void IosGoogleAnalytics::sendHit(const QString &amp;screenName) { id&lt;GAITracker&gt; tracker = [[GAI sharedInstance] defaultTracker]; // Send a screen view [tracker send:[[[GAIDictionaryBuilder createAppView] set:screenName.toNSString() forKey:kGAIScreenName] build]]; } </code></pre> <h4 id="conclusion">Conclusion</h4> <p>This is a working and simple solution to integrate an iOS/Android Qt Quick app with Google Analytics. The code is available on GitHub: <a href='https://github.com/lasconic/GATutorial-QML' >GATutorial-QML</a>. <br /> The approach would be similar for any service which provide more value in native SDKs than in its crossplatform API, or even doesn't provide a crossplatform API.</p> <p>Of course, it's a crude example. We could expose more <a href='https://developers.google.com/analytics/solutions/mobile-implementation-guide' >Google Analytics features</a> to QML such as e-commerce tracking. Also, if we would want to support more platforms such as desktop ones or Windows Mobile, we would need to complete the fallback to Measurement Protocol.</p> <p>Any comment? Find me on <a href='https://twitter.com/lasconic' >twitter</a>.</p>http://127.0.0.1:2368/how-to-use-google-analytics-in-a-qt-quick-app-for-androidios/cfc1bb7a-ae6f-4387-b8e0-257452a6bbdaanalyticsQtQMLQt QuickAndroidiOsmobilelasconicThu, 11 Sep 2014 21:33:17 GMTFree blog hosting using Ghost, Buster and GitHub Pages<p>I considered several blog engines before starting this blog. First, I wanted an unexpensive and portable solution. Also, I wanted to avoid ads. I considered a self hosted <a href='http://wordpress.org/' >Wordpress</a> but Wordpress has become a huge and complex pile of code. I'm using Markdown and git daily, so an engine based on them would be nice. I tested <a href='http://jekyllrb.com/' >Jekyll</a> with <a href='https://github.com/poole' >Hyde and Lanyon</a> and <a href='http://octopress.org/' >Octopress</a> but Ruby... no thanks. I prefer Python, so I took a look to <a href='http://getpelican.org/' >Pelican</a> but it feels less supported. So no good solution...</p> <p>I came accross <a href='https://ghost.org/download/' >Ghost</a> which seems to gain momentum. Ghost lets me write in Markdown syntax and is based on Node.js. The default theme looks good enough and the community support seems ok. Still, I needed a database and a Node.js instance and so a server. I'd prefer to use Github Pages instead, so static HTML pages.</p> <p><a href='https://github.com/axitkhurana/buster' >Buster</a> to the rescue! Buster (Ghost, Buster, <a href='https://www.youtube.com/watch?v=I9bNyDjczVQ' #t=12">got it</a>?) is a Python script to extract static content from a running Ghost instance. OK! Sound like a plan.</p> <h2 id="installingghost">Installing Ghost</h2> <p>I like to use nvm to manage the version of node I'm running. </p> <pre><code>$ nvm install 0.10.30 $ nvm use 0.10.30 </code></pre> <p>Then installing Ghost is easy enough. Download the zip from <a href='https://ghost.org/download/' >Ghost download page</a>. Unzip it.</p> <pre><code>$ cd ghost $ npm install --production </code></pre> <p>And run it</p> <pre><code>$ npm start $ open 127.0.0.1:2368/ghost/ </code></pre> <p>Configure Ghost in the browser.</p> <h2 id="installingbuster">Installing Buster</h2> <ul> <li>Install <a href='http://pip.readthedocs.org/en/latest/installing.html' >pip</a> if necessary. </li> <li>Install <a href='https://github.com/axitkhurana/buster' >Buster</a></li> </ul> <pre><code>$ pip install buster </code></pre> <h2 id="githubpages">GitHub pages</h2> <ul> <li>Create a new repository on GitHub</li> <li><code>cd ghost</code></li> <li><code>buster setup</code> and enter repository address</li> <li><code>buster add-domain blog.example.com</code></li> <li>Modify CNAME of DNS provider. <ul><li>Name: <code>blog</code> </li> <li>Value: <code>blog.example.com.</code></li></ul></li> </ul> <h2 id="worflow">Worflow</h2> <ul> <li>Type article in the web interface of Ghost</li> <li>When article is ready to be published </li> </ul> <pre><code> $ buster generate --domain 127.0.0.1:2368 $ buster preview $ buster deploy </code></pre> <h2 id="conclusion">Conclusion</h2> <p>Since you are reading this conclusion, it's running. I still need to pick a theme and add a couple of features I'm missing.</p> <ul> <li>About page</li> <li>Syntax hightlighter</li> <li>Analytics</li> <li>Comments, probably using Disqus</li> <li>Eventually a search engine but with static websites is not always easy. I will check <a href='http://tapirgo.com/' >Tapir</a> and <a href='http://www.tipue.com/search/' >Tipue</a></li> </ul>http://127.0.0.1:2368/free-blog-hosting-using-ghost-buster-and-github-pages/7d69aed9-af65-4228-a679-f97330cd5d75ghost nodejsinstalltutoriallasconicFri, 05 Sep 2014 08:39:54 GMTFirst post, what to expect<p>I started several blogs along the years. My last attempt was probably around 2011. I hope this one will last longer! So what to expect from this blog?</p> <h2 id="programmingandsoftwarearchitecture">Programming and software architecture</h2> <p>I'm currently the CTO at <a href='http://musescore.com/' >MuseScore</a>. Primarly, MuseScore is a desktop application written in C++/Qt. I'm also in charge of the development of the MuseScore mobile apps for both iOS and Android. So I will probably write about Qt, QML, C++ and mobile development.</p> <p>MuseScore is also two websites <a href='http://musescore.org/' >http://musescore.org</a> and <a href='http://musescore.com/' >http://musescore.com</a>. I'm not the webmaster of these two websites, but I did work on the development of both. The two websites are made with <a href='http://drupal.org/' >Drupal</a> and PHP. In particular, I worked on the <a href='http://developers.musescore.com/' >MuseScore.com API</a> and on the overall architecture of MuseScore.com running on AWS. So you might expect post about Drupal, PHP, Javascript, Java and AWS services.</p> <p>I'm learning Python which is becoming my scripting language of choice. I also like to dabble in Node.js. And I'm planning to play with Go in the coming months.</p> <h3 id="musicandmusicnotation">Music and Music Notation</h3> <p>I'm a drummer and music lover. MuseScore is all about music notation. Expect some posts about music in general, music notation in particular, and maybe drumming.</p> <h3 id="entrepeneurship">Entrepeneurship</h3> <p>MuseScore is my first entrepeunarial endeavior. Putting in place a business model around a B2C free and open source software is challenging. I might post about this challenge and about the sheet music and music education market in general.</p> <h3 id="englishmainly">English mainly</h3> <p>Maybe you didn't figure it out yet but my mother tongue is French. I'm more confortable with English for technical topics, so I will mainly blog in English.</p> <p>Happy reading!</p>http://127.0.0.1:2368/first-post-what-to-expect/ff607005-dd48-47c6-ae6b-7da805d0a670blogaboutlasconicWed, 03 Sep 2014 10:04:38 GMT