如何在 ROOT TButton 中使用 C++ lambda 或函数回调
ROOT C++ TButton API 只允许你向 TButton 对象传递 method 字符串,该字符串由 CINT 解释器解释。虽然这在某些方面很灵活,但它不提供任何向按钮传递 lambda 或函数指针的方法。
解决方案是从 TButton 派生自定义类并重写其 TButton::ExecuteEvent() 函数。大部分事件处理程序应从原始 TButton::ExecuteEvent() 函数复制,只有
TButtonWithCallback.cpp
gROOT->ProcessLine(GetMethod());被替换为
clickCallback_replacement.cpp
clickCallback()作为回调成员,我们使用 std::function<void()> 对象,它与 C 函数指针不同,能够存储带上下文的 lambda,即向回调传递参数。
完整代码
TButtonWithCallback.hpp
TButtonWithCallback.hpp
#pragma once
#include <TButton.h>
#include <functional>
/**
* Custom TButton class that allows setting a callback function
* (instead of an interpreted string) to be called when the button is clicked.
*/
class TButtonWithCallback : public TButton {
friend class TButton;
public:
TButtonWithCallback(const char *title, std::function<void()> clickCallback, Double_t x1, Double_t y1, Double_t x2, Double_t y2);
void ExecuteEvent(Int_t event, Int_t px, Int_t py) override;
private:
std::function<void()> clickCallback;
bool fFocused = false;
};TButtonWithCallback.cpp
TButtonWithCallback_impl.cpp
#include "TButtonWithCallback.hpp"
#include <TROOT.h>
#include <TVirtualPad.h>
#include <TCanvas.h>
TButtonWithCallback::TButtonWithCallback(const char *title, std::function<void()> clickCallback, Double_t x1, Double_t y1, Double_t x2, Double_t y2)
: TButton(title, "(void)0;", x1, y1, x2, y2), clickCallback(clickCallback) {}
void TButtonWithCallback::ExecuteEvent(Int_t event, Int_t px, Int_t py) {
//check case where pressing a button deletes itself
if (ROOT::Detail::HasBeenDeleted(this)) return;
if (IsEditable()) {
TPad::ExecuteEvent(event,px,py);
return;
}
auto cdpad = gROOT->GetSelectedPad();
HideToolTip(event);
switch (event) {
case kMouseEnter:
TPad::ExecuteEvent(event,px,py);
break;
case kButton1Down:
SetBorderMode(-1);
fFocused = kTRUE;
Modified();
Update();
break;
case kMouseMotion:
break;
case kButton1Motion:
if (px<XtoAbsPixel(1) && px>XtoAbsPixel(0) &&
py<YtoAbsPixel(0) && py>YtoAbsPixel(1)) {
if (!fFocused) {
SetBorderMode(-1);
fFocused = kTRUE;
Modified();
GetCanvas()->Modified();
Update();
}
} else if (fFocused) {
SetBorderMode(1);
fFocused = kFALSE;
Modified();
GetCanvas()->Modified();
Update();
}
break;
case kButton1Up:
SetCursor(kWatch);
if (fFocused) {
TVirtualPad::TContext ctxt(cdpad, kTRUE, kTRUE);
clickCallback();
}
//check case where pressing a button deletes itself
if (ROOT::Detail::HasBeenDeleted(this)) return;
SetBorderMode(1);
Modified();
Update();
SetCursor(kCross);
break;
}
}完整使用示例
此示例将代码合并到单个文件中
button_example.cpp
#include <TApplication.h>
#include <TCanvas.h>
#include <TButton.h>
#include <iostream>
#include "TSystem.h"
#include <TROOT.h>
#include <functional>
using std::cout;
using std::endl;
/**
* Custom TButton class that allows setting a callback function
* (instead of an interpreted string) to be called when the button is clicked.
*/
class TButtonWithCallback : public TButton {
friend class TButton;
public:
TButtonWithCallback(const char *title, std::function<void()> clickCallback, Double_t x1, Double_t y1, Double_t x2, Double_t y2)
: TButton(title, "(void)0;", x1, y1, x2, y2), clickCallback(clickCallback) {}
void ExecuteEvent(Int_t event, Int_t px, Int_t py) override {
//check case where pressing a button deletes itself
if (ROOT::Detail::HasBeenDeleted(this)) return;
if (IsEditable()) {
TPad::ExecuteEvent(event,px,py);
return;
}
auto cdpad = gROOT->GetSelectedPad();
HideToolTip(event);
switch (event) {
case kMouseEnter:
TPad::ExecuteEvent(event,px,py);
break;
case kButton1Down:
SetBorderMode(-1);
fFocused = kTRUE;
Modified();
Update();
break;
case kMouseMotion:
break;
case kButton1Motion:
if (px<XtoAbsPixel(1) && px>XtoAbsPixel(0) &&
py<YtoAbsPixel(0) && py>YtoAbsPixel(1)) {
if (!fFocused) {
SetBorderMode(-1);
fFocused = kTRUE;
Modified();
GetCanvas()->Modified();
Update();
}
} else if (fFocused) {
SetBorderMode(1);
fFocused = kFALSE;
Modified();
GetCanvas()->Modified();
Update();
}
break;
case kButton1Up:
SetCursor(kWatch);
if (fFocused) {
TVirtualPad::TContext ctxt(cdpad, kTRUE, kTRUE);
clickCallback();
}
//check case where pressing a button deletes itself
if (ROOT::Detail::HasBeenDeleted(this)) return;
SetBorderMode(1);
Modified();
Update();
SetCursor(kCross);
break;
}
}
private:
std::function<void()> clickCallback;
// Clone of TButton::fFocused which is private
bool fFocused = false;
};
int main(int argc, char **argv) {
// Initialize the ROOT application
TApplication app("ROOT Application", &argc, argv);
// Create a canvas for displaying the button
TCanvas *canvas = new TCanvas("canvas", "Button Example", 800, 600);
// Create a button and set its callback function
TButtonWithCallback *button = new TButtonWithCallback("Click Me", [&]() {
cout << "Button clicked!" << endl;
}, 0.1, 0.1, 0.3, 0.2);
button->Draw(); // Draw the button on the canvas
// Update the canvas to display the button
canvas->Modified();
canvas->Update();
// Run the application event loop
app.Run();
return 0;
}使用以下命令编译代码
compile_button_example.sh
g++ -o button_example button_example.cpp `root-config --cflags --libs`
当你点击按钮时,
button_click_output.txt
Button clicked!将打印到控制台。
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow