You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:15:21