Showing posts with label custom. Show all posts
Showing posts with label custom. Show all posts

Sunday, January 22, 2012

Animating element on curve in QML

I got comment on my post "Animating object along with curve in Qt using QPainterPath" that how similar thing can be achieved in QML. 

I tried to answer that comment in this post.

Originally I intend to achieve it using only QML but did not find any easy way. So I created a custom QDeclarativeItem that uses QPainterPath and implement required functionality.

Well code it quite simple, but it required little hack so that I can use some QML element like PathQuad or PathLine into my c++ code. 

So let's now move to code, I will try to explain as much as I can.

So let's begin with QML, what I want my QML code to look like. My QML has one rect item which act as circle and my custom QML element which act as curve.

I want my curve element to be drawn on screen and I also want circle to follow the curve. In code path is list which contain different kind of path element.

I was not able to intrepreat QML Path element into C++ class , so I end up creating my own path property. My curve also has pointAtPercentage function which provide point at certain progress on curve, which I use with timer to make dot follow that curve.
import QtQuick 1.0
import MyCurve 1.0

Rectangle {
    width: 500; height: 500

    Rectangle {
        id:dot
        width:10;height:10; radius: 5
        color:"steelblue"
    }

    MyCurve {
        id:curve
        path:[
                PathQuad { x: 400; y: 400; controlX: 100; controlY: 600 },
                PathLine{x: 120; y: 120},
                PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 },
                PathQuad { x: 120; y: 100; controlX: -20; controlY: 75 }
            ]
    }

    Timer{
        property variant progress:0;
        interval: 100; running: true; repeat: true
        onTriggered: {
            progress += 0.005;
            if( progress > 1 ) {
                progress  = 0;
            }
            var point = curve.pointAtPercent(progress);
            dot.x = point.x
            dot.y = point.y
        }
    }
}
Now lets look at MyCurve, custom QDeclaraiveItem class, which holds the QPainterPath. MyCurve is quite simple class if we don't include code to interpret PathQuad or PathLine QML element. We could have populated QPainterPath directly in c++ class. But I choose not to do so, to make it more reusable and also that I can try my hands on QDeclarativeListProperty and can show how we can read property of QML element.

#ifndef MYCURVE_H
#define MYCURVE_H

#include <QDeclarativeItem>
#include <QPainterPath>
#include <QGraphicspathItem>
#include <QDeclarativeListProperty>

class MyCurve : public QDeclarativeItem
{
    Q_OBJECT
    Q_PROPERTY(QDeclarativeListProperty<QObject> path READ path)
public:
    explicit MyCurve(QDeclarativeItem *parent = 0);

public slots:
    QPointF pointAtPercent(double Percent);

public:
    QDeclarativeListProperty<QObject> path();

private:
    static void appendItem(QDeclarativeListProperty<QObject> *list,
         QObject *pathItem);

    void appendPathQuad( QObject* pathQual);
    void appendPathLine( QObject* pathLine);

private:    
    QGraphicsPathItem* _path;
    QPainterPath _painterPath;
};
#endif // MYCURVE_H
Only Interesting about header is appendItem static method, which I will explain after showing its implementation. So here is implementation of header.
#include "mycurve.h"
#include <QDebug>
#include <QDeclarativeItem>
#include <QPen>

MyCurve::MyCurve(QDeclarativeItem *parent) :
    QDeclarativeItem(parent),_path(0),_startX(0),_startY(0)
{
    _path = new QGraphicsPathItem(this);
    _path->setPen(QPen(Qt::blue));
}

QPointF MyCurve::pointAtPercent(double Percent) {
    return _painterPath.pointAtPercent(Percent);
}

QDeclarativeListProperty<QObject> MyCurve::path()
{
    return QDeclarativeListProperty<QObject>(this,0,&MyCurve::appendItem);
}

void MyCurve::appendItem(QDeclarativeListProperty<QObject> *list, 
    QObject *pathItem)
{
    MyCurve *myCurve = qobject_cast<MyCurve *>(list->object);
    if (myCurve) {
         if( QString(pathItem->metaObject()->className())
                == QString("QDeclarativePathQuad") ) {
             myCurve->appendPathQuad(pathItem);
         } else if (QString(pathItem->metaObject()->className()) 
               == QString("QDeclarativePathLine")){
             myCurve->appendPathLine(pathItem);
         }
       }
}

void MyCurve::appendPathQuad( QObject* pathQuad) {
    double x = pathQuad->property("x").toDouble();
    double y = pathQuad->property("y").toDouble();
    double controlX = pathQuad->property("controlX").toDouble();
    double controlY = pathQuad->property("controlY").toDouble();

    _painterPath.quadTo(controlX,controlY,x,y);
    _path->setPath(_painterPath);
}

void MyCurve::appendPathLine( QObject* pathLine) {
    double x = pathLine->property("x").toDouble();
    double y = pathLine->property("y").toDouble();
    _painterPath.lineTo(x,y);
    _path->setPath(_painterPath);
}
There are quite a few interesting things here.

Usage of QDeclarativeListProperty(I am using it for first time), QDeclarativeListProperty is used to create list of property in QML. To operate on QDeclarativeListProperty, we need to provide static method which take pointer of list object and list item. appendItem is similar static method which add path element to our QPainterPath. To know more how other operation can be implemented on QDeclarativeListProperty, please visit this or this link.

In above code I am using metaObject to identify which QML Path element is being added by appendItem ( which is kind of dirty hack ) and after identifying element I am using property method to get its value and finally using those value to call appropriate QPainterPath method.

And finally here is how it looks,

Saturday, December 24, 2011

Creating custom list view and adaptor in Android

I recently got new toy for myself a imuz TX72 7" android tablet to satisfy my book reading hobby.

I will write a seperate post for tablet, but as I got this tablet, I thought to port my Audiobook reader app to Android so I can here my audiobook on this tablet as well.

As I started to port, First thing I need to learn is how to create view and specially list view. I required to create custom Listview so I can show thumnail and other audiobook details.

So to start, First you need to create is activity. I also wanted to put button on main activity so I did not derived my activity from ListActivity but derived it from Activity. For new people to android like me, Activity is something that we see on screen , a view.

My main activity goes like below.

    
public class AudiobookReaderActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       
       setContentView(R.layout.main);
       
       ListView list = (ListView) findViewById(R.id.BookList);
       list.setAdapter( new BookListAdaptor(this));  
    }  
}

Now we have list view, but how invidiual list item looks is decided by item's xml file, which provide layout and views details to be drawn as item.

Following is my list item, with one image and two text view.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=
       "http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
    
    <ImageView
        android:id="@+id/AlbumArt"        
      android:layout_width="55dip"
  android:layout_height="55dip"/>
    
 <LinearLayout xmlns:android=
              "http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical" >    
 
  <TextView xmlns:android=
                "http://schemas.android.com/apk/res/android"
      android:id="@+id/BookName"     
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textStyle="bold" 
      android:padding="3dp"
      android:textColor="#FFFF00" 
      android:textSize="16sp" >
  </TextView>
  
  <TextView xmlns:android=
                      "http://schemas.android.com/apk/res/android"
      android:id="@+id/TrackName"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:padding="3dp" >
  </TextView>
  
 </LinearLayout>
</LinearLayout>

Now look is set for list view, now we need to provide data to list, by creating adaptor class. Adaptor class act as model and provide data to list view.

Following is my custom list adaptor class to support Book data structure. Here getView method is importat as it uses our xml file to create liste item and decide our list view's look.

import java.util.ArrayList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.content.Context;

public class BookListAdaptor extends BaseAdapter {
 
 public BookListAdaptor( Context context) {
  mContext = context;
  
  //load book list from file or database
  mBookList = new ArrayList();

  mBookList.add(new AudioBook("Harry Potter"));
  mBookList.add(new AudioBook("The Hobbit"));
 }

 @Override
 public int getCount() { 
  return mBookList.size();
 }

 @Override
 public Object getItem(int position) {
  return mBookList.get(position);
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {  
  
        if (convertView == null) {
   LayoutInflater layoutInflator = 
                             LayoutInflater.from(mContext);
   convertView = layoutInflator.
                             inflate(R.layout.list_item, null);
        }
     
        TextView bookName = (TextView) convertView.
                                      findViewById(R.id.BookName);
        TextView trackName = (TextView)convertView.
                                      findViewById(R.id.TrackName);
        ImageView imageView = (ImageView)convertView.
                                      findViewById(R.id.AlbumArt);
  
  
  AudioBook book = mBookList.get(position);
  bookName.setText(book.bookName());
  trackName.setText(book.bookName());
  imageView.setBackgroundResource(android.R.drawable.btn_plus);
  
  return convertView;
  
 }

    private Context mContext;
    private ArrayList mBookList;
}

Book class looks something like following.

package com.niku;

import java.util.ArrayList;


public class AudioBook {
 
    public AudioBook( String aBookName) {
 mBookName = aBookName;
    }
 
    public String bookName() 
    {
        return mBookName;
    }

    private String mBookName;
}  
That's all. Final list view will looks like followign snap. Thanks fo reading my blog.






Saturday, November 26, 2011

Using custom Font with Qt

Recently in one of my application I wanted to use custom font and in little time I figured out that Qt has vary nice support to use custom font with both Qt C++ and QML.

I used following code in my application. Here is output of below code.


Following is code to use custom font with Qt C++.
#include <QPainter>
#include <QFontDatabase>

MyWidget::MyWidget(QWidget *parent) :
    QWidget(parent)
{
    QFontDatabase::addApplicationFont("sandsped.ttf");
}

void MyWidget::paintEvent(QPaintEvent */*event*/) {
    QPainter painter(this);
    painter.drawText(10,20,"Default Font");
    painter.setFont(QFont("Sandoval Speed"));
    painter.drawText(10,50,"Custom Font");
}


Following is code to use custom font with QML.
    FontLoader { id: myFont; source: "sandsped.ttf" }

    Column {
        spacing: 30
        anchors.horizontalCenter: parent.horizontalCenter
        Text {
            text: "Default Font"
        }

        Text {
            font.family: myFont.name
            text: "Custom Font";
        }
    }