修正Gtk++ WidgetConnector模板,适配带参及非void返回的回调函数
问题:Gtk++ WidgetConnector类如何支持带参数或非void返回值的回调函数
我需要实现一个类来管理Gtk++控件与回调函数的sigc::connection连接,实现延迟建立连接、批量断开/重连等功能。目前编写的WidgetConnector类中,getConnectedWidget模板函数仅支持无返回值且无参数的回调,当使用带参数(如void onButtonPressed(int x))或返回非void类型(如bool onButtonPressed())的回调时无法编译。请问需要如何修正该类及模板函数以解决此问题?
原代码示例
原Gtk++连接代码
Gtk::Button* button; builder->get_widget("fooButton", button); button->signal_clicked().connect(sigc::mem_fun(this, &ThisClassName::onButtonPressed)); // ThisClassName为当前类名
原WidgetConnector类代码
#ifndef WIDGETCONNECTOR_H #define WIDGETCONNECTOR_H #include <functional> #include <map> #include <algorithm> #include <cassert> #include <iostream> #include <sigc++/sigc++.h> #include <gtkmm.h> class WidgetConnector { public: using ConnectionProxy = std::function<sigc::connection()>; WidgetConnector(const Glib::RefPtr<Gtk::Builder>& builder) : mBuilder(builder) { assert(mBuilder); } virtual ~WidgetConnector() {} template<class T_Widget, auto S, typename... T> T_Widget& getConnectedWidget(std::string label, const typename sigc::slot< void(T...)>& slot) { T_Widget* w = nullptr; mBuilder->get_widget(label, w); assert(w); using signal_type = decltype(std::mem_fun(S)); const signal_type& widgetSignal = std::mem_fun(S); // 延迟连接,避免构建期间触发回调 mConnectRefList[label] = std::make_unique<ConnectionProxy>([w, slot, widgetSignal]() { return widgetSignal(w).connect(slot); }); return *w; } void connectWidgetsWithSignals() { for (auto const& [label, functor] : mConnectRefList) { connectWidgetWithSignal(label); } } void connectWidgetWithSignal(std::string label, bool doOverwrite = false) { auto connectionRef = (*mConnectRefList.at(label))(); addDisconnectRef(label, connectionRef, doOverwrite); } template<typename CONN> void addDisconnectRef(std::string label, CONN connectRef, bool doOverwrite = false) { try { if (mDisconnectRefList[label].connected() && doOverwrite) mDisconnectRefList[label].disconnect(); if (! mDisconnectRefList[label].connected()) { mDisconnectRefList[label] = connectRef; mIsConnected = true; } } catch (...) { std::cerr << "No connection set up for " << label << std::endl; } } void disconnectWidgetsWithSignals() { for (auto const& [label, functor] : mDisconnectRefList) { disconnectWidgetWithSignal(label); } mIsConnected = false; } void disconnectWidgetWithSignal(std::string label) { try { if (mDisconnectRefList.at(label).connected()) mDisconnectRefList.at(label).disconnect(); } catch (...) { std::cerr << "No connection set up for " << label << std::endl; } } private: const Glib::RefPtr<Gtk::Builder> mBuilder; std::map<std::string, std::unique_ptr<ConnectionProxy>> mConnectRefList; std::map<std::string, sigc::connection> mDisconnectRefList; bool mIsConnected = false; // 原代码缺失此成员,需补充 }; #endif // WIDGETCONNECTOR_H
原使用示例
auto& w = getConnectedWidget<Gtk::Button, &Gtk::Button::signal_clicked>("fooButton", static_cast<const typename sigc::slot<void()>>(sigc::mem_fun(this, &ThisClassName::onButtonPressed))); // 对w进行操作,如w.set_sensitive() connectWidgetsWithSignals(); // 实际建立连接
解决方案
核心修改点
- 泛化槽类型:将
getConnectedWidget的槽参数从固定的sigc::slot<void(T...)>改为模板参数,让编译器自动推导回调的返回值和参数类型,不再限制为void无参。 - 简化信号调用:直接通过成员函数指针获取信号,无需
std::mem_fun,使用(widget->*SignalMember)()直接调用信号成员函数得到信号对象。 - 移除冗余类型转换:使用示例中不需要手动
static_cast槽类型,模板会自动匹配回调的签名。
修改后的完整WidgetConnector类代码
#ifndef WIDGETCONNECTOR_H #define WIDGETCONNECTOR_H #include <functional> #include <map> #include <cassert> #include <iostream> #include <sigc++/sigc++.h> #include <gtkmm.h> class WidgetConnector { public: using ConnectionProxy = std::function<sigc::connection()>; WidgetConnector(const Glib::RefPtr<Gtk::Builder>& builder) : mBuilder(builder), mIsConnected(false) { assert(mBuilder); } virtual ~WidgetConnector() {} // 泛化模板,支持任意签名的槽 template<class T_Widget, auto SignalMember, typename SlotType> T_Widget& getConnectedWidget(std::string label, const SlotType& slot) { T_Widget* widget = nullptr; mBuilder->get_widget(label, widget); assert(widget && "Widget not found in builder"); // 存储延迟连接的lambda,直接调用信号成员函数 mConnectRefList[label] = std::make_unique<ConnectionProxy>( [widget, slot]() { return (widget->*SignalMember)().connect(slot); } ); return *widget; } void connectWidgetsWithSignals() { for (const auto& [label, proxy] : mConnectRefList) { connectWidgetWithSignal(label); } } void connectWidgetWithSignal(const std::string& label, bool doOverwrite = false) { try { auto conn = (*mConnectRefList.at(label))(); addDisconnectRef(label, conn, doOverwrite); } catch (const std::out_of_range&) { std::cerr << "No connection proxy found for widget: " << label << std::endl; } } template<typename ConnType> void addDisconnectRef(const std::string& label, ConnType conn, bool doOverwrite = false) { try { auto& existing_conn = mDisconnectRefList[label]; if (existing_conn.connected() && doOverwrite) { existing_conn.disconnect(); } if (!existing_conn.connected()) { existing_conn = std::move(conn); mIsConnected = true; } } catch (const std::out_of_range&) { mDisconnectRefList[label] = std::move(conn); mIsConnected = true; } } void disconnectWidgetsWithSignals() { for (auto& [label, conn] : mDisconnectRefList) { if (conn.connected()) { conn.disconnect(); } } mIsConnected = false; } void disconnectWidgetWithSignal(const std::string& label) { try { auto& conn = mDisconnectRefList.at(label); if (conn.connected()) { conn.disconnect(); } } catch (const std::out_of_range&) { std::cerr << "No active connection found for widget: " << label << std::endl; } } bool isConnected() const { return mIsConnected; } private: const Glib::RefPtr<Gtk::Builder> mBuilder; std::map<std::string, std::unique_ptr<ConnectionProxy>> mConnectRefList; std::map<std::string, sigc::connection> mDisconnectRefList; bool mIsConnected; }; #endif // WIDGETCONNECTOR_H
修改后的使用示例
1. 无参数无返回值回调
// 回调函数 void onButtonClicked() { std::cout << "Button clicked!" << std::endl; } // 使用WidgetConnector WidgetConnector connector(builder); auto& button = connector.getConnectedWidget<Gtk::Button, &Gtk::Button::signal_clicked>( "fooButton", sigc::mem_fun(this, &ThisClassName::onButtonClicked) ); button.set_sensitive(true); connector.connectWidgetsWithSignals();
2. 带参数的回调(如Gtk::Entry的signal_activate带字符串参数)
// 回调函数 void onEntryActivated(const Glib::ustring& text) { std::cout << "Entry text: " << text << std::endl; } // 使用WidgetConnector auto& entry = connector.getConnectedWidget<Gtk::Entry, &Gtk::Entry::signal_activate>( "fooEntry", sigc::mem_fun(this, &ThisClassName::onEntryActivated) );
3. 返回非void类型的回调(如Gtk::Window的signal_delete_event返回bool)
// 回调函数 bool onWindowDelete(GdkEventAny* event) { std::cout << "Window closing..." << std::endl; return true; // 返回true阻止窗口关闭,false允许关闭 } // 使用WidgetConnector auto& window = connector.getConnectedWidget<Gtk::Window, &Gtk::Window::signal_delete_event>( "mainWindow", sigc::mem_fun(this, &ThisClassName::onWindowDelete) );
内容的提问来源于stack exchange,提问作者Markus K.
相关产品推荐
相关产品推荐

