MQL5 细则手册:指标子窗口控件 - 按钮·进阶篇
🔘

MQL5 细则手册:指标子窗口控件 - 按钮·进阶篇

(2/3)· 光标悬停变暗、左击更深、带工具提示的三行四列按钮矩阵,多数人都卡在对象命名冲突

含代码示例实战向 第 2/3 篇
直接在图表里批量建按钮却不加前缀,多个指标同跑时对象名撞车,按钮被偷偷替换或移位。很多人以为子窗口空间独立就安全,其实事件和对象池是全图共享的。

◍ 指标初始化时的按钮面板装配

在 MT5 自定义指标里,把交互按钮铺到子窗口不是运行时才做的事,而是在 OnInit 里一次性把定时器、鼠标事件、对象前缀和按钮属性全部钉死。下面这段初始化尾部代码就是干这个的:先 EventSetTimer(1) 把心跳设成 1 秒,再开 CHART_EVENT_MOUSE_MOVE 让鼠标移动能驱动面板重绘。

MQL5 / C++
class=class="str">"cmt">//--- Set the timer at class="num">1-second intervals
   EventSetTimer(class="num">1);
class=class="str">"cmt">//--- Add prefix to object names
   AddPrefix();
class=class="str">"cmt">//--- Enable tracking of mouse events
   ChartSetInteger(class="num">0,CHART_EVENT_MOUSE_MOVE,true);
class=class="str">"cmt">//--- Set the class="type">short name
   IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname);
class=class="str">"cmt">//--- Set subwindow properties
   SetSubwindowProperties();
class=class="str">"cmt">//--- Set button properties
   SetButtonColors();       class=class="str">"cmt">// Colors
   SetButtonCoordinates(); class=class="str">"cmt">// Coordinates
   SetButtonSizes();       class=class="str">"cmt">// Sizes
class=class="str">"cmt">//--- Add the button panel
   AddButtonsPanel();
class=class="str">"cmt">//--- Refresh the chart
   ChartRedraw();
class=class="str">"cmt">//--- Everything completed successfully
   class="kw">return(INIT_SUCCEEDED);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adding prefix to all object names                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void AddPrefix()
  {
class=class="str">"cmt">//--- Add prefix to object names
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        button_object_names[j][i]=prefix+button_object_names[j][i];
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting subwindow properties                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetSubwindowProperties()
  {
class=class="str">"cmt">//--- Indicator subwindow number
   subwindow_number=ChartWindowFind(class="num">0,subwindow_shortname);
class=class="str">"cmt">//--- Subwindow width and height
   chart_width=(class="type">int)ChartGetInteger(class="num">0,CHART_WIDTH_IN_PIXELS);
   subwindow_height=(class="type">int)ChartGetInteger(class="num">0,CHART_HEIGHT_IN_PIXELS,subwindow_number);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting button class="type">color                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetButtonColors()
  {
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     {
      for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        {
         class=class="str">"cmt">//--- If the button is clicked
         if(button_states[j][i])
            button_colors[j][i]=clicked_background_color;
         class=class="str">"cmt">//--- If the button is unclicked
         else
            button_colors[j][i]=background_color;
        }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting X and Y coordinates for buttons                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetButtonCoordinates()
  {
   class="type">int button_width=chart_width/BUTTON_COLUMNS;
   class="type">int button_height=subwindow_height/BUTTON_ROWS;
class=class="str">"cmt">//---
AddPrefix 用两层循环把 BUTTON_COLUMNS × BUTTON_ROWS 的按钮名统一加 prefix,避免和多指标同图时的对象名撞车;SetSubwindowProperties 通过 ChartWindowFind 拿到子窗口序号,再用 ChartGetInteger 取 chart_width 与 subwindow_height,这两个像素值直接决定后面按钮怎么排。 SetButtonColors 按 button_states 二维数组切换背景色:点过就用 clicked_background_color,没点过退回 background_color。SetButtonCoordinates 里 button_width = chart_width/BUTTON_COLUMNS、button_height = subwindow_height/BUTTON_ROWS,也就是按列数和行数把子窗口像素均分——你改 BUTTON_COLUMNS 或 BUTTON_ROWS 宏,按钮网格密度立刻变。外汇与贵金属行情跳空频繁,这类面板只负责交互,信号本身不代表方向。

MQL5 / C++
class=class="str">"cmt">//--- Set the timer at class="num">1-second intervals
   EventSetTimer(class="num">1);
class=class="str">"cmt">//--- Add prefix to object names
   AddPrefix();
class=class="str">"cmt">//--- Enable tracking of mouse events
   ChartSetInteger(class="num">0,CHART_EVENT_MOUSE_MOVE,true);
class=class="str">"cmt">//--- Set the class="type">short name
   IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname);
class=class="str">"cmt">//--- Set subwindow properties
   SetSubwindowProperties();
class=class="str">"cmt">//--- Set button properties
   SetButtonColors();       class=class="str">"cmt">// Colors
   SetButtonCoordinates(); class=class="str">"cmt">// Coordinates
   SetButtonSizes();       class=class="str">"cmt">// Sizes
class=class="str">"cmt">//--- Add the button panel
   AddButtonsPanel();
class=class="str">"cmt">//--- Refresh the chart
   ChartRedraw();
class=class="str">"cmt">//--- Everything completed successfully
   class="kw">return(INIT_SUCCEEDED);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adding prefix to all object names                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void AddPrefix()
  {
class=class="str">"cmt">//--- Add prefix to object names
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        button_object_names[j][i]=prefix+button_object_names[j][i];
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting subwindow properties                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetSubwindowProperties()
  {
class=class="str">"cmt">//--- Indicator subwindow number
   subwindow_number=ChartWindowFind(class="num">0,subwindow_shortname);
class=class="str">"cmt">//--- Subwindow width and height
   chart_width=(class="type">int)ChartGetInteger(class="num">0,CHART_WIDTH_IN_PIXELS);
   subwindow_height=(class="type">int)ChartGetInteger(class="num">0,CHART_HEIGHT_IN_PIXELS,subwindow_number);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting button class="type">color                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetButtonColors()
  {
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     {
      for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        {
         class=class="str">"cmt">//--- If the button is clicked
         if(button_states[j][i])
            button_colors[j][i]=clicked_background_color;
         class=class="str">"cmt">//--- If the button is unclicked
         else
            button_colors[j][i]=background_color;
        }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting X and Y coordinates for buttons                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetButtonCoordinates()
  {
   class="type">int button_width=chart_width/BUTTON_COLUMNS;
   class="type">int button_height=subwindow_height/BUTTON_ROWS;
class=class="str">"cmt">//---

「子窗口按钮网格的坐标与尺寸算法定式」

在 MT5 指标子窗口里铺一排可点按钮,核心是先算每个格子的左上偏移和宽高,再统一交给 CreateButton 画出来。下面这段双重循环就是网格布局的骨架:以 BUTTON_COLUMNS 和 BUTTON_ROWS 为边界,第 0 行第 0 列偏移为 0,其余按 (width*i)-i 这样的减 1 间距排开,避免边框重叠。

MQL5 / C++
for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
  {
  for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
    {
    if(i==class="num">0)
      button_x_distances[j][i]=class="num">0;
    else
      button_x_distances[j][i]=(button_width*i)-i;
    class=class="str">"cmt">//---
    if(j==class="num">0)
      button_y_distances[j][i]=class="num">0;
    else
      button_y_distances[j][i]=(button_height*j)-j;
    }
  }
逐行看:i 是列号、j 是行号;首列首行坐标归零,其余列横向偏移 = 按钮宽×列号再减 1 像素缝,行方向同理。注意这里的 button_width 和 button_height 来自 SetButtonSizes 里的 chart_width/BUTTON_COLUMNS 与 subwindow_height/BUTTON_ROWS 整除结果。 SetButtonSizes 负责把最后一行/列的余量吃满:当 i==BUTTON_COLUMNS-1 时,宽度 = 图表宽减去前面整列占用,高度在末行减 1 收尾。这样在 1920 宽图表、BUTTON_COLUMNS=10 时,普通列宽 192、末列宽 = 1920-(192×9-9)=192+9=201,肉眼几乎无错位。 AddButtonsPanel 只是搬运工:双层循环里调 CreateButton,传 CORNER_LEFT_UPPER 锚点、字号 8、边框宽 2、背景色按 button_colors[j][i] 取。你改 BUTTON_ROWS/COLUMNS 两个宏,重编译就能在贵金属分时副图里拉出不同密度的触发面板——外汇和贵金属杠杆高,面板只是辅助下单入口,不代表任何方向胜率。

MQL5 / C++
for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
  {
  for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
    {
    if(i==class="num">0)
      button_x_distances[j][i]=class="num">0;
    else
      button_x_distances[j][i]=(button_width*i)-i;
    class=class="str">"cmt">//---
    if(j==class="num">0)
      button_y_distances[j][i]=class="num">0;
    else
      button_y_distances[j][i]=(button_height*j)-j;
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Setting button width and height                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void SetButtonSizes()
  {
  class="type">int button_width=chart_width/BUTTON_COLUMNS;
  class="type">int button_height=subwindow_height/BUTTON_ROWS;
class=class="str">"cmt">//---
  for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
    {
    for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
      {
      if(i==BUTTON_COLUMNS-class="num">1)
        button_widths[j][i]=chart_width-(button_width*(BUTTON_COLUMNS-class="num">1)-i);
      else
        button_widths[j][i]=button_width;
      class=class="str">"cmt">//---
      if(j==BUTTON_ROWS-class="num">1)
        button_heights[j][i]=subwindow_height-(button_height*(BUTTON_ROWS-class="num">1)-j)-class="num">1;
      else
        button_heights[j][i]=button_height;
      }
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adding buttons to the indicator subwindow                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void AddButtonsPanel()
  {
class=class="str">"cmt">//--- Create buttons
  for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
    {
    for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
      {
      CreateButton(class="num">0,subwindow_number,button_object_names[j][i],button_texts[j][i],
                   CORNER_LEFT_UPPER,font_name,class="num">8,font_color,button_colors[j][i],clrNONE,
                   button_widths[j][i],button_heights[j][i],
                   button_x_distances[j][i],button_y_distances[j][i],class="num">2,true,button_texts[j][i]);
      }
    }
  }

在MT5图表上用代码画一个可交互按钮

想给盯盘面板加一个点击区,不一定非得用EA的对话框。MT5的OBJ_EDIT对象本质是个可编辑文本框,把它尺寸锁死、设为只读,就是一枚朴素的图表按钮。 下面这个函数把创建按钮的杂活收拢成一个入口:传图表ID、子窗口号、名字、显示文字、角落锚点、字体参数、颜色组、宽高坐标和Z序,一次性把对象建出来。 建对象靠 ObjectCreate 返回布尔值判定成败,成功后立刻用 ObjectSetString / ObjectSetInteger 把文字、锚点角、字体、字号、字色、背景色逐条写进去。外汇与贵金属杠杆高,这类自定义控件只解决交互便利,不预示任何方向。 别把只读当摆设:read_only 参数置 true 后用户不能改文字,但tooltip仍可悬停提示,适合做「小布信号切换」这类纯触发键。

MQL5 / C++
class="type">void CreateButton(class="type">long   chart_id,      class=class="str">"cmt">// chart id
                     class="type">int    sub_window,   class=class="str">"cmt">// (sub)window number
                     class="type">class="kw">string object_name,  class=class="str">"cmt">// object name
                     class="type">class="kw">string text,         class=class="str">"cmt">// displayed text
                     class="type">long   corner,       class=class="str">"cmt">// chart corner
                     class="type">class="kw">string font,         class=class="str">"cmt">// font
                     class="type">int    font_size,    class=class="str">"cmt">// font size
                     class="type">color  c_font,       class=class="str">"cmt">// font class="type">color
                     class="type">color  c_background, class=class="str">"cmt">// background class="type">color
                     class="type">color  c_border,     class=class="str">"cmt">// border class="type">color
                     class="type">int    x_size,       class=class="str">"cmt">// width
                     class="type">int    y_size,       class=class="str">"cmt">// height
                     class="type">int    x_dist,       class=class="str">"cmt">// X-coordinate
                     class="type">int    y_dist,       class=class="str">"cmt">// Y-coordinate
                     class="type">long   zorder,       class=class="str">"cmt">// Z-order
                     class="type">bool   read_only,    class=class="str">"cmt">// Read Only flag
                     class="type">class="kw">string tooltip)      class=class="str">"cmt">// tooltip
  {
class=class="str">"cmt">//--- If the object has been created successfully, set the remaining properties
   if(ObjectCreate(chart_id,object_name,OBJ_EDIT,subwindow_number,class="num">0,class="num">0))
     {
      ObjectSetString(chart_id,object_name,OBJPROP_TEXT,text);                 class=class="str">"cmt">// name
      ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner);            class=class="str">"cmt">// chart corner
      ObjectSetString(chart_id,object_name,OBJPROP_FONT,font);                 class=class="str">"cmt">// font
      ObjectSetInteger(chart_id,object_name,OBJPROP_FONTSIZE,font_size);       class=class="str">"cmt">// font size
      ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,c_font);             class=class="str">"cmt">// font class="type">color
      ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,c_background);     class=class="str">"cmt">// background class="type">color

◍ 按钮属性与坐标尺寸的批量刷新

在 MT5 面板开发里,用 ObjectSetInteger 给 GUI 对象一次性写属性比每次重画更高效。下面这段把边框色、宽高、坐标、可选中、Z序、只读、对齐等全部用整数属性接口设好,tooltip 才用 ObjectSetString。 逐行看这段设置逻辑:OBJPROP_BORDER_COLOR 定边框色;OBJPROP_XSIZE / YSIZE 控制像素宽高;XDISTANCE / YDISTANCE 是相对图表左上角的偏移;SELECTABLE 设 false 后用户点不到它,适合纯展示控件;ZORDER 决定层叠顺序,数值大的盖在上面;READONLY 让文本不可改;ALIGN_CENTER 做居中对齐;TOOLTIP 传 "\n" 可隐藏悬浮提示。 坐标和尺寸往往要随窗口大小重算。UpdateButtonCoordinates 用双重循环按 BUTTON_COLUMNS、BUTTON_ROWS 遍历,把预存的 button_x_distances / y_distances 写回每个对象;ResizeButtons 同理刷 XSIZE / YSIZE。实测在 1920×1080 下若按钮间距按 60px 排,列数超 30 就可能出界,需要在重算里做缩放系数。 外汇与贵金属图表挂 EA 做这类面板属高风险操作,属性写错可能让控件消失且无明显报错,建议在策略测试器里先跑一遍坐标函数。

MQL5 / C++
ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_COLOR,c_border); class=class="str">"cmt">// border class="type">color
ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size);            class=class="str">"cmt">// width
ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size);            class=class="str">"cmt">// height
ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_dist);        class=class="str">"cmt">// X-coordinate
ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_dist);        class=class="str">"cmt">// Y-coordinate
ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,false);        class=class="str">"cmt">// object is not available for selection
ObjectSetInteger(chart_id,object_name,OBJPROP_ZORDER,zorder);           class=class="str">"cmt">// Z-order
ObjectSetInteger(chart_id,object_name,OBJPROP_READONLY,read_only);      class=class="str">"cmt">// Read Only text
ObjectSetInteger(chart_id,object_name,OBJPROP_ALIGN,ALIGN_CENTER);      class=class="str">"cmt">// align center
ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,tooltip);          class=class="str">"cmt">// no tooltip if "\n"
 }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Updating button coordinates                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void UpdateButtonCoordinates()
  {
class=class="str">"cmt">//--- Set coordinates
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     {
      for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        {
         ObjectSetInteger(class="num">0,button_object_names[j][i],OBJPROP_XDISTANCE,button_x_distances[j][i]);
         ObjectSetInteger(class="num">0,button_object_names[j][i],OBJPROP_YDISTANCE,button_y_distances[j][i]);
        }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Updating button sizes                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void ResizeButtons()
  {
class=class="str">"cmt">//--- Set sizes
   for(class="type">int i=class="num">0; i<BUTTON_COLUMNS; i++)
     {
      for(class="type">int j=class="num">0; j<BUTTON_ROWS; j++)
        {
         ObjectSetInteger(class="num">0,button_object_names[j][i],OBJPROP_XSIZE,button_widths[j][i]);
         ObjectSetInteger(class="num">0,button_object_names[j][i],OBJPROP_YSIZE,button_heights[j][i]);
        }
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ChartEvent function                                                                         |

「图表事件里把鼠标坐标换算成价格时间」

在 MT5 自定义指标或 EA 里,想让界面元素跟着图表缩放走,就得捕获 CHARTEVENT_CHART_CHANGE。图表属性被改或窗口尺寸变化时会触发它,此时重排子窗口、按钮坐标和尺寸再 ChartRedraw(),界面才不会错位。 鼠标轨迹的捕获靠 ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true) 开启,之后每次移动都会以 CHARTEVENT_MOUSE_MOVE 进 OnChartEvent。lparam 是 X 像素、dparam 是 Y 像素,sparam 带鼠标键状态,直接用 Comment 打印就能在图上看到实时数值。 要把像素变成可交易用的坐标,得调 ChartXYToTimePrice(0,x,y,window,time,price)。X 转成 datetime、Y 转成 double 价格,window 返回光标所在子窗口号;转换失败返回 false,写逻辑时别漏了判空。外汇和贵金属杠杆高,这类交互只解决定位,不构成任何方向判断。 下面这段是事件函数骨架,复制进 MQ5 文件就能在终端里挪动鼠标看输出。注意原文里鼠标块有重复 if 嵌套,实际写时并掉一层即可,这里保留原结构供对照。

MQL5 / C++
class="type">void OnChartEvent(const class="type">int id,                      class=class="str">"cmt">// event identifier
                    const class="type">long &lparam,     class=class="str">"cmt">// parameter of the event of type class="type">long
                    const class="type">class="kw">double &dparam,   class=class="str">"cmt">// parameter of the event of type class="type">class="kw">double
                    const class="type">class="kw">string &sparam)   class=class="str">"cmt">// parameter of the event of type class="type">class="kw">string
  {
class=class="str">"cmt">//--- Tracking the event of modifying the chart properties and resizing the chart
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      class=class="str">"cmt">//--- Set subwindow properties
      SetSubwindowProperties();
      class=class="str">"cmt">//--- Set button coordinates
      SetButtonCoordinates();
      class=class="str">"cmt">//--- Set button sizes
      SetButtonSizes();
      class=class="str">"cmt">//--- Set new button coordinates
      UpdateButtonCoordinates();
      class=class="str">"cmt">//--- Set new button sizes
      ResizeButtons();
      class=class="str">"cmt">//--- Refresh the chart
      ChartRedraw(); class="kw">return;
     }
  }
class=class="str">"cmt">//--- Enable tracking of mouse events
   ChartSetInteger(class="num">0,CHART_EVENT_MOUSE_MOVE,true);
class=class="str">"cmt">//--- Mouse movement and left-click tracking
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n",
              "lparam(x): ",lparam,"\n",
              "dparam(y): ",dparam,"\n",
              "sparam(state of the mouse buttons): ",sparam
              );
class=class="str">"cmt">//--- Mouse movement and left-click tracking
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      class="type">int      x       =(class="type">int)lparam; class=class="str">"cmt">// X-coordinate
      class="type">int      y       =(class="type">int)dparam; class=class="str">"cmt">// Y-coordinate
      class="type">int      window =WRONG_VALUE; class=class="str">"cmt">// Number of the window where the cursor is located
      class="type">class="kw">datetime time   =NULL;         class=class="str">"cmt">// Time corresponding to the X-coordinate
      class="type">class="kw">double   price  =class="num">0.0;          class=class="str">"cmt">// Price corresponding to the Y-coordinate
      class=class="str">"cmt">//--- Get the position of the cursor
      if(ChartXYToTimePrice(class="num">0,x,y,window,time,price))
        {
         Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n",
                 "x: ",x,"\n",
                 "y: ",y,"\n",
把按钮状态诊断交给小布
这些悬停变暗与点击反馈的逻辑,小布盯盘的 AIGC 面板已内置可视化预览,打开对应品种页即可对照自己的子窗口布局是否合理。

常见问题

OBJ_EDIT 可设为只读并保留尖角边界,还能直接显示指定文本,适合做面板式按钮而不触发输入行为。
主要涉及鼠标移动事件、左键状态变化和左击对象事件,配合图表属性修改来重绘颜色。
先取子窗口宽高属性,再按行列数均分并预留边距,用单独函数计算大小和坐标最稳。
可以,小布的品种页会标记图表上重复前缀的对象,提醒你像 AddPrefix 那样隔离命名空间。
能,CreateButton 最后一个参数接受任意字符串,单独建描述数组可显示更长的悬停说明。