Web++ framework for Qt. This is a framework supplementary to Qt for mobile, for creating mobile apps for iOS and Android. More platforms support will be added later, currently this library only support iOS and Android.
- Android support: minSDK: 10, targetSDK: 22
- iOS support: iOS7+
- Gradle (don't use Apache Ant)
- Android SDK with android-22 installed
* Android SDK with "Android SDK Build-tools" (22.0.1+) installed
You will usually use this project as a git-submodule of your project. Setup with this:
cd YourQtProject
git submodule add https://github.com/minixxie/wpp-qt.git
Once you've cloned this project, make sure to download sub-modules dependencies:
- B-Sides/ELCImagePickerController
- skywinder/ActionSheetPicker-3.0
- donglua/PhotoPicker
- leolin310148/ShortcutBadger
cd wpp-qt
git submodule init
git submodule update
Then, remember to include the project file in YourQtProject.pro (assuming wpp-qt folder is under your project folder):
## import library project "wpp"
include($$PWD/wpp-qt/wpp.pri)
To make sure the android part works, please do this (You can ignore if you don't target to support android):
- create android template folder from Qt Creator, and remember to "Use Gradle" as your packager:
The above also help add these lines into the pro file:
DISTFILES += \
android/gradle/wrapper/gradle-wrapper.jar \
android/AndroidManifest.xml \
android/res/values/libs.xml \
android/build.gradle \
android/gradle/wrapper/gradle-wrapper.properties \
android/gradlew \
android/gradlew.bat
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
- Create the link to the android library project "wpp-android"
YourQtProject/android/settings.gradle:
include 'wpp-android'
project(':wpp-android').projectDir = new File('../../YourQtProject/wpp-qt/wpp-android')
YourQtProject/android/build.gradle:
dependencies {
...
compile project(':wpp-android')
}
YourQtProject/android/AndroidManifest.xml:
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="22"/>
To use this library, the first requirement is to substitute QGuiApplication with wpp::qt::QGuiApplication, and QQmlApplicationEngine with wpp::qt::QQmlApplicationEngine:
#include <wpp/qt/QGuiApplication.h>
#include <wpp/qt/QQmlApplicationEngine.h>
int main(int argc, char *argv[])
{
wpp::qt::QGuiApplication app(argc, argv);//changed from QGuiApplication
wpp::qt::QQmlApplicationEngine engine;//changed from QQmlApplicationEngine, which provides "wpp" object in QML
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
The wpp::qt::QGuiApplication class actually inherits from QGuiApplication and it registers some wpp library things in addition. The wpp::qt::QQmlApplicationEngine class inherits from QQmlApplicationEngine and it injects "wpp" object into the QML root context.
All QML elements only support pixel values for x, y, width, height and all size and dimension related properties. With the main function used in "To Begin", "wpp" variable can be used in QML like this:
Rectange {
anchors.fill: parent
anchors.margins: 10*wpp.dp2px //dp2px means changing 10 from "dp" to "px" as all QML properties only accept pixels
}
By default, Qt does "adjustPan" of the window content when the soft keyboard comes out. This is not professional as an app. Normally we would like to keep the title bar not moved, but just the content under the title bar to scroll up. For android, we can choose "adjustResize" for window:softInputMode in AndroidManifest.xml and Qt has already implemented it since Qt5.3. But for iOS, I still couldn't find a work around. That's why I did this work around myself:
By using wpp::qt::QGuiApplication, the whole app is by default with "adjustResize" characteristics for both iOS and Android. No setting in AndroidManifest.xml is needed for window:softInputMode, this is implemented in QGuiApplication class.
Scenario I: I want to scroll a particular flickable when an input box is clicked, so that it will not be covered by the soft keyboard.
import wpp.qt 2.0
TitleBar {
id: titleBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
label.text: "My App"
}
Flickable {
id: pageScrollable
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
TextField {
id: textfield1
...
HandleSoftKeyboardMouseArea {
anchors.fill: parent
flickable: pageScrollable //will scroll this flickable appropriately when keyboard shown, to avoid the who app window to be moved upward
}
}
...
TextField {
id: textfield10
anchors.top: textfield9.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 24*wpp.dp2px
//has to add this element to each input element (TextInput, TextField, TextEdit, etc)
HandleSoftKeyboardMouseArea {
anchors.fill: parent
flickable: pageScrollable
}
}
}
Scenario II: I want to shorten the app window height, so that it only occupy the space where keyboard doesn't block. This is the same as the "adjustResize" effect implemented by Android.
Rectangle {
//since this is stick to the bottom of the window, it will automatically be raised up to the top of the keyboard, when soft keyboard is shown
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
TextInput {
...
HandleSoftKeyboardMouseArea {
anchors.fill: parent
//no need to assign "flickable" here as we don't need to scroll anything when this input box is focused
}
}
}
Mechansim: HandleSoftKeyboardMouseArea handle the clicking by the user, delay the keyboard from showing up, and delay the input element from acquiring focus, thus avoiding the UI to be pushed upward. And it saves some time for the flickable to scroll up before the keyboard is shown.
see example: AdjustResize
TimeAgo is a class for generating human readable date/time. For example, it shows "2 hours ago", "15 mins ago", etc. By using wpp::qt::QGuiApplication and wpp::qt::QQmlApplicationEngine in main(), timeago can be used in QML:
Text {
text: timeago.getTimeAgo(unixTimestamp)
}
This QML element mimic the TitleBar of NavigationController on iOS.
TitleBar {
id: titleBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: "Hello"
}
We usually need to use rounded corner on images, Qt doesn't support it by default. With this library you can do this. But the current version has a limitation that the image should not lie on a boundary of two differnt colors or other background images:
Avatar {
id: profilePhoto
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10*wpp.dp2px
height: 40*wpp.dp2px
width: height
circleMask: true //this is the main property to make circle
maskColor: "#ffffff" //assume the background is white, write this to make sure 4 round corners are in white background
url: "http://xxxxxx/abc.jpg"
onClicked: {
//....
}
}
This is a common use case, which often show a empty photo for clicking to set the profile photo of a user. With this library, you can do this:
ImageSelector {
id: imageSelector
anchors.fill: parent
maxPick: 1
onPhotoTaken: { //string imagePath
console.debug("onPhotoTaken:" + imagePath);
profilePhoto.url = "file:" + imagePath;
}
onPhotoChosen: { //variant imagePaths
if ( imagePaths.length == 1 )
{
var imagePath = imagePaths[0];
console.debug("onPhotoChosen:" + imagePath);
profilePhoto.url = "file:" + imagePath;
}
}
onCropFinished: {
}
onCropCancelled: {
}
}
Avatar {
id: profilePhoto
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 10*wpp.dp2px
height: 100*wpp.dp2px
width: height
bgText: qsTr("Upload Image")
bgTextColor: "#0080ff"
//url: "http://xxxxxx/abc.jpg"
onClicked: {
imageSelector.open();
}
}
<!-- android/AndroidManifest.xml: register 2 activities -->
<activity android:name="me.iwf.photopicker.PhotoPickerActivity" android:theme="@style/Theme.AppCompat.NoActionBar">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
</activity>
<activity android:name="me.iwf.photopicker.PhotoPagerActivity" android:theme="@style/Theme.AppCompat.NoActionBar">
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
</activity>
see example: UsingNativeCameraAndImagePicker
Employed the native DateTime picker UI for you:
DateTimeControl {
id: startDateTimeControl
anchors.top: parent.top
anchors.left: parent.left; anchors.right: parent.right;
height: 36*wpp.dp2px
topBorder: true; bottomBorder: true
title: qsTr("Date/Time")
msecSinceEpoch: new Date().getTime()
//timeZoneId: "Asia/Hong_Kong"
onPicked: {
dateTime = dateTimePicked;
console.debug("picked=" + dateTimePicked);
}
}
Screenshot on Android and iOS:
see example: UsingDateTimeControl
To load phone contact, this library already support both Android and iOS.
#include <wpp/qt/AddressBookReader.h>
void SomeClass::someFunc()
{
wpp::qt::AddressBookReader& addressBookReader = wpp::qt::AddressBookReader::getInstance();
addressBookReader.asyncFetchAll(this, SLOT(onAddressBookLoaded(QList<QObject*>)));
}
void SomeClass::onAddressBookLoaded(QList<QObject*> contacts)
{
for ( QObject *obj : contacts )
{
wpp::qt::AddressBookContact *contact = dynamic_cast<wpp::qt::AddressBookContact *>(obj);
qDebug() << "first name: " << contact->getFirstName();
qDebug() << "last name: " << contact->getLastName();
qDebug() << "latin full name: " << contact->getLatinFullName();
qDebug() << "full name: " << contact->getFullName();
for ( QObject *phoneObj : contact->getPhones() )
{
wpp::qt::AddressBookContactPhone *phone = dynamic_cast<wpp::qt::AddressBookContactPhone *>(phoneObj);
qDebug() << "phone label: " << phone->getLabel();
qDebug() << "phone number: " << phone->getPhone();
}
for ( QObject *emailObj : contact->getEmails() )
{
wpp::qt::AddressBookContactEmail *email = dynamic_cast<wpp::qt::AddressBookContactEmail *>(emailObj);
qDebug() << "email label: " << email->getLabel();
qDebug() << "email address: " << email->getEmail();
}
}
}
This library provide a very easy way to use sqlite to persist your data. LocalStorage class will create the sqlite db file named "LocalStorage.db" under a suitable writable folder. The SQLite DB was initialized with a "DataMap" table for storing key-value records. As "key" is unique in the table, records with same "key" will be overwritten.
#include <wpp/qt/LocalStorage.h>
void someFunction()
{
LocalStorage& localStorage = LocalStorage::getInstance(); //get singleton
//persist data
QString userId = ....;
localStorage.setData("userId", userId);
//retrieve data
QString userId = localStorage.getData("userId");
}
Since it is just a key-value storage, you have to serialize/de-serialize the data yourself. For example, you can make use of QJsonObject/QJsonDocument to do this, and save the json string.
Qt provides QCookieJar for user to implement how to save the cookies and put the cookies to subsequent HTTP requests. In this library, the class CookieJar is for persisting the HTTP cookies into SQLite thru the class LocalStorage.
#include <wpp/qt/CookieJar.h>
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
manager->setCookieJar(new wpp::qt::CookieJar);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
Supported platforms: Android, iOS
In C++, e.g.:
#include <wpp/qt/System.h>
wpp::qt::QGuiApplication app(argc, argv);
app.registerApplePushNotificationService(); //necessary for iOS, this function does nothing on other platforms
int count = 7;
wpp::qt::System::getInstance().setAppIconUnreadCount(count);
In QML, e.g.:
onClicked: {
var count = 7;
wpp.setAppIconUnreadCount(count);
}
see example: UsingBadgeUnreadCount
QML:
import wpp.qt.SMS 2.0
SMS {
id: sms
onSent: {
console.log("SMS finished: sent");
}
onFailed: {
console.log("SMS finished: failed");
}
onCancelled: {
console.log("pressed cancel");
}
}
....
MouseArea {
onClicked: {
sms.phones = ["+852XXXXXXXX", "+86138XXXXXXXX"];
sms.msg = "Thanks for register, your code: 1234";
sms.open();
}
}
android/AndroidManifest.xml:
<uses-permission android:name="android.permission.SEND_SMS" />
see example: SendSMS
QML:
onClicked: {
wpp.dial("+86138XXXXXXXX");
//wpp.dial("+86138XXXXXXXX", true); means dial directly
}
Or C++:
#include <wpp/qt/Wpp.h>
wpp::qt::Wpp::getInstance().dial("+86138XXXXXXXX");
android/AndroidManifest.xml:
<uses-permission android:name="android.permission.CALL_PHONE" />
see example: DialPhone
onClicked: {
wpp.vibrate(1000);//for 1 second
}
Or C++
#include <wpp/qt/Wpp.h>
wpp::qt::Wpp::getInstance().vibrate(1000);
android/AndroidManifest.xml:
<uses-permission android:name="android.permission.VIBRATE" />
Since Apple doesn't allow control on the length of vibration, the milliseconds parameter of the function will be ignored on iOS platform.
see example: VibrateDevice
Create constants.json in any location (e.g. in root folder of the project):
{
"host": "www.myhost.com"
}
By using class "Constants", we can load it into our program and use those constants:
#include <wpp/qt/Constants.h>
int main()
{
QQGuiApplication app(argc, argv);
wpp::qt::Constants::load(":/constants.json"); //meaning qrc:/constants.json
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("constants", wpp::qt::Constants::getInstance() );
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Remember to add constants.json into the QRC file:
<RCC>
<qresource prefix="/">
...
<file>constants.json</file>
</qresource>
</RCC>
Then the constants can be used anywhere in the QML:
Image {
source: constants.host + "/img/happy.png";
}
Copyright 2015 Simon, Tse Chi Ming
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Deep thanks to these engineers, they have given me much help and contributed their code into my repository:
Currently I'm the only author of this project. You may contact me directly via github, or sending issues, or via these QQ groups:
- 345043587 Qt手机app开发Android
- 19346666 Qt5 for Android,iOS