sample/qt_ui/main.cpp

/******************************************************************************
 *
 * Qt 3 Ping & Pong Example.
 *
 * В данном примере показано каким образом агенты SObjectizer могут
 * взаимодействовать с GUI виджетами.
 *
 * При старте примера появляются два окна, в каждом из которых можно установить
 * скорость отправки очередного сообщения со случайным числом посредством
 * разных виджетов. При получении сообщения со случайным числом каждое окно
 * отображает его в ползунке прогресса.
 *
 * У каждого из окон есть кнопка "Ping" и "Pong" соответственно, нажатие на
 * которую инициирует отправку соощения окну-соседу, после чего кнопка будет
 * отключена. Окно-сосед, получив такое сообщение, включает свою кнопку, что
 * дает возможность уже этому окну отправить сообщение окну-соседу. Получение
 * этих сообщений также отображается в строке состояния.
 *
 *****************************************************************************/


// ACE include.
#include <ace/OS_main.h>
#include <ace/Thread_Manager.h>

// Qt include.
#include <qrect.h>
#include <qwidget.h>
#include <qtimer.h>
#include <qlayout.h>
#include <qpushbutton.h>
#include <qprogressbar.h>
#include <qdial.h>
#include <qtooltip.h>
#include <qapplication.h>
#include <qstatusbar.h>
#include <qslider.h>

// SObjectizer include.
#include <so_4/rt/h/rt.hpp>
#include <so_4/api/h/api.hpp>

#include <so_4/timer_thread/simple/h/pub.hpp>
#include <so_4/disp/active_obj/h/pub.hpp>
#include <so_4/disp/qt_ui/h/pub.hpp>

// STL include.
#include <string>
#include <cstdlib>

const int c_max = 99;

//
// sobj_thread_t
//

class sobj_thread_t
{
private :
  so_4::rt::agent_coop_t * m_start_coop;

public :
  sobj_thread_t( so_4::rt::agent_coop_t * start_coop = 0 )
  : m_start_coop( start_coop )
  {}

  virtual ~sobj_thread_t()
  {}

  virtual void
  start()
  {
    if( -1 == ACE_Thread_Manager::instance()->spawn(
      entry_point,
      this ) )
    {
      ACE_ERROR(( LM_ERROR, "unable to spawn work thread\n" ));
    }
  }

  virtual void
  wait()
  {
    ACE_Thread_Manager::instance()->wait(); 
  }

protected :
  virtual void
  body()
  {
    so_4::ret_code_t rc = so_4::api::start(
      so_4::disp::qt_ui::create_disp(
        so_4::disp::active_obj::create_disp(
          so_4::timer_thread::simple::create_timer_thread(),
          so_4::auto_destroy_timer ) ),
      so_4::auto_destroy_disp, 
      m_start_coop
      );
    if( rc )
    {
      std::cerr << "start: " << rc << std::endl;
    }
  }

  static ACE_THR_FUNC_RETURN
  entry_point( void * self_object )
  {
    sobj_thread_t * sobj_thread = ACE_reinterpret_cast(
        sobj_thread_t *,
        self_object );
    sobj_thread->body();

    return 0;
  }
};// class sobj_thread_t


//
// a_msg_owner_t
//

// Класс агента, единственной задачей которого является
// владение сообщениями и рассылка периодического сообщения.
class a_msg_owner_t
  : public so_4::rt::agent_t
{
  typedef so_4::rt::agent_t base_type_t;

public :
  a_msg_owner_t()
    : base_type_t( agent_name() )
    ,   m_interval( 1000 )
  {};

  virtual ~a_msg_owner_t()
  {};

  virtual const char *
  so_query_type() const;

  virtual void
  so_on_subscription()
  {
    so_subscribe( "evt_start",
      so_4::rt::sobjectizer_agent_name(), "msg_start" );

    so_subscribe( "evt_random", "msg_random" );
    so_subscribe( "evt_interval", "msg_interval" );
  };

  static std::string
  agent_name()
  {
    return "a_msg_owner";
  };

  struct msg_interval {

    int m_interval;
    std::string m_sender;

    msg_interval() {};
    msg_interval( int interval,
      const std::string & sender )
      : m_interval( interval )
      , m_sender( sender )
    {}

    static bool
    check( const msg_interval * msg )
    {
      return ( 0 != msg &&
        msg->m_sender.length() );
    }
  };// struct msg_interval

  struct msg_random {

    int m_random;

    msg_random()
      : m_random( ( double( rand() ) / RAND_MAX ) * c_max )
    {}
  };// struct msg_random

  struct msg_ping_pong {
    
    std::string m_sender;
    std::string m_value;

    msg_ping_pong() {}
    msg_ping_pong( const std::string & sender,
      const std::string & value )
      : m_sender( sender )
      , m_value( value )
    {}

    static bool
    check( const msg_ping_pong * msg )
    {
      return ( 0 != msg &&
        msg->m_sender.length() &&
        msg->m_value.length() );
    }
  };// struct msg_ping_pong

  void evt_start()
  {
    // Запускаем сообщение msg_random.
    so_4::api::send_msg_safely(
      agent_name(),
      "msg_random",
      new msg_random() );
  };

  void evt_random( const msg_random & msg )
  {
    so_4::api::send_msg_safely(
      agent_name(),
      "msg_random",
      new msg_random(),
      "", m_interval, 0 );
  };

  void evt_interval( const msg_interval & msg )
  {
    m_interval = msg.m_interval;
  };

private:
  int m_interval;
};// class a_msg_owner_t

SOL4_CLASS_START( a_msg_owner_t )

  SOL4_MSG_START( msg_shutdown, a_msg_owner_t::msg_ping_pong )
    SOL4_MSG_FIELD( m_sender )
    SOL4_MSG_FIELD( m_value )

    SOL4_MSG_CHECKER( a_msg_owner_t::msg_ping_pong::check )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_ping_pong, a_msg_owner_t::msg_ping_pong )
    SOL4_MSG_FIELD( m_sender )

    SOL4_MSG_CHECKER( a_msg_owner_t::msg_ping_pong::check )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_random, a_msg_owner_t::msg_random )
    SOL4_MSG_FIELD( m_random )
  SOL4_MSG_FINISH()

  SOL4_MSG_START( msg_interval, a_msg_owner_t::msg_interval )
    SOL4_MSG_FIELD( m_interval )
    SOL4_MSG_FIELD( m_sender )

    SOL4_MSG_CHECKER( a_msg_owner_t::msg_interval::check )
  SOL4_MSG_FINISH()

  SOL4_EVENT( evt_start )

  SOL4_EVENT_STC(
    evt_random,
    a_msg_owner_t::msg_random )

  SOL4_EVENT_STC(
    evt_interval,
    a_msg_owner_t::msg_interval )

  SOL4_STATE_START( st_initial )
    SOL4_STATE_EVENT( evt_start )
    SOL4_STATE_EVENT( evt_random )
    SOL4_STATE_EVENT( evt_interval )
  SOL4_STATE_FINISH()

SOL4_CLASS_FINISH( /* a_msg_owner_t */ )


//
// a_qt_widget_t
//

class a_qt_widget_t
  : public QWidget
  , public so_4::rt::agent_t
{
  typedef so_4::rt::agent_t base_type_t;

  Q_OBJECT

protected:
  a_qt_widget_t( const std::string & agent_name,
    const std::string & ping_or_pong,
    QWidget * parent = 0 )
    : base_type_t( agent_name )
    , QWidget( parent, agent_name.c_str() )
    , m_button( 0 )
    , m_label( 0 )
    , m_status_bar( 0 )
  {
    // Должны работать на GUI-нити.
    so_add_traits( so_4::disp::qt_ui::query_gui_thread_traits() );
  };

public:
  virtual ~a_qt_widget_t()
  {};

  virtual const char *
  so_query_type() const;

  virtual void
  so_on_subscription()
  {
    // Подписываемся на сообщения a_msg_owner_t.
    so_subscribe( "evt_ping_pong",
      a_msg_owner_t::agent_name(),
      "msg_ping_pong" );

    so_subscribe( "evt_shutdown",
      a_msg_owner_t::agent_name(),
      "msg_shutdown" );

    so_subscribe( "evt_random",
      a_msg_owner_t::agent_name(),
      "msg_random" );

    so_subscribe( "evt_interval",
      a_msg_owner_t::agent_name(),
      "msg_interval" );
  };

  // Разрешает или запрещает использование кнопки.
  void
  evt_ping_pong( const a_msg_owner_t::msg_ping_pong & msg )
  {
    // Если это не наше сообщение, то включаем кнопку
    // иначе кнопку отключаем.
    if( so_query_name() != msg.m_sender )
    {
      m_button->setEnabled( TRUE );
      m_button->setFocus();
      setActiveWindow();
      if( "ping" == msg.m_value )
        m_status_bar->message( "Ping was received...", 2000 );
      else
        m_status_bar->message( "Pong was received...", 2000 );
    }
    else
      m_button->setEnabled( FALSE );
  };

  // Событие на сообщение со случайным числом.
  void
  evt_random( const a_msg_owner_t::msg_random & msg )
  {
    m_label->setProgress( msg.m_random );
  };

  // Событие на сообщение msg_interval.
  virtual void
  evt_interval( const a_msg_owner_t::msg_interval & msg )
  {};

  // Закрывает виджет (окно).
  void
  evt_shutdown( const a_msg_owner_t::msg_ping_pong & msg )
  {
    if( msg.m_sender != so_query_name() )
      // Инициируем закрытие виджета.
      close();
  };

public slots:
  void
  slot_ping_pong()
  {
    // Отсылаем сообщение a_msg_owner_t::msg_ping_pong.
    so_4::api::send_msg_safely(
      a_msg_owner_t::agent_name(), "msg_ping_pong",
      new a_msg_owner_t::msg_ping_pong( so_query_name(),
        m_button->text().ascii() ) );
  };
  void
  slot_interval( int interval )
  {
    so_4::api::send_msg_safely(
      a_msg_owner_t::agent_name(), "msg_interval",
      new a_msg_owner_t::msg_interval( interval, so_query_name() ) );
  };

protected:
  virtual void
  closeEvent( QCloseEvent * e )
  {
    if( !isHidden() )
    {
      // Говорим всем заинтересованным виджетам, что нужно закрыться.
      so_4::api::send_msg_safely(
        a_msg_owner_t::agent_name(), "msg_shutdown",
        new a_msg_owner_t::msg_ping_pong( so_query_name(), "shutdown" ) );

      e->accept();
    }
  };

  QPushButton * m_button;
  QProgressBar * m_label;
  // StatusBar.
  QStatusBar * m_status_bar;
};// class a_qt_widget_t

SOL4_CLASS_START( a_qt_widget_t )

  SOL4_EVENT_STC(
    evt_ping_pong,
    a_msg_owner_t::msg_ping_pong )

  SOL4_EVENT_STC(
    evt_random,
    a_msg_owner_t::msg_random )

  SOL4_EVENT_STC(
    evt_interval,
    a_msg_owner_t::msg_interval )

  SOL4_EVENT_STC(
    evt_shutdown,
    a_msg_owner_t::msg_ping_pong )

  SOL4_STATE_START( st_initial )
    SOL4_STATE_EVENT( evt_ping_pong )
    SOL4_STATE_EVENT( evt_shutdown )
    SOL4_STATE_EVENT( evt_random )
    SOL4_STATE_EVENT( evt_interval )
  SOL4_STATE_FINISH()

SOL4_CLASS_FINISH( /* a_qt_widget_t */ )

#include "moc/main.moc"


//
// slider_traits_t
//

struct slider_traits_t
{
  typedef QSlider control_type_t;

  static control_type_t *
  make( QWidget * parent )
  {
    control_type_t * r = new control_type_t( parent );
    r->setOrientation( Qt::Horizontal );
    return r;
  }
};// struct slider_traits_t


//
// struct dial_traits_t
//

struct dial_traits_t
{
  typedef QDial control_type_t;

  static control_type_t *
  make( QWidget * parent )
  {
    control_type_t * r = new control_type_t( parent );
    return r;
  }
};// struct dial_traits_t


//
// a_qt_widget_impl_t
//

// Реализация виджета.
template< class TRAITS >
class a_qt_widget_impl_t
  : public a_qt_widget_t
{
public:
  a_qt_widget_impl_t( const std::string & agent_name,
    const std::string & ping_or_pong,
    QWidget * parent = 0 )
    : a_qt_widget_t( agent_name, ping_or_pong, parent )
    , m_dial( 0 )
  {
    QVBoxLayout * layout = new QVBoxLayout( this );
    m_dial = TRAITS::make( this );
    m_dial->setMinValue( 50 );
    m_dial->setMaxValue( 3000 );
    m_dial->setLineStep( 50 );
    m_dial->setPageStep( 100 );
    m_dial->setValue( 1000 );
    QToolTip::add( m_dial, QString::fromLocal8Bit(
      "Регулятор скорости генерации случайных чисел" ) );
    layout->addWidget( m_dial );
    m_button = new QPushButton( this, "Ping Pong Button" );
    if( "pong" == ping_or_pong )
      m_button->setEnabled( FALSE );
    m_button->setText( ping_or_pong.c_str() );
    m_button->setFocus();
    m_button->setAutoDefault( TRUE );
    QToolTip::add( m_button, QString::fromLocal8Bit(
      "Отсылает сообщение msg_ping_pong другому окошку" ) );
    layout->addWidget( m_button );
    

    m_label = new QProgressBar( this );
    m_label->setTotalSteps( 99 );
    m_label->setPercentageVisible( TRUE );
    QToolTip::add( m_label, QString::fromLocal8Bit(
      "Отображает случайные числа от 0 до 99 в виде QProgressBar-а" ) );
    layout->addWidget( m_label );

    m_status_bar = new QStatusBar( this );
    layout->addWidget( m_status_bar );

    connect( m_button, SIGNAL( clicked() ), this, SLOT( slot_ping_pong() ) );
    connect( m_dial, SIGNAL( valueChanged( int ) ), this,
        SLOT( slot_interval( int ) ) );
  };

  virtual ~a_qt_widget_impl_t()
  {};

  // Событие на сообщение msg_interval.
  virtual void
  evt_interval( const a_msg_owner_t::msg_interval & msg )
  {
    if( msg.m_sender != so_query_name() )
      if( m_dial )
        m_dial->setValue( msg.m_interval );
  };

private:
  typename TRAITS::control_type_t * m_dial;
};// class a_qt_widget_impl_t

int main( int argc, char ** argv )
{
  QApplication app( argc, argv );
  app.connect( &app, SIGNAL( lastWindowClosed() ),
    &app, SLOT( quit() ) );

  // Создаем владельца сообщений.
  a_msg_owner_t a_msg_owner;
  // Создаем агенты-виджеты.
  a_qt_widget_impl_t< dial_traits_t > a_widget_1( "widget_1", "ping" );
  a_qt_widget_impl_t< slider_traits_t > a_widget_2( "widget_2", "pong" );

  // Кооперация примера.
  so_4::rt::agent_t * g_coop_agents[] = {
    &a_msg_owner,
    &a_widget_1,
    &a_widget_2,
  };

  so_4::rt::agent_coop_t  a_coop( "a_coop", g_coop_agents,
    sizeof( g_coop_agents ) / sizeof( g_coop_agents[ 0 ] ) );

  // Запускаем SObjectizer.
  sobj_thread_t sobj_thread( &a_coop );
  sobj_thread.start();

  app.setMainWidget( &a_widget_1 );
  a_widget_1.show();
  
  a_widget_2.show();
  a_widget_2.move( a_widget_1.pos().x() + a_widget_1.frameSize().width(),
    a_widget_1.pos().y() + a_widget_1.frameSize().height() );

  int q_result = app.exec();

  // Завершаем SObjectizer.
  so_4::api::send_msg( so_4::rt::sobjectizer_agent_name(),
    "msg_normal_shutdown" );

  so_4::api::shutdown();
  // Ожидаем завершения SObjectizer.
  sobj_thread.wait();

  return q_result; 
}

Документация по SObjectizer v.4.4 'Тебуломста'. Последние изменения: Thu Sep 18 10:26:45 2008. Создано системой  doxygen1.5.6 Intervale SourceForge.net Logo