如何使Motif Push Button组件呈现永久按下状态?
解决Motif按钮无法保持按下状态的问题
你遇到的核心问题是:普通XmPushButton设计为瞬时触发动作,不支持保持按下状态。要实现单选按钮组(选中后保持按下、切换时自动取消其他按钮选中)的效果,需要改用XmToggleButton配合启用单选行为的RowColumn容器。
修改步骤
1. 替换按钮类型与回调
- 将头文件
Xm/PushB.h替换为Xm/ToggleB.h - 使用
XmCreateToggleButton创建按钮,替代原有的XmCreatePushButton - 改用
XmNvalueChangedCallback作为回调(更适配ToggleButton的状态变化逻辑)
2. 配置RowColumn的单选行为
在创建按钮容器时,添加XmNradioBehavior参数并设为True,容器会自动管理按钮的互斥选中状态,无需手动循环处理其他按钮。
3. 简化回调逻辑
由于RowColumn已自动处理互斥,回调只需在按钮选中时执行音频设备切换即可。
修改后的代码
xaudio.c
#include <Xm/Xm.h> #include <Xm/ToggleB.h> // 替换原PushB.h头文件 #include <Xm/MainW.h> #include <Xm/RowColumn.h> #include <stdio.h> #include <stdlib.h> #include "devices.h" struct button_data { int device_num; Widget button; }; /* GLOBAL VARIABLES */ int num_devices; struct device *devices; struct button_data *buttons; void button_callback(Widget widget, XtPointer client_data, XtPointer call_data); void create_device_buttons(Widget parent, struct device *devices, int num_devices); void button_callback(Widget widget, XtPointer client_data, XtPointer call_data) { struct button_data *data = (struct button_data *)client_data; int device_num = data->device_num; XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)call_data; // 仅在按钮被选中时执行设备切换 if (cb->set) { set_audio_device(device_num); } } void create_device_buttons(Widget parent, struct device *devices, int num_devices) { int i; XmString label; Arg args[5]; int n; buttons = malloc(num_devices * sizeof(struct button_data)); if (buttons == NULL) { perror("Failed to allocate memory"); exit(EXIT_FAILURE); } for (i = 0; i < num_devices; i++) { // 创建ToggleButton替代PushButton n = 0; label = XmStringCreateLocalized(devices[i].description); XtSetArg(args[n], XmNlabelString, label); n++; buttons[i].button = XmCreateToggleButton(parent, "device_button", args, n); XmStringFree(label); XtManageChild(buttons[i].button); // 设置按钮数据 buttons[i].device_num = devices[i].number; // 使用状态变化回调替代激活回调 XtAddCallback(buttons[i].button, XmNvalueChangedCallback, button_callback, &buttons[i]); } } int main(int argc, char *argv[]) { XtAppContext app; Widget top_level, main_window, button_rowcolumn; int default_device; int n=0; Arg args[10]; top_level = XtVaOpenApplication(&app, "XAudio", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL); main_window = XmCreateMainWindow(top_level, "main_window", NULL, 0); XtManageChild(main_window); n=0; XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; XtSetArg(args[n], XmNradioBehavior, True); n++; // 启用单选组行为 button_rowcolumn = XmCreateRowColumn(main_window, "button_rowcolumn", args, n); XtManageChild(button_rowcolumn); /* 获取设备列表与默认设备 */ default_device = get_default_device(&devices, &num_devices); int i=0; for(i=0; i<num_devices; i++) { printf("%d %s\n", devices[i].number, devices[i].description); } create_device_buttons(button_rowcolumn, devices, num_devices); XtRealizeWidget(top_level); /* 设置默认按钮为选中状态 */ if (default_device >= 0) { XtVaSetValues(buttons[default_device].button, XmNset, True, NULL); } XtAppMainLoop(app); free(devices); free(buttons); return 0; }
其他文件无需修改
devices.c、devices.h和Makefile保持原代码即可。
原理说明
XmToggleButton原生支持XmNset属性,可保持选中状态并呈现按下的视觉效果- 启用
XmNradioBehavior的RowColumn会自动维护按钮组的互斥关系,选中一个按钮时自动取消其他按钮的选中状态 XmNvalueChangedCallback可精准捕捉按钮状态变化,避免误触发操作
内容的提问来源于stack exchange,提问作者M.E.
相关产品推荐
相关产品推荐

