2012年10月27日 (土)

QGraphicsSceneを複数のQGraphicsViewから参照したとき

一つのQGraphicsSceneを複数のQGraphicsViewを参照することができます。各々のViewに対して操作を行えば、下記のようなMulti Viewが簡単にできあがります。
M_view_sample01
さて、ここで、Sceneに対する操作を考えます。あるViewでは、グリッドを表示させますが、別のViewでは、グリッドを表示させないというのはどうすればいいでしょうか。
これは、各ViewのQGraphicsView::viewport()のPaintイベントを捕まえて、その中で、SceneのItemの表示を切り替えてあげればいいみたいです。
以下、実行結果です。
M_view_sample02
Multi Viewを考えたとき、Sceneに対する操作が障害となっており、各々のViewに各々のSceneを設定して、Sceneの同期機構を組もうかと思っていましたが、無事解決できました。

| | コメント (0) | トラックバック (0)

2012年9月23日 (日)

起動時にQDockWidgetの状態を復元する

QDockWidget::saveGeomerty() / QDockWidget::restoreGeometry()を使えば、ウインドウの位置/状態を簡単に前回終了状態に戻せるとのことなので、下記のようなコードを書いてみました。

/* --------------------------------------------------------------------------------------
 *  復元
 * -------------------------------------------------------------------------------------- */
void CMessageWindow::restoreSettings( QSettings* settings ) {
    QVariant v = settings->value( STATE_MESSAGE_WINDOW_GEOMETRY );
    if( !v.isNull() ) {
        m_dock_widget->restoreGeometry( v.toByteArray() );
    }
}
/* --------------------------------------------------------------------------------------
 *  保存
 * -------------------------------------------------------------------------------------- */
void CMessageWindow::saveSettings( QSettings* settings ) {
    settings->setValue( STATE_MESSAGE_WINDOW_GEOMETRY, m_dock_widget->saveGeometry() );
}

だけど、元に戻るのは、Dockがメインウインドウにくっついていないときだけで、半日ほど悩みました。

いろいろやった結果、メインウインドウ(QMainWindow)の状態を保持/復元してあげれば、くっついているDockにも適用されました。

| | コメント (0) | トラックバック (0)

2012年2月 4日 (土)

boost.contextを使ってみました。

以前、setjmp / longjmp関数を使用してコルーチンを実現してみましたが、スタックの関係上、コルーチン内部では、クラスインスタンスが使えないという問題がありました。

今回、boost.contextというものがあるのを知りましたので、試してみました。

以下、ソースになります。

#include <iostream>
#include <string>
#include <boost/context/all.hpp>
using namespace std;
namespace APP {
class CSub {
public:
    void print( int n ) {
        cout << "(" << m_name << ") -> " << n << endl;
    }
public:
    CSub( const string& name )
        : m_name( name )
    { cout << "CSub() : " << m_name << endl; }
    ~CSub() { cout << "~CSub() : " << m_name << endl; }
private:
    string m_name;
};
class CMain {
private:
    static void torampoline( CMain* self ) {
        self->function();
    }
private:
    void yield( void ) {
        m_context.suspend();
    }
    void yield_break( void ) {
        m_complete = true;
        m_context.suspend();
    }
private:
    void function( void ) {
        CSub sub_1( "_1" );
        for( int n=0; n<10; ++n ) {
            sub_1.print( n );
            yield();
        }
        for( int n=0; n<10; ++n ) {
            CSub( "_2" ).print( n );
            if( n != 5 ) {
                yield();
            }
            else {
                yield_break();
            }
        }
    }
public:
    int run( void ) {
        m_context = boost::contexts::context(
            &CMain::torampoline, this,
            boost::contexts::default_stacksize(),
            boost::contexts::no_stack_unwind, boost::contexts::return_to_caller );
        m_complete  = false;
        m_context.start();
        while( !m_complete && !m_context.is_complete() ) {
            m_context.resume();
        }
        if( !m_context.is_complete() ) {
            m_context.unwind_stack();
        }
        return 0;
    }
public:
    CMain() { ; }
    ~CMain() { ; }
private:
    boost::contexts::context m_context;
    bool                     m_complete;
};
}   /* APP */
static APP::CMain g_MainInst;
int main( int argc, char* argv[] ) {
    return g_MainInst.run();
}
実行結果です。
CSub() : _1
(_1) -> 0
(_1) -> 1
(_1) -> 2
(_1) -> 3
(_1) -> 4
(_1) -> 5
(_1) -> 6
(_1) -> 7
(_1) -> 8
(_1) -> 9
CSub() : _2
(_2) -> 0
~CSub() : _2
CSub() : _2
(_2) -> 1
~CSub() : _2
CSub() : _2
(_2) -> 2
~CSub() : _2
CSub() : _2
(_2) -> 3
~CSub() : _2
CSub() : _2
(_2) -> 4
~CSub() : _2
CSub() : _2
(_2) -> 5
~CSub() : _2
~CSub() : _1
すばらしい!!

| | コメント (0) | トラックバック (0)

2011年4月10日 (日)

コルーチン

setjmp / longjmp関数を使用したコルーチンをネットで調べ、自分が使いやすいように整形。

setjmp / longjmp関数はC++のオブジェクトに対応していないみたいですね。
まぁ、コルーチン化したい部分には、C++オブジェクトは置かないように注意すればいいかな。

コルーチンクラス

/* ----------------------------------------------------------------------------
 *  coroutine.hpp : コルーチン
 * ---------------------------------------------------------------------------- */
#pragma once

#define NOINLINE __declspec( noinline )

#include <setjmp.h>

namespace D1Z {
    class CCoroutine {
        enum {
            STKPAD = 1<<16,
            STKSAV = 1<<10
        };
    public:
        enum ERoutineState {
            rsSTART,        // 初期状態
            rsCONTINUE,      // 継続
            rsEND,      // 終了
        };
    public:
        /* 呼び出し */
        ERoutineState call( void ) {
            if( setjmp( m_PointA ) == 0 ) {
                if( m_state != rsSTART ) {
                    memcpy( const_cast<char*>( m_stkptrL ), m_Stack, m_stkptrH - m_stkptrL );
                    longjmp( m_PointB, 1 );
                }
                callDoProcess();
            }
            return m_state;
        }
    public:
        /* コンストラクタ */
        CCoroutine() : m_state( rsSTART ) { ; }
        /* デストラクタ */
        virtual ~CCoroutine() { ; }

    protected:
        /* 処理 */
        virtual void doProcess( void ) = 0;

    protected:
        /* 処理の呼び出し */
        NOINLINE void callDoProcess( void ) {
            char stktmp[STKPAD];
            m_stkptrH = stktmp;
            doProcess();
        }
        /* 制御を渡す */
        NOINLINE void yield( ERoutineState value ) {
            char curtmp;
            m_stkptrL = ( &curtmp ) - 16;
            if( setjmp( m_PointB ) == 0 ) {
                m_state = value;
                memcpy( m_Stack, const_cast<char*>( m_stkptrL ), m_stkptrH - m_stkptrL );
                longjmp( m_PointA, 1 );
            }
        }
    protected:
        CCoroutine( const CCoroutine& );
        CCoroutine& operator=( const CCoroutine& );

    protected:
        volatile ERoutineState m_state;
        volatile char*         m_stkptrH;
        volatile char*         m_stkptrL;
        jmp_buf                m_PointA;
        jmp_buf                m_PointB;
        char                   m_Stack[STKSAV];
    };
} // D1Z

使用サンプル

/* ----------------------------------------------------------------------------
 *  coroutine.cpp : コルーチン
 * ---------------------------------------------------------------------------- */
#include <iostream>
#include "coroutine.hpp"

using namespace std;

class CTest : public D1Z::CCoroutine {
protected:
    void doProcess( void ) {
        for( int n=0; n<10; ++n ) {
            yield( rsCONTINUE );
            cout << n << endl;
        }
        yield( rsEND );
    }
};

int main( int argc, char** argv ) {
    CTest test;
    D1Z::CCoroutine::ERoutineState state = D1Z::CCoroutine::rsSTART;
    while( state != D1Z::CCoroutine::rsEND ) {
        state = test.call();
        cout << "back!!" << endl;
    }
    return 0;
}

| | コメント (0) | トラックバック (0)

2010年10月23日 (土)

お気軽にXMLを扱ってみる 其の7

では、構文解析です。

構文は、字句解析して得た語句を走査して、状態遷移を切り替えながら解析していきます。

たとえば、最初の状態から、『<』をみつけたら、要素定義開始状態に移行して、『>』を見つけるまで、要素として情報として取得して、『>』をみつけたら、要素定義完了みたいな感じです。

ということで、要素関連で状態遷移図を設計したら、以下の様になりました。

1

状態を一つずつ解説しますと、

初期状態 初期、要素の中にいる状態
要素開始 要素名取得前状態
要素名 要素名取得状態
終了要素開始 要素名(終了)取得前状態
終了要素名 要素名(終了)取得状態
終了要素 要素終了前状態

となっています。

次回は、これらをコード化してみます。

| | コメント (0) | トラックバック (0)

2010年10月22日 (金)

お気軽にXMLを扱ってみる 其の6

では、字句解析を設計します。

区切り記号は、以下のワードにします。

『<』,『/』,『"』,『>』,『=』,『 』,『\r』,『\n』,『\t』

上記区切りの中でも、コントロールコードの『\r』,『\n』,『\t』につきましては、参照等を行わないので、構文解析を簡単にするためにも、無視してしまいます。

たとえば、以下の文章に適用すると、

<fruit>
  <apple price="100" />
  <glape price="200" />
</fruit>

以下のような字句に分解できます。

『<』『fruit』『>』『<』『apple』『 』『price』『=』『"』『100』・・・

XMLの文章をいきなり解析しようとすると、どうやってやろうかって感じですが、字句に分解されていれば、なんとか解析できそうですね。

それでは、コード化しましょう。

文章を渡すと、分解された語句の集まりが取得できるような関数になればいいので、以下のような関数を設計します。

void collectTokens( const char* _xml, list<string>& TokenList )

区切りを見つけるたびに、_xmlの文字を一文字ずつ取得して、その文字が区切りでないならば、語句として合成していきます。区切り文字がきたときには、その時点でできあがっている語句をリストに積んで、また、語句の合成を行っていきます。

以上の事をコードにすると、以下のような感じになりました。

void collectTokens( const char* _xml, list<string>& TokenList ) {
    string str;
    for( ;; ) {
        // 文章から一文字取得
        char c = *_xml++;
        if( c == NULL ) {
            // 走査完了
            if( !str.empty() ) {
                // 未処理ワードが残っているならば、リストに積む
                TokenList.push_back( str );
            }
            break;
        }
        else if( ( c == '<' ) || ( c == '/' ) || ( c == '"' ) ||
            ( c == '>' ) ||   ( c == '=' ) || ( c == ' ' ) ||
            ( c == '\n' ) || ( c == '\r' ) || ( c == '\t' ) )
        {
            if( !str.empty() ) {
                TokenList.push_back( str );
                str.clear();
            }
            if( ( c != '\n' ) && ( c != '\r' ) && ( c != '\t' ) )
            {
                // 上記の無視ワード以外
                str = c;
                TokenList.push_back( str );
                str.clear();
            }
        }
        else  {
            str += c;
        }
    }
}

つぎは、構文解析を考えていきます。

| | コメント (0) | トラックバック (0)

2010年9月 8日 (水)

const_castを実装中のプログラムの中に見た

XMLはちょっとお休みです。

最近、関わっているプロジェクトのプログラム中に『const_cast』を見かけました。

const_castとは、constやvolatile修飾子を外すために使用するのですが、そもそも何か訳があってconstにしているわけで、それを外す必要があるということは、設計上誤りがあったということになると思います。

そういう場合は、const_castを使う前に、constをつけた設計があっていたのかどうかを見るべきでしょう。

しかし、大規模な変更になってしまう場合には、リスクを考え、const_castを使うのも一つの手かと思います。

また、読み取り専用として使うのだけど、constが邪魔という場合があります。それが、今回、見かけたパターンになります。

リストの中に何かのクラスが入っています。そのクラスの中にリストがあって、そのリストの中にやはり、別のクラスが入っています。クラスには、自身が持つリストにアクセスするためのイテレータを返す関数が用意されています。

最初のリストをconst_iteratorで取得してしまった場合、リストにあるクラスからそのリストを参照するときには、const属性でアクセスしなければいけません。ただし、リストを参照する関数のなかにconst属性で取得する関数が用意されていないとき、関数からリストが参照できません。コンパイルエラーです。まぁ、const属性で取得できる関数を用意すればいいのですが、const属性を外しても、読み取り専用として使わないのであれば、const_castもありかなと思いました。

こんな場面に出くわすのはそうそうないと思いますが、const_castが出てくるときには、何らかのミスが生じていると思われます。上記の例も、const属性で取得する関数を用意し忘れたというミスになります。

| | コメント (0) | トラックバック (0)

2010年8月30日 (月)

お気軽にXMLを扱ってみる 其の5

前回までで、要素、属性を保持する入れ物ができましたので、XMLを解析してクラスに代入していきましょう。

まず、XMLを解析していくわけですが、順番としては、字句解析 -> 構文解析と進めます。

字句解析は、XMLの内容を最小単位に分解する作業になります。この最小単位をトークンと呼びます。今回、トークンとして表現するものは、以下のようにします。

・区切りとなる記号(『<』とか、『=』とか、『"』とかになります)

・要素名や属性名等々

字句解析が完了したら、構文解析に移ります。構文解析では、要素や属性等々を特定する作業になります。
ここでは、状態遷移を使うことで、解析を行っていこうと思います。

それでは、次回、字句解析を設計していきます。

| | コメント (0) | トラックバック (0)

2010年8月18日 (水)

お気軽にXMLを扱ってみる 其の4

ずいぶん、時間が経ってしまいましたが、お気軽にXMLを扱ってみたいと思います。

前回、属性のクラスを設計したので、今回は、要素のクラスを設計します。

要素は、名前と、要素、属性を保持することができます。名前は問題ないとして、要素と属性は、リストかなんかで管理しますか。

とりあえず、要素と属性のリストに関しては、生成、削除、参照の機能を用意します。

では、それらを踏まえて、ソースです。

class element {
public:
    /* 名前の取得 */
    std::string getName( void ) const {
        return m_Name;
    }
    /* 名前の設定 */
    void setName( std::string name ) {
        m_Name = name;
    }
    /* 要素生成 */
    element* createElement( void ) {
        element* e = new element;
        m_ElementList.push_back( e );
        return e;
    }
    /* 要素削除 */
    void deleteElement( element* e ) {
        std::list<element*>::iterator it =
            find( m_ElementList.begin(), m_ElementList.end(), e );
        if( it != m_ElementList.end() ) {
            delete *it;
            m_ElementList.erase( it );
        }
    }
    /* 要素参照 */
    std::list<element*>::iterator getElementBegin( void ) {
        return m_ElementList.begin();
    }
    std::list<element*>::iterator getElementEnd( void ) {
        return m_ElementList.end();
    }
    /* 属性生成 */
    attribute* createAttribute( void ) {
        attribute* a = new attribute;
        m_AttributeList.push_back( a );
        return a;
    }
    /* 属性削除 */
    void deleteAttribute( attribute* a ) {
        std::list<attribute*>::iterator it =
            find( m_AttributeList.begin(), m_AttributeList.end(), a );
        if( it != m_AttributeList.end() ) {
            delete *it;
            m_AttributeList.erase( it );
        }
    }
    /* 属性参照 */
    std::list<attribute*>::iterator getAttributeBegin( void ) {
        return m_AttributeList.begin();
    }
    std::list<attribute*>::iterator getAttributeEnd( void ) {
        return m_AttributeList.end();
    }
public:
    /* コンストラクタ */
    element() {;}
    /* デストラクタ */
    virtual ~element() {
        /* 属性の削除 */
        for( std::list<attribute*>::iterator it = m_AttributeList.begin();
             it != m_AttributeList.end(); it++ )
        {
            delete *it;
        }
        m_AttributeList.clear();
        /* 要素の削除 */
        for( std::list<element*>::iterator it = m_ElementList.begin();
             it != m_ElementList.end(); it++ )
        {
            delete *it;
        }
        m_ElementList.clear();
    }
protected:
    element( const element& );
    const element& operator=( const element& );
protected:
    /* 名前 */
    std::string m_Name;
    /* 要素リスト */
    std::list<element*> m_ElementList;
    /* 属性リスト */
    std::list<attribute*> m_AttributeList;
};

あんまり、解説するところはないですね。一応、参照につきましては、リストのイテレータを取得できるようにしました。

XMLを保持するための入れ物が完成しましたので、つぎは、XMLの読み取りを行っていきたいと思います。

| | コメント (0) | トラックバック (0)

2010年7月23日 (金)

お気軽にXMLを扱ってみる 其の3

ずいぶん、横道にそれていましたが、お気軽にXMLを扱ってみたいと思います。

まずは、属性クラスを設計します。

属性は、名前と値を管理するだけですので、さくっとできます。
以下、ソースリストです。

class attribute {
public:
    /* 名前の取得 */
    std::string getName( void ) const {
        return m_Name;
    }
    /* 値の取得 */
    std::string getValue( void ) const {
        return m_Value;
    }
    /* 名前の設定 */
    void setName( string name ) {
        m_Name = name;
    }
    /* 値の設定 */
    void setValue( string value ) {
        m_Value = value;
    }
public:
    /* コンストラクタ */
    attribute() {;}
    /* デストラクタ */
    virtual ~attribute() {;}
protected:
    attribute( const attribute& );
    const attribute& operator=( const attribute& );
};

解説するまでもないですね。

短いですが、今回はここまで。次回は、要素を管理するクラスを設計します。

| | コメント (0) | トラックバック (0)

«基底クラスのデストラクタにvirtualをつけない