Как реализовать универсальный класс кнопки, который выполняет различные функции при нажатии? - PullRequest
2 голосов
/ 02 мая 2011

Хорошо, я использую SDL с C ++ , и я начал работать над простым графическим интерфейсом для моего редактора карт.Из рабочего прототипа одной кнопки я написал универсальный класс GUIObject и универсальный класс Button.Но теперь у меня проблема: чтобы класс можно было многократно использовать, я должен иметь возможность установить для каждой кнопки, какую функцию он вызывает (например, Help! открывает окно справки, Open открывает диалоговое окно файла, это один и тот же класс Button, но они выполняют разные функции).

Теперь, я использовал некоторое время назад простую библиотеку для Ruby GUI, и ее синтаксис использовал то, что было записанотам как «оператор блока»:

Пример:

@login_button = Button.new(@window, 98, 131, Network_Data::LOGIN_BUTTON){execute_login}

Последняя часть {} была той, где вы поместили свою собственную функцию, а класс каким-то образом извлек ее и выполнил эту функцию, когдащелкнул.Я также слышал, что в Java есть нечто похожее:

onClick = new Function() { }

Итак, как бы мне поступить и реализовать это в программе на C ++ (которая использует библиотеки SDL).Я не хочу использовать какие-либо библиотеки GUI, потому что они кажутся слишком сложными, и мне будет трудно вставить эти библиотеки в мой уже созданный код.

Ответы [ 4 ]

2 голосов
/ 02 мая 2011

Вы можете сделать это, используя указатели на функции / функторы / методы. (функций должно быть достаточно и относительно просто)

Вы можете передать указатель на функцию обратного вызова в конструкторе кнопок.

Вот базовый пример использования указателей на функции.

class Button
{   
    typedef void(*eventFunction)();

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() () const
    {
        (m_funcPtr)();
    }
    ///< Invokes the registered function.

    private:
        eventFunction m_funcPtr;
};

И вы можете использовать его следующим образом:

void myFunction1() { /* do something */ }
void myFunction2() { /* do something else */ }

Button myButton1( &myFunction1 );
myButton1(); // invoke the function registered

Button myButton2( &myFunction2 );
myButton2(); // invoke the function registered

Вот более сложный пример использования шаблонов с переменным числом (C ++ 0x)

template < class Tr, class... Args >
class Button
{
    typedef Tr(*eventFunction)(Args...);

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() ( Args&... args ) const
    {
        (m_funcPtr)(args...);
    }
    ///< Invokes the registered function with the provided arguments.
    /// @param args The arguments to transmit to the invoked function.

    private:
        eventFunction m_funcPtr;
};

И вы можете использовать его следующим образом:

void myFunction1( int, double ) { /* do something */ }
void myFunction2() { /* do something */ }

Button<void, int, double> myButton1( &myFunction1 );
myButton1( 10, 20.5 );  // invoke the function registered with two arguments

Button<void> myButton2( &myFunction2 );
myButton1(); // invoke the function registered without argument

Шаблон Tr можно удалить и заменить на void, если вы никогда не планируете использовать другие функции, кроме функций, возвращающих void.

Таким образом Button<void, int, double> myButton1( &myFunction1 ); изменится на Button<int, double> myButton1( &myFunction1 );

и Button<void> myButton2( &myFunction2 ); изменится на Button<> myButton2( &myFunction2 );


Вы можете сделать почти то же самое с методами (вам нужно зарегистрировать указатель на экземпляр класса для вызова И указатель на метод) и с функторами (вам нужно зарегистрировать указатель на экземпляр функтора позвонить).


По вашему требованию здесь тот же класс, чтобы делать это с методами. Вам решать, как заставить ваш класс Button принимать функции, функторы и методы одновременно (подумайте виртуально ^^). Это версия C ++ 0x

template < class C, class Tr, class... Args >
class Button
{
    typedef Tr(C::*eventMethod)(Args...);
    ~Button(){}

    Button( C& target, eventMethod method ) : m_target(target), m_method(method) {}
    ///< A constructor.
    /// @param target The class member instance used to call the method.
    /// @param method The class member method pointer to register.

    bool operator () ( Args&... args ) const
    {
        (m_target.*(m_method))(args...);
    }
    ///< Invokes the registered method with the provided arguments.
    /// @param args The arguments to transmit to the invoked method.

    private:
        eventMethod m_method;
        C& m_target;
};

И вы можете использовать это так:

class MyClass
{
    void method1(){};
    void method2( int, double ){};
};

MyClass myC1;

Button<MyClass, void> myButton1( myC1, &MyClass::method1 );
myButton1(); // invoke the registered method using the registered class instance.

Button<MyClass, void, int, double> myButton2( myC1, &MyClass::method2 );
myButton2( 15, 170.5 ); // invoke the registered method using the registered class instance.

Если вам нужно вызвать его из класса, к которому принадлежит метод, используйте this в качестве целевого параметра вместо myC1.

1 голос
/ 02 мая 2011

Есть два подхода.Вы можете использовать полиморфизм во время выполнения (boost::function или std::tr1::function или std::function) или использовать шаблон.

0 голосов
/ 02 мая 2011

В c ++ он работает почти так же, как и в java:

onClick = new ClickFunction;

, но тогда вам, очевидно, потребуется следующее для определения необходимых классов:

template<class A, class B>
class Function { public: virtual B Map(A a) const=0; };

Function<int, int> *onClick;

class ClickFunction : public Function<int,int>
{
public:
    ClickFunction(MyObject &o) : o(o) { }
    int Map(int a) const { return 10; }
private:
    MyObject &o;
};
0 голосов
/ 02 мая 2011

Вам понадобится ваш класс кнопок для вызова функций, указанных его пользователями. Я рекомендую вам взглянуть на библиотеку Boost Signals . Эта библиотека реализует подход сигналов и слотов, имеет очень хорошую документацию и очень хорошо поддерживается. Кроме того, эти ссылки должны быть полезны, чтобы получить общее представление:

Надеюсь, это поможет. Удачи!

...