C:\Users\[username]\AppData\Roaming\Code\User\settings.json

由于博客内容为空,暂无法提供包含关键信息的摘要。
{
    "editor.fontFamily": "Consolas, 'Courier New', monospace",
    "editor.fontSize": 14,
    "editor.largeFileOptimizations": false,
    "editor.foldingMaximumRegions": 65000,
    "security.workspace.trust.untrustedFiles": "open",
    "editor.renderWhitespace": "all",
    "workbench.colorTheme": "Visual Studio Dark - C++",
    "window.title": "${dirty}${activeEditorMedium}${separator}${rootName}${separator}${appName}",
    "iar.enabled": true,
    "editor.rulers": [48],
    "editor.mouseWheelZoom": true,

    "search.searchEditor.defaultNumberOfContextLines": 0,
    "search.searchOnType": true,
    "editor.matchBrackets":"never",
    "explorer.copyRelativePathSeparator": "/",
    // "files.autoSave": "onFocusChange",
    "background.enabled": true,
    "background.fullscreen": {
        "images": ["file:///Users/somepath/a.jpg"], // urls of your images
        "opacity": 0.91, // 0.85 ~ 0.95 recommended
        "size": "cover", // also css, `cover` to self-adaption (recommended),or `contain`、`200px 200px`
        "position": "center", // alias to `background-position`, default `center`
        "interval": 0 // seconds of interval for carousel, default `0` to disabled.
    }
        // /** 编辑器背景 */
        // "window.titleBarStyle": "custom", //首先把标题栏改为非原生的
        // "background.enabled": true,
        // "background.useDefault": false,
        // "background.customImages": [
        //     //  这个是图片地址 注意这个是左斜杠
        //     "C:/Users/tianjia.hou/Downloads/purple.jpg"
        // ],
        // //这些都像css那样可以改样式的
        // "background.style": {
        //     "content": "''",
        //     "pointer-events": "none",
        //     "position": "absolute",
        //     "z-index": "99999",
        //     "width": "100%",
        //     "height": "100%",
        //     "background-position": "cover",
        //     "background-repeat": "no-repeat",
        //     "background-size": "cover",  
        //     "opacity": 1  //这个是设置透明度
        // },
    "workbench.colorCustomizations": {
        "toolbar.hoverBackground": "#ae00ff",
        "button.background": "#ae00ff",
        "button.foreground": "#ffffff",
        "button.border": "#000000",
        "button.separator":"#ffffff",
        // "button.secondaryForeground":"#000000",
        // "button.secondaryBackground":"#000000",
        // "button.secondaryHoverBackground":"#000000",
        // "checkbox.background": "#ff0000",
        // "button.hoverBackground":"#d06aff",
        // "list.deemphasizedForeground": "#ff0000",
        // "diffEditor.border": "#eeff00",
        // "diffEditor.insertedLineBackground": "#ff0000",
        "diffEditor.insertedLineBackground": "#2bff0021",
        "diffEditor.insertedTextBorder": "#2bff0021",
        "diffEditor.insertedTextBackground": "#2bff0021",
        "diffEditor.diagonalFill": "#006888",
        // "diffEditor.unchangedRegionForeground": "#ff0000",
        // "diffEditor.unchangedCodeBackground": "#ff0000",
        "diffEditor.removedLineBackground": "#ff00005d",
        "diffEditor.removedTextBackground": "#ff00005d",
        // "diffEditor.removedTextBorder": "#ffffff5d",
        "progressBar.background": "#fbff00",
        //"contrastActiveBorder": "#1e1f1e",
        //"window.activeBorder": "#9900ff",
        "scrollbarSlider.background": "#7700ff79",
        "scrollbarSlider.activeBackground": "#8c00ff",
        "scrollbarSlider.hoverBackground": "#8c00ff9f",
        "titleBar.activeBackground": "#540099",
        "titleBar.activeForeground": "#ffffff",
        "titleBar.border": "#540099",
        "editorCursor.background": "#ff0000",
        "editorCursor.foreground": "#fffb00",
        "menu.background": "#000000",
        "menu.foreground": "#ffffff",
        "menubar.selectionForeground": "#000000",
        "menubar.selectionBackground": "#bbff00",
        "menubar.selectionBorder": "#bbff00",
        "menu.border": "#bbff00",
        "menu.selectionForeground": "#000000",
        "menu.selectionBackground": "#bbff00",
        
        "editor.background": "#000000",
        "editor.hoverHighlightBackground": "#33006d",
        //"editor.selectionHighlightBackground": "#010366",
        "editor.selectionHighlightBorder": "#ff9900",
        "editor.selectionBackground": "#00179c",
        "editor.selectionForeground": "#002fff",
        "editor.inactiveSelectionBackground": "#010366",
        //"editor.lineHighlightBackground": "#0059ff7c",
        "editor.lineHighlightBorder": "#00ff408c",
        //"editor.wordHighlightStrongBorder": "#ff0000"
        "editor.findMatchBackground": "#1a83007e",
        "editor.findMatchBorder": "#d9ff00",
        "editor.findMatchHighlightBorder": "#88ff00",
        "editor.findMatchHighlightBackground": "#08007786",
        "editor.findRangeHighlightBackground": "#2d0346",
        "editor.findRangeHighlightBorder": "#ffffff",
        "editorLineNumber.activeForeground":"#d0ff00",
        "editorLineNumber.foreground": "#664d77",

        "editorIndentGuide.activeBackground": "#bbff00",
        "editorIndentGuide.background": "#664d77",
        
        "editorGutter.background": "#200033",
        "editorGutter.modifiedBackground":"#00fff2",
        "editorGutter.deletedBackground":"#ff0000",
        "editorGutter.addedBackground": "#66ff00",
        "editorGutter.commentRangeForeground": "#ff0000",
        "terminal.background": "#1d0052",
        //"minimap.findMatchHighlight": "#ffffff", 
        //"minimap.selectionHighlight": "#ff0000", 
        //"minimap.errorHighlight": "#ff0000", 
        //"minimap.warningHighlight": "#ff0000", 
        "minimap.background": "#160020", 
        "minimapSlider.background": "#4400ff54", 
        "minimapSlider.hoverBackground": "#e5ff007c", 
        "minimapSlider.activeBackground": "#ffe6006c", 
        //"minimapGutter.addedBackground": "#ff0000", 
        //"minimapGutter.modifiedBackground": "#ff0000", 
        //"minimapGutter.deletedBackground": "#ff0000", 
        "editorGroup.border":"#a200ff", 
        "editorGroup.dropBackground":"#8c00ff59", 
        //"editorGroupHeader.noTabsBackground":"#ff0000", 
        "editorGroupHeader.tabsBackground":"#000000", 
        "editorGroupHeader.tabsBorder":"#4e007a", 
        //"editorGroup.emptyBackground":"#ff0000", 
        //"editorGroup.focusedEmptyBorder":"#ff0000", 
        "tab.activeBackground":"#000000", 
        "tab.unfocusedActiveBackground":"#1d0030", 
        "tab.activeForeground":"#00ffdd", 
        "tab.border":"#000000", 
        "tab.activeBorder":"#000000", 
        "tab.unfocusedActiveBorder":"#ffffff", 
        "tab.activeBorderTop":"#ffee00", 
        "tab.unfocusedActiveBorderTop":"#7700ff", 
        "tab.inactiveBackground":"#5f5f5f", 
        "tab.inactiveForeground":"#ffffff", 
        //"tab.unfocusedActiveForeground":"#ff0000", 
        //"tab.unfocusedInactiveForeground":"#ff0000", 
        "tab.hoverBackground":"#00137e", 
        //"tab.unfocusedHoverBackground":"#001691", 
        "tab.hoverBorder":"#ff0000", 
        //"tab.unfocusedHoverBorder":"#ff0000", 
        //"tab.activeModifiedBorder":"#ff0000", 
        //"tab.inactiveModifiedBorder":"#ff0000", 
        //"tab.unfocusedActivevModifiedBorder":"#ff0000", 
        //"tab.unfocusedInactiveModifiedBorder":"#ff0000", 
        //"editorPane.background":"#ff0000", 
        "list.activeSelectionBackground": "#b700ff",
        "list.activeSelectionForeground": "#ffffff",
        "list.activeSelectionIconForeground": "#ffffff",
        "list.focusAndSelectionOutline": "#000000",
        "list.dropBackground": "#5e8000",
        "list.focusBackground": "#fbff00",
        "list.focusForeground": "#bbff00",
        // "list.highlightForeground": "#006eff",
        "list.hoverBackground": "#370080",
        // "list.hoverForeground": "#ff0000",
        "list.inactiveSelectionBackground": "#5e8000",
        "list.inactiveSelectionForeground": "#ffffff",
        "list.inactiveFocusBackground": "#7eac00",
        // "list.inactiveFocusOutline": "#ff0000",
        // "list.invalidItemForeground": "#ff0000",
        // "list.errorForeground": "#ff0000",
        // "list.warningForeground": "#ff0000",
        // "listFilterWidget.background": "#ff0000",
        // "listFilterWidget.outline": "#ff0000",
        // "listFilterWidget.noMatchesOutline": "#ff0000",
        // "list.filterMatchBackground": "#2a004d",
        // "list.focusHighlightForeground": "#2a004d",
        "list.filterMatchBorder": "#bbff00",
        "tree.indentGuidesStroke": "#bbff00",

        "scm.providerBorder": "#ffffff",
        // "searchEditor.findMatchBackground": "#9dff00",
        "searchEditor.findMatchBorder": "#bbff00",

        //"terminal.background":"#000000",
        // "terminal.foreground": "#26e045",
        // "terminalCursor.background": "#D8D8D8",
        // "terminalCursor.foreground": "#D8D8D8",
        // "terminal.ansiBlack": "#181818",
        // "terminal.ansiBlue": "#7CAFC2",
        // "terminal.ansiBrightBlack": "#585858",
        // "terminal.ansiBrightBlue": "#7CAFC2",
        // "terminal.ansiBrightCyan": "#86C1B9",
        // "terminal.ansiBrightGreen": "#A1B56C",
        // "terminal.ansiBrightMagenta": "#BA8BAF",
        // "terminal.ansiBrightRed": "#AB4642",
        // "terminal.ansiBrightWhite": "#F8F8F8",
        // "terminal.ansiBrightYellow": "#F7CA88",
        // "terminal.ansiCyan": "#86C1B9",
        // "terminal.ansiGreen": "#A1B56C",
        // "terminal.ansiMagenta": "#BA8BAF",
        // "terminal.ansiRed": "#AB4642",
        // "terminal.ansiWhite": "#D8D8D8",
        // "terminal.ansiYellow": "#F7CA88",
        // "terminal.integrated.cursorBlinking": true,
        // "terminal.integrated.lineHeight": 1.6,
        // "terminal.integrated.letterSpacing": 0.1,

        // "toolbar.activeBackground": "#ff0000",
        
        "dropdown.background": "#ff0000",
        "scrollbar.shadow":"#d493ff",
        "badge.background": "#ae00ff",
        "badge.foreground": "#ffffff",
        "widget.shadow": "#ffffff",
        "icon.foreground": "#ffffff",
        "sash.hoverBorder": "#ffffff",
        "inputOption.activeForeground": "#ffffff",
        "inputOption.activeBorder":"#000000",
        "inputOption.activeBackground":"#ae00ff",
        // "input.placeholderForeground":"#ae00ff",
        "input.border": "#000000",
        // "menu.selectionBorder": "#ff0000"
        // "tree.tableOddRowsBackground":"#ff0000"
        "activityBar.background":"#000000",
        "activityBar.border":"#540099",
        "activityBarBadge.background":"#ae00ff",
        "activityBarBadge.foreground":"#ffffff",
        "activityBar.activeBorder": "#ffffff",
        "sideBar.background":"#000000",
        "sideBar.border": "#540099",
        "sideBarSectionHeader.border": "#540099",
        "sideBarTitle.foreground": "#ffffff",
        "window.inactiveBorder": "#000000",
        // "widget.border": "#ff0000"
        // "list.focusOutline": "#ff0000"
        // "list.inactiveFocusOutline": "#ff0000"
        // "activityBar.dropBorder": "#ff0000"
        


    },
    "editor.tokenColorCustomizations": {
        "[Visual Studio Dark - C++]": {
            "comments": "#00791a",
            //"functions": "#00f7ff",
            "keywords": "#00aeff",
            //"numbers": "#FF0000",
            //"strings": "#bd0000",
            //"types": "#00ffc8",
            //"variables": "#b8c5ff",
            "textMateRules": [
                {
                    "scope": [
                        "entity.name.function", 
                        "keyword.operator.sizeof" 
                    ],
                    "settings": {
                        "foreground": "#00d9ff",
                        "fontStyle": "italic bold",
                    }
                },
                {
                    "scope": [
                        "storage.type", 
                    ],
                    "settings": {
                        "foreground": "#c4aaff",
                        "fontStyle": "italic"
                    }
                },
                {
                    "scope": [
                        "entity.name.type", 
                    ],
                    "settings": {
                        "foreground": "#9899ff",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "storage.type.built-in.primitive.c", 
                    ],
                    "settings": {
                        "foreground": "#9899ff",
                        "fontStyle": ""
                    }
                },
                {
                    "scope": [
                        "entity.name.function.preprocessor", 
                    ],
                    "settings": {
                        "foreground": "#ff0000",
                        "fontStyle": "bold underline"
                    }
                },
                {
                    "scope": [
                        "variable.other.global", 
                    ],
                    "settings": {
                        "foreground": "#d2ff69",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "variable.other.local", 
                    ],
                    "settings": {
                        "foreground": "#d2ff69",
                        "fontStyle": "italic bold"
                    }
                },
                {
                    "scope": [
                        "variable.parameter", 
                    ],
                    "settings": {
                        "foreground": "#d2ff69",
                        "fontStyle": "italic underline"
                    }
                },
                {
                    "scope": [
                        "variable.other.property", 
                    ],
                    "settings": {
                        "foreground": "#d2ff69",
                        "fontStyle": ""
                    }
                },
                {
                    "scope": [
                        "meta.preprocessor", 
                    ],
                    "settings": {
                        "foreground": "#ff6f6f",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "keyword.control.directive", 
                    ],
                    "settings": {
                        "foreground": "#ff6f6f",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "keyword.control", 
                    ],
                    "settings": {
                        "foreground": "#c884ff",
                        "fontStyle": "italic"
                    }
                },
                {
                    "scope": [
                        "storage.modifier", 
                    ],
                    "settings": {
                        "foreground": "#c884ff",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "constant.numeric" 
                    ],
                    "settings": {
                        "foreground": "#ff0000",
                        "fontStyle": ""
                    }
                },
                {
                    "scope": [
                        "variable.other.enummember" 
                    ],
                    "settings": {
                        "foreground": "#da0000",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "string.quoted.double.include" 
                    ],
                    "settings": {
                        "foreground": "#da0000",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "string.quoted.double.include" 
                    ],
                    "settings": {
                        "foreground": "#da0000",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "comment.block.documentation",
                    ],
                    "settings": {
                        "foreground": "#00b82e",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "punctuation.definition.comment.begin.documentation",
                        "punctuation.definition.comment.end.documentation" 
                    ],
                    "settings": {
                        "foreground": "#00b82e",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "comment.block" 
                    ],
                    "settings": {
                        "foreground": "#00b85c",
                        "fontStyle": ""
                    }
                },
                {
                    "scope": [
                        "punctuation.definition.comment.begin",
                        "punctuation.definition.comment.end" 
                    ],
                    "settings": {
                        "foreground": "#00b85c",
                        "fontStyle": ""
                    }
                },
                {
                    "scope": [
                        "comment.line.double-slash" 
                    ],
                    "settings": {
                        "foreground": "#48c400",
                        "fontStyle": "bold"
                    }
                },
                {
                    "scope": [
                        "meta.resultLinePrefix.matchLinePrefix.search"
                    ],
                    "settings": {
                        "foreground": "#4b7a00",
                        "fontStyle": "bold"
                    }
                }
                
                {
                    "scope": [
                        "meta.resultLinePrefix.lineNumber.search",
                        "punctuation.separator"
                    ],
                    "settings": {
                        "foreground": "#9dff00",
                        "fontStyle": "bold"
                    }
                }
            ],

        }
    },
    "files.associations": {
        "bootloader_client_inc.h": "c",
        "*.log.*": "log"
    },
    "logFileHighlighter.customPatterns": [
        {
            "pattern": "<0> ",
            "background": "#00ffaa",
            "border": "1px solid black",
        },
        {
            "pattern": "<1> ",
            "background": "#5e01d8",
            "border": "1px solid black"
        },
        {
            "pattern": "<2> ",
            "background": "#ffee00",
            "border": "1px solid black"
        },
        {
            "pattern": "<3> ",
            "background": "#ff00c8",
            "border": "1px solid black"
        },
        {
            "pattern": "<4> ",
            "background": "#0077ff",
            "border": "1px solid black"
        },
        {
            "pattern": "<5> ",
            "background": "#464646",
            "border": "1px solid black"
        },
        {
            "pattern": "<6> ",
            "background": "#00b11dc4",
            "border": "1px solid black"
        },
        {
            "pattern": "<7> ",
            "background": "#3d0061",
            "border": "1px solid black"
        },
        {
            "pattern": "<8> ",
            "background": "#ff0055",
            "border": "1px solid black"
        },
        {
            "pattern": "<9> ",
            "background": "#f56da6",
            "border": "1px solid black"
        },
        {
            "pattern": "<10> ",
            "background": "#7ac501",
            "border": "1px solid black"
        },
        {
            "pattern": "<11> ",
            "background": "#ff8800",
            "border": "1px solid black"
        },
        {
            "pattern": "<12> ",
            "background": "#00ffaa",
            "border": "1px solid black"
        },
        {
            "pattern": "<13> ",
            "background": "#5e01d8",
            "border": "1px solid black"
        },
        {
            "pattern": "<14> ",
            "background": "#ffee00",
            "border": "1px solid black"
        },
        {
            "pattern": "<15> ",
            "background": "#ff00c8",
            "border": "1px solid black"
        },
        {
            "pattern": "<16> ",
            "background": "#0077ff",
            "border": "1px solid black"
        },
        {
            "pattern": "<17> ",
            "background": "#ae00ff",
            "border": "1px solid black"
        },
        {
            "pattern": "<255> ",
            "foreground": "#000000",
            "background": "#ffffff"
        },
    ]
    /*GXT-Doxygen Documentation Generator插件使用-Begin*/
    //"doxdocgen.generic.authorName": "thou",
    //"doxdocgen.generic.authorEmail": "tianjia@jiduauto.com",
    //"doxdocgen.generic.authorTag": "@Author : {author} email:{email}",
    //"doxdocgen.generic.dateFormat": "YYYY-MM-DD",
    //"doxdocgen.generic.dateTemplate": "@Creat Date : {date}",
    //"doxdocgen.generic.briefTemplate": "@brief{indent:8}{text}",
    //"doxdocgen.file.fileTemplate": "@File Name: {name}",
    //"doxdocgen.file.versionTag": "@Version : 1.0",
    //"doxdocgen.file.copyrightTag": [
    //    "@copyright Copyright (c) {year} XXXX技术有限公司"
    //],
    //"doxdocgen.file.customTag": [
    //    "modification history :",
    //    "Date:       Version:      Author:     ",
    //    "Changes: ",
    //],
    //"doxdocgen.file.fileOrder": [
    //    "file",
    //    "brief",
    //    "author",
    //    "version",
    //    "date",
    //    "empty",
    //],
    "doxdocgen.generic.includeTypeAtReturn": true,
    "doxdocgen.generic.paramTemplate": "@param{indent:8}{param}: ",
    "doxdocgen.generic.returnTemplate": "@return{indent:8}{type}: ",
    "doxdocgen.generic.order": [
        "brief",
        "tparam",
        "param",
        "return"
    ],
    
    
    "doxdocgen.generic.splitCasingSmartText": true,

    // "subtleBrackets.style": {
    //     // https://vshaxe.github.io/vscode-extern/vscode/DecorationRenderOptions.html
    //     // rounded border:
    //     "borderWidth": "2px",
    //     "borderStyle": "solid",//"dotted",
    //     "borderColor": "#bbff00",// "#99999977",
        
    //     "borderRadius": "8px",
    //     // "fontWeight": "bold",
    //     // "backgroundColor": "#99999922",
    //     // "letterSpacing": "3px",
        
    //     },

    "workbench.iconTheme": "material-icon-theme",
    "diffEditor.ignoreTrimWhitespace": false,


}

太棒了!我们现在将 **Superset 自动轮播系统** 完整、专业地呈现为一个可用于生产环境的部署项目。以下是 **超详细部署方案**,涵盖: - ✅ 项目结构 - ✅ Python 虚拟环境配置 - ✅ `.env` 安全管理 - ✅ 日志系统 - ✅ WSL2 + Ubuntu 22.04 部署全流程 - ✅ 开机自启与浏览器全屏播放(Kiosk 模式) - ✅ 维护建议 --- # 🎯 项目名称:`superset-kiosk` > 用于大屏展示 Superset Dashboard 的自动轮播系统 > 支持标签过滤、定时切换、Token 刷新、日志记录、安全配置 --- ## 📁 一、完整项目结构(最终版) ```bash superset-kiosk/ ├── .env # 环境变量(敏感信息,不提交 Git) ├── .gitignore # 忽略日志、虚拟环境等 ├── requirements.txt # 依赖列表 ├── main.py # 主程序入口 │ ├── config/ │ ├── __init__.py │ └── settings.py # 从 .env 加载所有配置 │ ├── auth/ │ ├── __init__.py │ └── superset_auth.py # 登录 + token 管理(含刷新机制) │ ├── api/ │ ├── __init__.py │ └── dashboard_api.py # 获取 dashboard 列表(支持 tag 过滤) │ ├── kiosk/ │ ├── __init__.py │ └── html_generator.py # 生成自动轮播 HTML 页面 │ ├── utils/ │ ├── __init__.py │ ├── logger.py # 全局日志模块(文件 + 控制台) │ └── helpers.py # 打开浏览器等工具函数 │ ├── static/ # 输出目录 │ └── superset_kiosk.html # 自动生成的轮播页面 │ ├── logs/ # 日志目录(运行后自动生成) │ └── app_20250405.log # 按日期命名的日志文件 │ └── README.md # 使用说明文档 ``` --- ## 🔐 二、`.env` 文件(根目录)—— 安全配置 ⚠️ 此文件 **绝不能提交到 Git** ```env # ==================== Superset 连接 ==================== SUPERSET_BASE_URL=http://localhost:8088 SUPERSET_USERNAME=admin SUPERSET_PASSWORD=admin SUPERSET_PROVIDER=db # ==================== 轮播行为 ==================== KIOSK_REFRESH_INTERVAL=15 # 每个仪表盘显示时间(秒) KIOSK_RANDOM_ORDER=false # 是否随机顺序(true/false) KIOSK_FILTER_TAG=kiosk # 只加载带有此标签的 dashboard;设为 None 表示不限制 KIOSK_DASHBOARD_IDS=1,3,5 # 指定 ID 列表;留空表示获取全部 # ==================== 输出路径 ==================== KIOSK_OUTPUT_HTML=../static/superset_kiosk.html ``` > 💡 提示: > - `KIOSK_FILTER_TAG=None` 表示不过滤标签 > - `KIOSK_DASHBOARD_IDS=` 留空表示获取所有 dashboard --- ## 📦 三、`requirements.txt` ```txt requests==2.31.0 python-dotenv==1.0.1 ``` 安装命令: ```bash pip install -r requirements.txt ``` --- ## ⚙️ 四、Python 虚拟环境(推荐使用) ### 1. 创建虚拟环境 ```bash python3 -m venv venv ``` ### 2. 激活虚拟环境 ```bash source venv/bin/activate ``` 激活后提示符变为: ```bash (venv) user@hostname:~/superset-kiosk$ ``` ### 3. 安装依赖 ```bash (venv) pip install -r requirements.txt ``` ### 4. (可选)退出虚拟环境 ```bash (venv) deactivate ``` --- ## 🧩 五、各模块代码实现(最终版本) > 下面是每个文件的 **完整、可复制粘贴代码** --- ### ✅ `config/settings.py` ```python # config/settings.py import os from dotenv import load_dotenv load_dotenv() # 加载 .env # === Superset 配置 === SUPerset_BASE_URL = os.getenv("SUPERSET_BASE_URL") USERNAME = os.getenv("SUPERSET_USERNAME") PASSWORD = os.getenv("SUPERSET_PASSWORD") PROVIDER = os.getenv("SUPERSET_PROVIDER", "db") # === 轮播配置 === try: REFRESH_INTERVAL_SEC = int(os.getenv("KIOSK_REFRESH_INTERVAL", 10)) except ValueError: REFRESH_INTERVAL_SEC = 10 RANDOM_ORDER = os.getenv("KIOSK_RANDOM_ORDER", "false").lower() == "true" FILTER_BY_TAG = os.getenv("KIOSK_FILTER_TAG", None) if FILTER_BY_TAG in ["None", "", "null"]: FILTER_BY_TAG = None # 解析 DASHBOARD_IDS DASHBOARD_IDS_RAW = os.getenv("KIOSK_DASHBOARD_IDS", "").strip() if DASHBOARD_IDS_RAW: try: DASHBOARD_IDS = [int(x.strip()) for x in DASHBOARD_IDS_RAW.split(",") if x.strip().isdigit()] except: DASHBOARD_IDS = [] else: DASHBOARD_IDS = [] # 输出路径 OUTPUT_HTML = os.getenv("KIOSK_OUTPUT_HTML", "../static/superset_kiosk.html") ``` --- ### ✅ `utils/logger.py` ```python # utils/logger.py import logging import os from datetime import datetime def setup_logger(): logger = logging.getLogger("superset_kiosk") if logger.handlers: return logger # 防止重复添加 handler logger.setLevel(logging.DEBUG) # 创建 logs 目录 log_dir = os.path.join(os.path.dirname(__file__), "..", "logs") os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, f"app_{datetime.now().strftime('%Y%m%d')}.log") # 文件处理器(DEBUG+) file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setLevel(logging.DEBUG) file_formatter = logging.Formatter( '%(asctime)s | %(levelname)-8s | %(name)s:%(lineno)d | %(message)s' ) file_handler.setFormatter(file_formatter) # 控制台处理器(INFO+) console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_formatter = logging.Formatter('%(levelname)s: %(message)s') console_handler.setFormatter(console_formatter) logger.addHandler(file_handler) logger.addHandler(console_handler) return logger logger = setup_logger() ``` --- ### ✅ `auth/superset_auth.py` ```python # auth/superset_auth.py from utils.logger import logger import requests import json from datetime import datetime, timedelta from config.settings import SUPerset_BASE_URL, USERNAME, PASSWORD, PROVIDER class SupersetAuth: def __init__(self): self.base_url = SUPerset_BASE_URL self.access_token = None self.refresh_token = None self.token_expires_at = None def login(self): logger.info("正在登录 Superset...") url = f"{self.base_url}/api/v1/security/login" payload = {"username": USERNAME, "password": PASSWORD, "provider": PROVIDER} headers = {"Content-Type": "application/json"} try: response = requests.post(url, data=json.dumps(payload), headers=headers) response.raise_for_status() data = response.json() self.access_token = data["access_token"] self.refresh_token = data.get("refresh_token") self.token_expires_at = datetime.now() + timedelta(minutes=29) logger.info("✅ 登录成功") return self.access_token except Exception as e: logger.error(f"❌ 登录失败: {e}") raise def is_token_expired(self): return datetime.now() >= self.token_expires_at def refresh_token(self): if not self.refresh_token: logger.warning("⚠️ 无 refresh_token,重新登录...") return self.login() url = f"{self.base_url}/api/v1/security/refresh" headers = {"Authorization": f"Bearer {self.refresh_token}"} try: response = requests.post(url, headers=headers) if response.status_code == 200: self.access_token = response.json()["access_token"] self.token_expires_at = datetime.now() + timedelta(minutes=29) logger.info("🔄 Token 已刷新") return self.access_token else: logger.warning(f"⚠️ Refresh 失败 ({response.status_code}),重新登录...") return self.login() except Exception as e: logger.error(f"❌ Refresh 异常: {e}") return self.login() def get_access_token(self): if not self.access_token or self.is_token_expired(): return self.refresh_token() return self.access_token ``` --- ### ✅ `api/dashboard_api.py` ```python # api/dashboard_api.py from utils.logger import logger import requests import json from auth.superset_auth import SupersetAuth from config.settings import SUPerset_BASE_URL, FILTER_BY_TAG class DashboardAPI: def __init__(self, auth_client: SupersetAuth): self.auth = auth_client self.base_url = SUPerset_BASE_URL def get_all_dashboards(self): logger.info("正在获取 Dashboard 列表...") url = f"{self.base_url}/api/v1/dashboard/" headers = {"Authorization": f"Bearer {self.auth.get_access_token()}"} query = {"order_column": "id", "order_direction": "asc"} if FILTER_BY_TAG: query["filters"] = [{"col": "tags", "opr": "rel_m_m", "value": FILTER_BY_TAG}] params = {"q": json.dumps(query)} try: response = requests.get(url, headers=headers, params=params) response.raise_for_status() result = response.json()["result"] dashboards = [ {"id": d["id"], "title": d["dashboard_title"], "url": f"{self.base_url}{d['url']}"} for d in result if d["id"] and d["url"] ] logger.info(f"📊 成功获取 {len(dashboards)} 个仪表盘") return dashboards except Exception as e: logger.error(f"❌ 获取 Dashboard 失败: {e}") raise ``` --- ### ✅ `kiosk/html_generator.py` ```python # kiosk/html_generator.py from utils.logger import logger import os import json from config.settings import OUTPUT_HTML, REFRESH_INTERVAL_SEC, RANDOM_ORDER class HTMLGenerator: @staticmethod def generate(dashboards): logger.info("生成轮播页面中...") if not dashboards: logger.warning("⚠️ 传入的 dashboard 列表为空") return if RANDOM_ORDER: from random import shuffle shuffle(dashboards) logger.debug("🔀 已启用随机顺序") os.makedirs(os.path.dirname(OUTPUT_HTML), exist_ok=True) html_content = f''' <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Superset Dashboard 轮播</title> <style> body, html {{ margin: 0; padding: 0; overflow: hidden; background: #000; color: white; font-family: Arial, sans-serif; }} #container {{ width: 100vw; height: 100vh; position: relative; }} iframe {{ width: 100%; height: 100%; border: none; }} .info-bar {{ position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.7); color: white; padding: 5px 10px; border-radius: 4px; z-index: 100; font-size: 14px; }} </style> </head> <body> <div id="container"> <iframe id="dash-frame" src="" frameborder="0"></iframe> <div class="info-bar" id="info">Loading...</div> </div> <script> const dashboards = {json.dumps(dashboards)}; let currentIndex = 0; const intervalSec = {REFRESH_INTERVAL_SEC}; function showNextDashboard() {{ if (dashboards.length === 0) {{ document.getElementById("info").textContent = "No dashboards found."; return; }} const dash = dashboards[currentIndex]; document.getElementById("dash-frame").src = dash.url + "?standalone=3"; document.getElementById("info").textContent = `[${{currentIndex + 1}}/${{dashboards.length}}] ${{dash.title}}`; currentIndex = (currentIndex + 1) % dashboards.length; }} showNextDashboard(); setInterval(showNextDashboard, intervalSec * 1000); </script> </body> </html> ''' with open(OUTPUT_HTML, "w", encoding="utf-8") as f: f.write(html_content) logger.info(f"✅ 轮播页面已生成: {os.path.abspath(OUTPUT_HTML)}") ``` --- ### ✅ `utils/helpers.py` ```python # utils/helpers.py import webbrowser import os def open_in_browser(filepath): abs_path = os.path.abspath(filepath) webbrowser.open(f"file://{abs_path}") print(f"🌐 已打开浏览器: file://{abs_path}") ``` --- ### ✅ `main.py` ```python # main.py from utils.logger import logger from auth.superset_auth import SupersetAuth from api.dashboard_api import DashboardAPI from kiosk.html_generator import HTMLGenerator from utils.helpers import open_in_browser from config.settings import DASHBOARD_IDS def main(): try: logger.info("🚀 启动 Superset 轮播系统") auth = SupersetAuth() auth.login() dashboard_api = DashboardAPI(auth) all_dashboards = dashboard_api.get_all_dashboards() if DASHBOARD_IDS: filtered = [d for d in all_dashboards if d["id"] in DASHBOARD_IDS] if not filtered: logger.warning("⚠️ 没有匹配 ID 的 Dashboard,请检查 .env 中 KIOSK_DASHBOARD_IDS 设置") return logger.info(f"✅ 已根据 ID 过滤出 {len(filtered)} 个 Dashboard") else: filtered = all_dashboards logger.info(f"✅ 使用全部 {len(filtered)} 个 Dashboard") HTMLGenerator.generate(filtered) open_in_browser("../static/superset_kiosk.html") logger.info(f"🎯 轮播系统启动完成!每 {REFRESH_INTERVAL_SEC} 秒切换一次") except KeyboardInterrupt: logger.info("👋 用户中断程序") except Exception as e: logger.critical(f"💥 系统运行出错: {e}", exc_info=True) if __name__ == "__main__": main() ``` --- ## 🐧 六、WSL2 + Ubuntu 22.04 详细部署步骤 ### 步骤 1:打开 PowerShell 启动 WSL ```powershell wsl ``` 确认你是 Ubuntu 用户: ```bash whoami # 应输出你的用户名 pwd # 应在 /home/yourname ``` ### 步骤 2:更新系统 & 安装基础工具 ```bash sudo apt update && sudo apt upgrade -y sudo apt install python3 python3-pip git -y ``` 验证: ```bash python3 --version # 推荐 3.10+ pip3 --version ``` ### 步骤 3:创建项目目录 ```bash mkdir ~/superset-kiosk cd ~/superset-kiosk ``` ### 步骤 4:初始化项目结构 ```bash mkdir -p config auth api kiosk utils static logs touch config/__init__.py auth/__init__.py api/__init__.py kiosk/__init__.py utils/__init__.py touch main.py requirements.txt .env .gitignore ``` ### 步骤 5:粘贴所有代码文件 依次创建并写入上面提供的各 `.py` 文件内容。 ### 步骤 6:创建 `.gitignore` ```bash nano .gitignore ``` 内容: ``` venv/ __pycache__/ *.pyc .env logs/ *.log static/*.html .DS_Store ``` ### 步骤 7:创建虚拟环境并安装依赖 ```bash python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt ``` ### 步骤 8:编辑 `.env` 文件 ```bash nano .env ``` 粘贴前面提供的 `.env` 内容,并根据你的 Superset 修改 URL 和账号。 ### 步骤 9:运行系统 ```bash source venv/bin/activate # 每次运行前激活 python main.py ``` --- ## 🖥 七、Windows 浏览器全屏播放(Kiosk 模式) ### 方法:Chrome Kiosk 模式 1. 打开 Chrome 快捷方式属性 2. 目标填入: ```cmd "C:\Program Files\Google\Chrome\Application\chrome.exe" --kiosk --start-fullscreen --app=file://\\wsl$\Ubuntu\home\yourname\superset-kiosk\static\superset_kiosk.html ``` 3. 勾选“以全屏模式运行” 4. 放入「开始」→「启动」文件夹,实现开机自动播放 --- ## 🛠 八、维护与扩展建议 | 功能 | 实现方式 | |------|----------| | 自动重启脚本 | 使用 `cron` 每天凌晨重启 | | 错误报警 | 添加邮件或微信推送(如 `smtplib` 或 Server酱) | | 多环境配置 | `.env.dev`, `.env.prod` + `ENV=prod` 切换 | | Docker 化 | 编写 `Dockerfile` 和 `docker-compose.yml` | | systemd 守护 | 在 WSL 中注册为服务自动运行 | --- ## 📄 示例日志输出(`logs/app_20250405.log`) ``` 2025-04-05 14:20:01,123 | INFO | superset_kiosk:25 | 🚀 启动 Superset 轮播系统 2025-04-05 14:20:01,124 | INFO | superset_auth:30 | 正在登录 Superset... 2025-04-05 14:20:02,200 | INFO | superset_auth:60 | ✅ 登录成功 2025-04-05 14:20:02,201 | INFO | dashboard_api:18 | 正在获取 Dashboard 列表... 2025-04-05 14:20:03,305 | INFO | dashboard_api:45 | 📊 成功获取 3 个仪表盘 2025-04-05 14:20:03,306 | INFO | html_generator:20 | 生成轮播页面中... 2025-04-05 14:20:03,310 | INFO | html_generator:50 | ✅ 轮播页面已生成 2025-04-05 14:20:03,311 | INFO | main:40 | 🎯 轮播系统启动完成!每 15 秒切换一次 ``` --- ### 根据以上内容,此处应该如何修改
最新发布
10-30
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值