LogStatus

在回测系统或实盘页面的状态栏中输出信息。

LogStatus(…msgs)

参数msg为输出的内容,可以传入多个参数。 msg false string / number / bool / object / array / any (系统支持的所有类型)

function main() {
    LogStatus('这是一个普通的状态提示')
    LogStatus('这是一个红色字体的状态提示#ff0000')
    LogStatus('这是一个多行的状态信息\n我是第二行')
}```
```python
def main():
    LogStatus('这是一个普通的状态提示')
    LogStatus('这是一个红色字体的状态提示#ff0000')
    LogStatus('这是一个多行的状态信息\n我是第二行')```
```cpp
void main() {
    LogStatus("这是一个普通的状态提示");
    LogStatus("这是一个红色字体的状态提示#ff0000");
    LogStatus("这是一个多行的状态信息\n我是第二行");
}```
支持设置输出内容的颜色:
```javascript
function main() {
    var table = {type: 'table', title: '持仓信息', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
    // JSON序列化后两边加上`字符,视为复杂消息格式(当前支持表格)
    LogStatus('`' + JSON.stringify(table) + '`')
    // 表格信息也可以在多行中显示
    LogStatus('第一行消息\n`' + JSON.stringify(table) + '`\n第三行消息')
    // 支持多个表格同时显示,将以标签页形式组合显示
    LogStatus('`' + JSON.stringify([table, table]) + '`')

    // 也可以在表格中构造按钮,策略通过GetCommand接收cmd属性的内容
    var table = {
        type: 'table',
        title: '持仓操作',
        cols: ['列1', '列2', 'Action'],
        rows: [
            ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}]
        ]
    }
    LogStatus('`' + JSON.stringify(table) + '`')
    // 或者构造单独的按钮
    LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
    // 可以自定义按钮样式(Bootstrap按钮属性)
    LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
}```
```python
import json
def main():
    table = {"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('第一行消息\n`' + json.dumps(table) + '`\n第三行消息')
    LogStatus('`' + json.dumps([table, table]) + '`')

    table = {
        "type" : "table",
        "title" : "持仓操作",
        "cols" : ["列1", "列2", "Action"],
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
        ]
    }
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "平仓"}) + '`')
    LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"}) + '`')```
```cpp
void main() {
    json table = R"({"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("第一行消息\n`" + table.dump() + "`\n第三行消息");
    json arr = R"([])"_json;
    arr.push_back(table);
    arr.push_back(table);
    LogStatus("`" + arr.dump() + "`");

    table = R"({
        "type" : "table",
        "title" : "持仓操作",
        "cols" : ["列1", "列2", "Action"],
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
        ]
    })"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
    LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
}```
状态栏数据输出示例:
```javascript
function main() {
    var table = {
        type: "table",
        title: "状态栏按钮样式",
        cols: ["默认", "原始", "成功", "信息", "警告", "危险"],
        rows: [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
            ]
        ]
    }
    LogStatus("`" + JSON.stringify(table) + "`")
}```
```python
import json
def main():
    table = {
        "type": "table",
        "title": "状态栏按钮样式",
        "cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
        "rows": [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
            ]
        ]
    }
    LogStatus("`" + json.dumps(table) + "`")```
```cpp
void main() {
    json table = R"({
        "type": "table",
        "title": "状态栏按钮样式",
        "cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
        "rows": [
            [
                {"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
                {"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
                {"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
                {"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
                {"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
                {"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
            ]
        ]
    })"_json;
    LogStatus("`" + table.dump() + "`");
}```
支持在状态栏中设计按钮控件(旧版按钮结构):
```javascript
function main() {
    var table = {
        type: "table",
        title: "状态栏按钮的禁用、描述功能测试",
        cols: ["列1", "列2", "列3"],
        rows: []
    }
    var button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
    var button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,已设置为禁用状态", "disabled": true}
    var button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,已设置为启用状态", "disabled": false}
    table.rows.push([button1, button2, button3])
    LogStatus("`" + JSON.stringify(table) + "`")
}```
```python
import json
def main():
    table = {
        "type": "table",
        "title": "状态栏按钮的禁用、描述功能测试",
        "cols": ["列1", "列2", "列3"],
        "rows": []
    }
    button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
    button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,已设置为禁用状态", "disabled": True}
    button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,已设置为启用状态", "disabled": False}
    table["rows"].append([button1, button2, button3])
    LogStatus("`" + json.dumps(table) + "`")```
```cpp
void main() {
    json table = R"({
        "type": "table",
        "title": "状态栏按钮的禁用、描述功能测试",
        "cols": ["列1", "列2", "列3"],
        "rows": []
    })"_json;
    json button1 = R"({"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"})"_json;
    json button2 = R"({"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,已设置为禁用状态", "disabled": true})"_json;
    json button3 = R"({"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,已设置为启用状态", "disabled": false})"_json;
    json arr = R"([])"_json;
    arr.push_back(button1);
    arr.push_back(button2);
    arr.push_back(button3);
    table["rows"].push_back(arr);
    LogStatus("`" + table.dump() + "`");
}```
设置状态栏按钮的禁用和描述功能(旧版按钮结构):
```javascript
function test1() {
    Log("调用自定义函数")
}

function main() {
    while (true) {
        var table = {
            type: 'table',
            title: '操作',
            cols: ['列1', '列2', 'Action'],
            rows: [
                ['a', '1', {
                    'type': 'button',
                    'cmd': "CoverAll",
                    'name': '平仓'
                }],
                ['b', '1', {
                    'type': 'button',
                    'cmd': 10,
                    'name': '发送数值'
                }],
                ['c', '1', {
                    'type': 'button',
                    'cmd': _D(),
                    'name': '调用函数'
                }],
                ['d', '1', {
                    'type': 'button',
                    'cmd': 'test1',
                    'name': '调用自定义函数'
                }]
            ]
        }
        LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`')

        var str_cmd = GetCommand()
        if (str_cmd) {
            Log("接收到的交互数据 str_cmd:", "类型:", typeof(str_cmd), "值:", str_cmd)
            if(str_cmd == "test1") {
                test1()
            }
        }

        Sleep(500)
    }
}```
```python
import json
def test1():
    Log("调用自定义函数")

def main():
    while True:
        table = {
            "type": "table",
            "title": "操作",
            "cols": ["列1", "列2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button",
                    "cmd": "CoverAll",
                    "name": "平仓"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "发送数值"
                }],
                ["c", "1", {
                    "type": "button",
                    "cmd": _D(),
                    "name": "调用函数"
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "调用自定义函数"
                }]
            ]
        }

        LogStatus(_D(), "\n", "`" + json.dumps(table) + "`")
        str_cmd = GetCommand()
        if str_cmd:
            Log("接收到的交互数据 str_cmd", "类型:", type(str_cmd), "值:", str_cmd)
            if str_cmd == "test1":
                test1()
        Sleep(500)```
```cpp
void test1() {
    Log("调用自定义函数");
}

void main() {
    while(true) {
        json table = R"({
            "type": "table",
            "title": "操作",
            "cols": ["列1", "列2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button",
                    "cmd": "CoverAll",
                    "name": "平仓"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "发送数值"
                }],
                ["c", "1", {
                    "type": "button",
                    "cmd": "",
                    "name": "调用函数"
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "调用自定义函数"
                }]
            ]
        })"_json;
        table["rows"][2][2]["cmd"] = _D();
        LogStatus(_D(), "\n", "`" + table.dump() + "`");
        auto str_cmd = GetCommand();
        if(str_cmd != "") {
            Log("接收到的交互数据 str_cmd", "类型:", typeid(str_cmd).name(), "值:", str_cmd);
            if(str_cmd == "test1") {
                test1();
            }
        }
        Sleep(500);
    }
}```
结合```GetCommand()```函数,构建状态栏按钮交互功能(旧版按钮结构):
```javascript
function main() {
    var tbl = {
        type: "table",
        title: "操作",
        cols: ["列1", "列2"],
        rows: [
            ["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
            ["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
        ]
    }

    LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
    while (true) {
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)
        }
        Sleep(1000)
    }
}```
```python
import json

def main():
    tbl = {
        "type": "table",
        "title": "操作",
        "cols": ["列1", "列2"],
        "rows": [
            ["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
            ["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
        ]
    }

    LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
    while True:
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
        Sleep(1000)```
```cpp
void main() {
    json tbl = R"({
        "type": "table",
        "title": "操作",
        "cols": ["列1", "列2"],
        "rows": [
            ["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
            ["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
        ]
    })"_json;

    LogStatus(_D(), "\n", "`" + tbl.dump() + "`");
    while(true) {
        auto cmd = GetCommand();
        if(cmd != "") {
            Log("cmd:", cmd);
        }
        Sleep(1000);
    }
}```
在构造状态栏按钮进行交互时,也支持输入数据功能,交互指令最终由```GetCommand()```函数捕获。

为状态栏中按钮控件的数据结构增加```input```项(旧版按钮结构),例如为```{"type": "button", "cmd": "open", "name": "开仓"}```增加:```"input": {"name": "开仓数量", "type": "number", "defValue": 1}```,即可使按钮在被点击时弹出一个带输入框控件的弹窗(输入框中默认值为1,即```defValue```设置的数据),可以输入数据并与按钮命令一起发送。例如以下测试代码运行时,点击「开仓」按钮后,会弹出一个带输入框的弹窗,在输入框中输入111后点击「确定」,```GetCommand()```函数将捕获消息:```open:111```。
```javascript
function main() {
    var tbl = {
        type: "table",
        title: "演示分组按钮控件",
        cols: ["操作"],
        rows: []
    }

    // 创建分组按钮控件结构
    var groupBtn = {
        type: "button",
        cmd: "open",
        name: "开仓",
        group: [
            {"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
            {"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
            {"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
            {"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true}
        ]
    }

    // 测试按钮1
    var testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
    var testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}

    // 在tbl中添加groupBtn
    tbl.rows.push([groupBtn])
    // 支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2]
    tbl.rows.push([[testBtn1, testBtn2]])

    while (true) {
        LogStatus("`" + JSON.stringify(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + JSON.stringify(groupBtn) + "`")
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)
        }
        Sleep(5000)
    }
}```
```python
import json

def main():
    tbl = {
        "type": "table",
        "title": "演示分组按钮控件",
        "cols": ["操作"],
        "rows": []
    }

    groupBtn = {
        "type": "button",
        "cmd": "open",
        "name": "开仓",
        "group": [
            {"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
            {"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
            {"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
            {"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": True}
        ]
    }

    testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
    testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}

    tbl["rows"].append([groupBtn])
    tbl["rows"].append([[testBtn1, testBtn2]])

    while True:
        LogStatus("`" + json.dumps(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + json.dumps(groupBtn) + "`")
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
        Sleep(5000)```
```cpp
void main() {
    json tbl = R"({
        "type": "table",
        "title": "演示分组按钮控件",
        "cols": ["操作"],
        "rows": []
    })"_json;

    json groupBtn = R"({
        "type": "button",
        "name": "开仓",
        "cmd": "open",
        "group": [
            {"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
            {"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
            {"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
            {"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true}
    ]})"_json;

    json testBtn1 = R"({"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"})"_json;
    json testBtn2 = R"({"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}})"_json;

    tbl["rows"].push_back({groupBtn});
    tbl["rows"].push_back({{testBtn1, testBtn2}});

    while(true) {
        LogStatus("`" + tbl.dump() + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + groupBtn.dump() + "`");
        auto cmd = GetCommand();
        if(cmd != "") {
            Log("cmd:", cmd);
        }
        Sleep(5000);
    }
}```
支持分组按钮控件(旧版按钮结构),功能与**支持输入数据的状态栏按钮**(使用"input"字段设置)一致。交互指令最终由```GetCommand()```函数捕获。区别在于使用```"group"```字段进行设置,当点击按钮触发交互时,页面会弹出包含预设**一组**输入控件的对话框,可以一次性输入多项数据。

关于状态栏按钮控件和分组按钮控件结构中的```"group"```字段,需要注意以下几点:
- group中的```type```属性仅支持以下4种类型,```defValue```属性为默认值。
  "selected":下拉框控件,设置下拉框中每个选项时使用```|```符号分隔。
  "number":数值输入框控件。
  "string":字符串输入框控件。
  "boolean":勾选框控件,勾选为(布尔值)真,不勾选为(布尔值)假。
- 交互输入时的控件支持依赖设置:
  例如以下示例中的:```"name": "tradePrice@orderType==1"```设置,该设置使**交易价格**(```tradePrice```)输入控件仅在**下单方式**(orderType)下拉框控件选择为**挂单**时才可用。
- 交互输入时的控件名称支持双语设置:
  例如以下示例中的:"description": "下单方式|order type"设置,使用```|```符号分隔中英文描述内容。
- group中的```name```、```description```与按钮结构中的```name```、```description```虽然字段名相同,但定义并不相同:
  group中的```name```与input中的```name```定义也不相同。
- 分组按钮控件触发后,发送的交互内容格式为:按钮的cmd字段值、group字段相关数据,例如以下示例测试时```Log("cmd:", cmd)```语句输出的内容:
  ```cmd: open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}```,即发生交互操作时```GetCommand()```函数返回的内容:```open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}```。
- 按钮控件的```type```属性仅支持:```"button"```:
  支持输入数据的按钮控件,即设置了```input```属性的控件,```input```字段配置信息中的```type```属性支持多种控件类型。
参考以下示例:
```javascript
function main() {
    // 状态栏按钮控件(设置input字段实现)testBtn1按钮触发的页面中的下拉框控件使用options字段设置选项,使用defValue字段设置默认选项。区别于本章其它例子中直接使用defValue设置选项。
    var testBtn1 = {
        type: "button",
        name: "testBtn1",
        cmd: "cmdTestBtn1",
        input: {name: "testBtn1ComboBox", type: "selected", options: ["A", "B"], defValue: 1}
    }

    /*
      状态栏按钮控件(设置input字段实现)testBtn2按钮触发的页面中的下拉框控件使用options字段设置选项,options字段中的选项不仅支持字符串,
      也支持使用```{text: "描述", value: "值"}```结构。使用defValue字段设置默认选项,默认选项可以是多选(通过数组结构实现多选)。多选需要设置额外的字段multiple为真值(true)。
    */
    var testBtn2 = {
        type: "button",
        name: "testBtn2",
        cmd: "cmdTestBtn2",
        input: {
            name: "testBtn2MultiComboBox",
            type: "selected",
            description: "实现下拉框多选",
            options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}],
            defValue: ["A", "C"],
            multiple: true
        }
    }

    // 状态栏分组按钮控件(设置group字段实现)testBtn3按钮触发的页面中的下拉框控件使用options字段设置选项,也支持直接使用defValue设置选项。
    var testBtn3 = {
        type: "button",
        name: "testBtn3",
        cmd: "cmdTestBtn3",
        group: [
            {name: "comboBox1", label: "labelComboBox1", description: "下拉框1", type: "selected", defValue: 1, options: ["A", "B"]},
            {name: "comboBox2", label: "labelComboBox2", description: "下拉框2", type: "selected", defValue: "A|B"},
            {name: "comboBox3", label: "labelComboBox3", description: "下拉框3", type: "selected", defValue: [0, 2], multiple: true, options: ["A", "B", "C"]},
            {
                name: "comboBox4",
                label: "labelComboBox4",
                description: "下拉框4",
                type: "selected",
                defValue: ["A", "C"],
                multiple: true,
                options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}, {text: "选项D", value: "D"}]
            }
        ]
    }
    while (true) {
        LogStatus("`" + JSON.stringify(testBtn1) + "`\n", "`" + JSON.stringify(testBtn2) + "`\n", "`" + JSON.stringify(testBtn3) + "`\n")
        var cmd = GetCommand()
        if (cmd) {
            Log(cmd)
        }
        Sleep(5000)
    }
}```
```python
import json

def main():
    testBtn1 = {
        "type": "button",
        "name": "testBtn1",
        "cmd": "cmdTestBtn1",
        "input": {"name": "testBtn1ComboBox", "type": "selected", "options": ["A", "B"], "defValue": 1}
    }

    testBtn2 = {
        "type": "button",
        "name": "testBtn2",
        "cmd": "cmdTestBtn2",
        "input": {
            "name": "testBtn2MultiComboBox",
            "type": "selected",
            "description": "实现下拉框多选",
            "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}],
            "defValue": ["A", "C"],
            "multiple": True
        }
    }

    testBtn3 = {
        "type": "button",
        "name": "testBtn3",
        "cmd": "cmdTestBtn3",
        "group": [
            {"name": "comboBox1", "label": "labelComboBox1", "description": "下拉框1", "type": "selected", "defValue": 1, "options": ["A", "B"]},
            {"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"},
            {"name": "comboBox3", "label": "labelComboBox3", "description": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": True, "options": ["A", "B", "C"]},
            {
                "name": "comboBox4",
                "label": "labelComboBox4",
                "description": "下拉框4",
                "type": "selected",
                "defValue": ["A", "C"],
                "multiple": True,
                "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}]
            }
        ]
    }

    while True:
        LogStatus("`" + json.dumps(testBtn1) + "`\n", "`" + json.dumps(testBtn2) + "`\n", "`" + json.dumps(testBtn3) + "`\n")
        cmd = GetCommand()
        if cmd:
            Log(cmd)
        Sleep(5000)```
```cpp
void main() {
    json testBtn1 = R"({
        "type": "button",
        "name": "testBtn1",
        "cmd": "cmdTestBtn1",
        "input": {"name": "testBtn1ComboBox", "type": "selected", "options": ["A", "B"], "defValue": 1}
    })"_json;

    json testBtn2 = R"({
        "type": "button",
        "name": "testBtn2",
        "cmd": "cmdTestBtn2",
        "input": {
            "name": "testBtn2MultiComboBox",
            "type": "selected",
            "description": "实现下拉框多选",
            "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}],
            "defValue": ["A", "C"],
            "multiple": true
        }
    })"_json;

    json testBtn3 = R"({
        "type": "button",
        "name": "testBtn3",
        "cmd": "cmdTestBtn3",
        "group": [
            {"name": "comboBox1", "label": "labelComboBox1", "description": "下拉框1", "type": "selected", "defValue": 1, "options": ["A", "B"]},
            {"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"},
            {"name": "comboBox3", "label": "labelComboBox3", "description": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": true, "options": ["A", "B", "C"]},
            {
                "name": "comboBox4",
                "label": "labelComboBox4",
                "description": "下拉框4",
                "type": "selected",
                "defValue": ["A", "C"],
                "multiple": true,
                "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}]
            }
        ]
    })"_json;

    while (true) {
        LogStatus("`" + testBtn1.dump() + "`\n", "`" + testBtn2.dump() + "`\n", "`" + testBtn3.dump() + "`\n");
        auto cmd = GetCommand();
        if (cmd != "") {
            Log(cmd);
        }
        Sleep(5000);
    }
}```
状态栏分组按钮控件(通过设置```group```字段实现)与状态栏按钮控件(通过设置```input```字段实现)被点击触发交互时(旧版按钮结构),页面弹出的对话框中的下拉框控件也支持多选。以下示例演示如何设计包含多选选项的下拉框控件:
```javascript
var symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"]

function createBtn(tmp, group) {
    var btn = JSON.parse(JSON.stringify(tmp))

    _.each(group, function(eleByGroup) {
        btn["group"].unshift(eleByGroup)
    })

    return btn
}

function main() {
    var arrManager = []

    _.each(symbols, function(symbol) {
        arrManager.push({
            "symbol": symbol,
        })
    })

    // Btn
    var tmpBtnOpen = {
        "type": "button",
        "cmd": "open",
        "name": "开仓下单",
        "group": [{
            "type": "selected",
            "name": "tradeType",
            "label": "下单类型",
            "description": "市价单、限价单",
            "default": 0,
            "group": "交易设置",
            "settings": {
                "options": ["市价单", "限价单"],
                "required": true,
            }
        }, {
            "type": "selected",
            "name": "direction",
            "label": "交易方向",
            "description": "买入、卖出",
            "default": "buy",
            "group": "交易设置",
            "settings": {
                "render": "segment",
                "required": true,
                "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],
            }
        }, {
            "type": "number",
            "name": "price",
            "label": "价格",
            "description": "订单价格",
            "group": "交易设置",
            "filter": "tradeType==1",
            "settings": {
                "required": true,
            }
        }, {
            "type": "number",
            "name": "amount",
            "label": "下单量",
            "description": "订单数量",
            "group": "交易设置",
            "settings": {
                "required": true,
            }
        }],
    }

    while (true) {
        var tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []}

        _.each(arrManager, function(m) {
            var btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": true}}])
            tbl["rows"].push([m["symbol"], btnOpen])
        })

        var cmd = GetCommand()
        if (cmd) {
            Log("收到交互消息:", cmd)

            // 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
            // 根据第一个冒号前的指令判断是哪种按钮模板触发的消息
            var arrCmd = cmd.split(":", 2)
            if (arrCmd[0] == "open") {
                var msg = JSON.parse(cmd.slice(5))
                Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", msg["tradeType"] == 0 ? "市价单" : "限价单", msg["tradeType"] == 0 ? ",订单价格:当前市价" : ",订单价格:" + msg["price"], ",订单数量:", msg["amount"])
            }
        }

        LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}```
```python
import json

symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"]

def createBtn(tmp, group):
    btn = json.loads(json.dumps(tmp))
    for eleByGroup in group:
        btn["group"].insert(0, eleByGroup)
    return btn

def main():
    arrManager = []

    for symbol in symbols:
        arrManager.append({"symbol": symbol})

    # Btn
    tmpBtnOpen = {
        "type": "button",
        "cmd": "open",
        "name": "开仓下单",
        "group": [{
            "type": "selected",
            "name": "tradeType",
            "label": "下单类型",
            "description": "市价单、限价单",
            "default": 0,
            "group": "交易设置",
            "settings": {
                "options": ["市价单", "限价单"],
                "required": True,
            }
        }, {
            "type": "selected",
            "name": "direction",
            "label": "交易方向",
            "description": "买入、卖出",
            "default": "buy",
            "group": "交易设置",
            "settings": {
                "render": "segment",
                "required": True,
                "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],
            }
        }, {
            "type": "number",
            "name": "price",
            "label": "价格",
            "description": "订单价格",
            "group": "交易设置",
            "filter": "tradeType==1",
            "settings": {
                "required": True,
            }
        }, {
            "type": "number",
            "name": "amount",
            "label": "下单量",
            "description": "订单数量",
            "group": "交易设置",
            "settings": {
                "required": True,
            }
        }],
    }

    while True:
        tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []}
        for m in arrManager:
            btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": True}}])
            tbl["rows"].append([m["symbol"], btnOpen])

        cmd = GetCommand()

        if cmd != "" and cmd != None:
            Log("收到交互消息:", cmd)

            # 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
            # 根据第一个冒号前的指令判断是哪种按钮模板触发的消息
            arrCmd = cmd.split(":")
            if arrCmd[0] == "open":
                msg = json.loads(cmd[5:])
                Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", "市价单" if msg["tradeType"] == 0 else "限价单", ",订单价格:当前市价" if msg["tradeType"] == 0 else ",订单价格:" + str(msg["price"]), ",订单数量:", msg["amount"])

        # 输出状态栏信息
        LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
        Sleep(1000)```
```cpp
// 略...```
使用当前最新的按钮结构,在状态栏表格中构建按钮,点击按钮触发交互时会弹出多控件对话框。

详细内容请参考:[用户指南-状态栏中的交互控件](https://www.youquant.com/user-guide#%E7%8A%B6%E6%80%81%E6%A0%8F%E4%B8%AD%E7%9A%84%E4%BA%A4%E4%BA%92%E6%8E%A7%E4%BB%B6)。
```javascript
function main() {
    var table = {
        type: 'table',
        title: '持仓操作',
        cols: ['列1', '列2', 'Action'],
        rows: [
            ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}]
        ]
    }

    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    exchange.SetContractType("rb888")

    var ticker = exchange.GetTicker()
    // 添加一行数据,第一个和第二个单元格合并,并且输出ticker变量在合并后的单元格内
    table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"])
    LogStatus('`' + JSON.stringify(table) + '`')
}```
```python
import json
def main():
    table = {
        "type" : "table",
        "title" : "持仓操作",
        "cols" : ["列1", "列2", "Action"],
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
        ]
    }

    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")

    ticker = exchange.GetTicker()
    table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"])
    LogStatus("`" + json.dumps(table) + "`")```
```cpp
void main() {
    json table = R"({
        "type" : "table",
        "title" : "持仓操作",
        "cols" : ["列1", "列2", "Action"],
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
        ]
    })"_json;

    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    exchange.SetContractType("rb888");

    auto ticker = exchange.GetTicker();
    json jsonTicker = R"({"Buy": 0, "Sell": 0, "High": 0, "Low": 0, "Volume": 0, "Last": 0, "Time": 0})"_json;
    jsonTicker["Buy"] = ticker.Buy;
    jsonTicker["Sell"] = ticker.Sell;
    jsonTicker["Last"] = ticker.Last;
    jsonTicker["Volume"] = ticker.Volume;
    jsonTicker["Time"] = ticker.Time;
    jsonTicker["High"] = ticker.High;
    jsonTicker["Low"] = ticker.Low;

    json arr = R"([{"body": {}, "colspan": 2}, "abc"])"_json;
    arr[0]["body"] = jsonTicker;
    table["rows"].push_back(arr);
    LogStatus("`" + table.dump() + "`");
}```
横向合并`LogStatus()`函数绘制的表格中的单元格:
```javascript
function main() {
    var table = {
        type: 'table',
        title: '表格演示',
        cols: ['列A', '列B', '列C'],
        rows: [
            ['A1', 'B1', {'type':'button', 'cmd': 'coverAll', 'name': 'C1'}]
        ]
    }

    // 鉴于测试代码,不使用商品期货策略一般架构
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    exchange.SetContractType("rb888")

    var ticker = exchange.GetTicker()
    var name = exchange.GetName()

    table.rows.push([{body : "A2 + B2:" + JSON.stringify(ticker), colspan : 2}, "C2"])
    table.rows.push([{body : "A3 + A4 + A5:" + name, rowspan : 3}, "B3", "C3"])
    // A3被上一行第一个单元格合并
    table.rows.push(["B4", "C4"])
    // A2被上一行第一个单元格合并
    table.rows.push(["B5", "C5"])
    table.rows.push(["A6", "B6", "C6"])
    LogStatus('`' + JSON.stringify(table) + '`')
}```
```python
import json
def main():
    table = {
        "type" : "table",
        "title" : "表格演示",
        "cols" : ["列A", "列B", "列C"],
        "rows" : [
            ["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}]
        ]
    }

    while not exchange.IO("status"):
        Sleep(1000)
    exchange.SetContractType("rb888")

    ticker = exchange.GetTicker()
    name = exchange.GetName()

    table["rows"].append([{"body": "A2 + B2:" + json.dumps(ticker), "colspan": 2}, "C2"])
    table["rows"].append([{"body": "A3 + A4 + A5:" + name, "rowspan": 3}, "B3", "C3"])
    table["rows"].append(["B4", "C4"])
    table["rows"].append(["B5", "C5"])
    table["rows"].append(["A6", "B6", "C6"])
    LogStatus("`" + json.dumps(table) + "`")```
```cpp
void main() {
    json table = R"({
        "type" : "table",
        "title" : "表格演示",
        "cols" : ["列A", "列B", "列C"],
        "rows" : [
            ["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}]
        ]
    })"_json;
    // 为了测试,代码简短易读,这里使用构造的数据
    json jsonTicker = R"({"High": 0, "Low": 0, "Buy": 0, "Sell": 0, "Last": 0, "Time": 0, "Volume": 0})"_json;
    auto name = exchange.GetName();
    json arr1 = R"([{"body": "", "colspan": 2}, "C2"])"_json;
    arr1[0]["body"] = "A2 + B2:" + jsonTicker.dump();
    json arr2 = R"([{"body": "", "rowspan": 3}, "B3", "C3"])"_json;
    arr2[0]["body"] = "A3 + A4 + A5:" + name;
    table["rows"].push_back(arr1);
    table["rows"].push_back(arr2);
    table["rows"].push_back(R"(["B4", "C4"])"_json);
    table["rows"].push_back(R"(["B5", "C5"])"_json);
    table["rows"].push_back(R"(["A6", "B6", "C6"])"_json);
    LogStatus("`" + table.dump() + "`");
}```
纵向合并`LogStatus()`函数绘制的表格中的单元格:
```javascript
function main() {
    var table1 = {type: 'table', title: 'table1', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
    var table2 = {type: 'table', title: 'table2', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
    LogStatus('`' + JSON.stringify([table1, table2]) + '`')
}```
```python
import json
def main():
    table1 = {"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
    table2 = {"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
    LogStatus("`" + json.dumps([table1, table2]) + "`")```
```cpp
void main() {
    json table1 = R"({"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
    json table2 = R"({"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
    json arr = R"([])"_json;
    arr.push_back(table1);
    arr.push_back(table2);
    LogStatus("`" + arr.dump() + "`");
}```
状态栏表格分页显示:
```javascript
function main(){
    var tab1 = {
        type : "table",
        title : "表格1",
        cols : ["1", "2"],
        rows : []
    }
    var tab2 = {
        type : "table",
        title : "表格2",
        cols : ["1", "2", "3"],
        rows : []
    }
    var tab3 = {
        type : "table",
        title : "表格3",
        cols : ["A", "B", "C"],
        rows : []
    }

    tab1.rows.push(["jack", "lucy"])
    tab2.rows.push(["A", "B", "C"])
    tab3.rows.push(["A", "B", "C"])

    LogStatus('`' + JSON.stringify(tab1) + '`\n' +
        '`' + JSON.stringify(tab2) + '`\n' +
        '`' + JSON.stringify(tab3) + '`')

    Log("exit")
}```
```python
import json
def main():
    tab1 = {
        "type": "table",
        "title": "表格1",
        "cols": ["1", "2"],
        "rows": []
    }
    tab2 = {
        "type": "table",
        "title": "表格2",
        "cols": ["1", "2", "3"],
        "rows": []
    }
    tab3 = {
        "type": "table",
        "title": "表格3",
        "cols": ["A", "B", "C"],
        "rows": []
    }

    tab1["rows"].append(["jack", "lucy"])
    tab2["rows"].append(["A", "B", "C"])
    tab3["rows"].append(["A", "B", "C"])
    LogStatus("`" + json.dumps(tab1) + "`\n" +
        "`" + json.dumps(tab2) + "`\n" +
        "`" + json.dumps(tab3) + "`")```
```cpp
void main() {
    json tab1 = R"({
        "type": "table",
        "title": "表格1",
        "cols": ["1", "2"],
        "rows": []
    })"_json;
    json tab2 = R"({
        "type": "table",
        "title": "表格2",
        "cols": ["1", "2", "3"],
        "rows": []
    })"_json;
    json tab3 = R"({
        "type": "table",
        "title": "表格3",
        "cols": ["A", "B", "C"],
        "rows": []
    })"_json;
    tab1["rows"].push_back(R"(["jack", "lucy"])"_json);
    tab2["rows"].push_back(R"(["A", "B", "C"])"_json);
    tab3["rows"].push_back(R"(["A", "B", "C"])"_json);
    LogStatus("`" + tab1.dump() + "`\n" +
        "`" + tab2.dump() + "`\n" +
        "`" + tab3.dump() + "`");
}```
除了支持分页显示表格外,还可以将多个表格垂直排列显示:
```javascript
function main() {
    var tbl = {
        type : "table",
        title : "test scroll",
        scroll : "auto",
        cols : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
            "col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
        rows : []
    }

    for (var i = 1 ; i < 100 ; i++) {
        tbl.rows.push([i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
            "11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i])
    }

    LogStatus("`" + JSON.stringify(tbl) + "`")
}```
```python
import json

def main():
    tbl = {
        "type" : "table",
        "title" : "test scroll",
        "scroll" : "auto",
        "cols" : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
            "col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
        "rows" : []
    }

    for index in range(1, 100):
        i = str(index)
        tbl["rows"].append([i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
            "11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i])

    LogStatus("`" + json.dumps(tbl) + "`")```
```cpp
void main() {
    json table = R"({
        "type" : "table",
        "title" : "test scroll",
        "scroll" : "auto",
        "cols" : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
            "col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
        "rows" : []
    })"_json;

    for (int index = 1; index < 100; ++index) {
        std::string i = std::to_string(index);
        table["rows"].push_back({i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
            "11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i});
    }

    LogStatus("`" + table.dump() + "`");
}```
支持设置状态栏表格的横向和纵向滚动模式。将```scroll```属性设置为```"auto"```,当状态栏表格纵向行数超过20行时,内容将自动滚动显示;当横向列数超出页面显示范围时,将启用横向滚动。使用```scroll```属性可以有效缓解实盘交易时状态栏大量数据写入导致的卡顿问题。请参考以下测试示例:

实盘运行时,```LogStatus()```函数输出的信息不会保存到实盘数据库,仅更新当前实盘的状态栏内容。
```LogStatus()```函数支持打印```base64```编码的图片,以``` ` ```开头,以``` ` ```结尾。例如:```LogStatus("`data:image/png;base64,AAAA`")```。
```LogStatus()```函数支持直接传入```Python```的```matplotlib.pyplot```对象,只要该对象包含```savefig```方法即可作为参数传入```LogStatus()```函数,例如:

```python
import matplotlib.pyplot as plt

def main():
    plt.plot([3,6,2,4,7,1])
    LogStatus(plt)

策略实盘运行时,如果在实盘页面翻看历史记录,状态栏将进入休眠状态并停止更新。仅当日志位于第一页时,状态栏数据才会刷新。支持在状态栏输出base64编码的图片,也支持在状态栏显示的表格中输出base64编码的图片。由于编码后的图片字符串数据通常很长,因此不再展示示例代码。

{@fun/Global/GetCommand GetCommand}