Merge commit '964a81c37cd165b9f8df20db87fd915ba03d10b5' into release
This commit is contained in:
commit
69bcfa4bd0
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-14 16:25+0800\n"
|
||||
"POT-Creation-Date: 2025-02-26 11:12+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -70,6 +70,9 @@ msgstr ""
|
||||
msgid "Accept"
|
||||
msgstr ""
|
||||
|
||||
msgid "Activate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Adaptive Bed Leveling"
|
||||
msgstr ""
|
||||
|
||||
@ -234,9 +237,6 @@ msgstr ""
|
||||
msgid "Confirm factory reset?\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Connecting"
|
||||
msgstr ""
|
||||
|
||||
@ -306,15 +306,24 @@ msgstr ""
|
||||
msgid "Do you want to recover %s?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elapsed trial time:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Elapsed:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Emergency Stop"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Registration Code"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable screen power management"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enabled successfully"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
@ -462,9 +471,6 @@ msgstr ""
|
||||
msgid "Host"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hostname"
|
||||
msgstr ""
|
||||
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
@ -507,6 +513,18 @@ msgstr ""
|
||||
msgid "Job Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Key is empty"
|
||||
msgstr ""
|
||||
|
||||
msgid "Key is invalid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Key is valid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Key:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Klipper Restart"
|
||||
msgstr ""
|
||||
|
||||
@ -559,6 +577,9 @@ msgstr ""
|
||||
msgid "Leveling only in the actual print area"
|
||||
msgstr ""
|
||||
|
||||
msgid "License key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Limits"
|
||||
msgstr ""
|
||||
|
||||
@ -682,6 +703,9 @@ msgstr ""
|
||||
msgid "Not Inserted"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not activated"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not all screens support this"
|
||||
msgstr ""
|
||||
|
||||
@ -745,12 +769,18 @@ msgstr ""
|
||||
msgid "Perform a full upgrade?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Permanent Activation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pins"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please ensure that the Probe Calibrate has been performed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please enter a key to activate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please enter a valid number"
|
||||
msgstr ""
|
||||
|
||||
@ -835,6 +865,9 @@ msgstr ""
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remaining Time:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove network"
|
||||
msgstr ""
|
||||
|
||||
@ -844,6 +877,12 @@ msgstr ""
|
||||
msgid "Reprint"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset successfully"
|
||||
msgstr ""
|
||||
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
@ -923,6 +962,9 @@ msgstr ""
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
|
||||
msgid "Serial Number:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
@ -944,6 +986,9 @@ msgstr ""
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Skip"
|
||||
msgstr ""
|
||||
|
||||
msgid "Slicer"
|
||||
msgstr ""
|
||||
|
||||
@ -983,15 +1028,15 @@ msgstr ""
|
||||
msgid "Start testing the Z offset value of the second nozzle?\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting WiFi Association"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting recovery for"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting update for"
|
||||
msgstr ""
|
||||
|
||||
msgid "State:"
|
||||
msgstr ""
|
||||
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
@ -1019,6 +1064,9 @@ msgstr ""
|
||||
msgid "The system will reboot!"
|
||||
msgstr ""
|
||||
|
||||
msgid "This device is not activated and is available for trial use only"
|
||||
msgstr ""
|
||||
|
||||
msgid "This operation is about to print the model"
|
||||
msgstr ""
|
||||
|
||||
@ -1037,6 +1085,15 @@ msgstr ""
|
||||
msgid "Total:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Trial Time:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unknown Heater"
|
||||
msgstr ""
|
||||
|
||||
@ -1139,6 +1196,9 @@ msgid_plural "hours"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "license"
|
||||
msgstr ""
|
||||
|
||||
msgid "macros that use 'rename_existing' are hidden"
|
||||
msgstr ""
|
||||
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: KlipperScreen\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-07 17:11+0800\n"
|
||||
"POT-Creation-Date: 2025-02-26 11:12+0800\n"
|
||||
"PO-Revision-Date: 2024-06-03 19:09+0000\n"
|
||||
"Last-Translator: wsj20050623 <2129426599@qq.com>\n"
|
||||
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
|
||||
@ -63,7 +63,7 @@ msgid "ADXL Not Configured"
|
||||
msgstr "未配置加速度计"
|
||||
|
||||
msgid "Abort"
|
||||
msgstr "中止打印"
|
||||
msgstr "终止"
|
||||
|
||||
msgid "Acceleration:"
|
||||
msgstr "加速度:"
|
||||
@ -71,6 +71,9 @@ msgstr "加速度:"
|
||||
msgid "Accept"
|
||||
msgstr "确定"
|
||||
|
||||
msgid "Activate"
|
||||
msgstr "激活"
|
||||
|
||||
msgid "Adaptive Bed Leveling"
|
||||
msgstr "自适应床调平"
|
||||
|
||||
@ -235,9 +238,6 @@ msgstr "紧急停止需要二次确认"
|
||||
msgid "Confirm factory reset?\n"
|
||||
msgstr "确定恢复出厂设置?\n"
|
||||
|
||||
msgid "Connected"
|
||||
msgstr "已连接"
|
||||
|
||||
msgid "Connecting"
|
||||
msgstr "连接中"
|
||||
|
||||
@ -307,15 +307,24 @@ msgstr "您要忘记或断开%s的连接吗?"
|
||||
msgid "Do you want to recover %s?"
|
||||
msgstr "你想恢复 %s 吗?"
|
||||
|
||||
msgid "Elapsed trial time:"
|
||||
msgstr "已用时间:"
|
||||
|
||||
msgid "Elapsed:"
|
||||
msgstr "已用时间:"
|
||||
|
||||
msgid "Emergency Stop"
|
||||
msgstr "紧急停止"
|
||||
|
||||
msgid "Enable Registration Code"
|
||||
msgstr "启用注册码"
|
||||
|
||||
msgid "Enable screen power management"
|
||||
msgstr "开启屏幕功率管理功能"
|
||||
|
||||
msgid "Enabled successfully"
|
||||
msgstr "启用成功"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "错误"
|
||||
|
||||
@ -463,9 +472,6 @@ msgstr "Z轴归零"
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
||||
msgid "Hostname"
|
||||
msgstr "主机名"
|
||||
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
@ -508,6 +514,18 @@ msgstr "设置文件可能存在错误"
|
||||
msgid "Job Status"
|
||||
msgstr "工作状态"
|
||||
|
||||
msgid "Key is empty"
|
||||
msgstr "密钥为空"
|
||||
|
||||
msgid "Key is invalid"
|
||||
msgstr "密钥无效"
|
||||
|
||||
msgid "Key is valid"
|
||||
msgstr "密钥有效"
|
||||
|
||||
msgid "Key:"
|
||||
msgstr "密钥:"
|
||||
|
||||
msgid "Klipper Restart"
|
||||
msgstr "重启Klipper"
|
||||
|
||||
@ -560,6 +578,9 @@ msgstr "剩余时间:"
|
||||
msgid "Leveling only in the actual print area"
|
||||
msgstr "仅在实际打印区域进行床调平"
|
||||
|
||||
msgid "License key"
|
||||
msgstr "许可证密钥"
|
||||
|
||||
msgid "Limits"
|
||||
msgstr "打印机限制"
|
||||
|
||||
@ -683,6 +704,9 @@ msgstr "未启用"
|
||||
msgid "Not Inserted"
|
||||
msgstr "未装载"
|
||||
|
||||
msgid "Not activated"
|
||||
msgstr "未激活"
|
||||
|
||||
msgid "Not all screens support this"
|
||||
msgstr "不一定所有屏幕都支持这项功能"
|
||||
|
||||
@ -745,12 +769,18 @@ msgstr "已暂停"
|
||||
msgid "Perform a full upgrade?"
|
||||
msgstr "是否更新全部?"
|
||||
|
||||
msgid "Permanent Activation"
|
||||
msgstr "永久激活"
|
||||
|
||||
msgid "Pins"
|
||||
msgstr "引脚"
|
||||
|
||||
msgid "Please ensure that the Probe Calibrate has been performed"
|
||||
msgstr "请确保已完成探针偏移校准"
|
||||
|
||||
msgid "Please enter a key to activate"
|
||||
msgstr "请输入密钥激活"
|
||||
|
||||
msgid "Please enter a valid number"
|
||||
msgstr "请输入有效的数字"
|
||||
|
||||
@ -835,6 +865,9 @@ msgstr "参考"
|
||||
msgid "Refresh"
|
||||
msgstr "检查更新"
|
||||
|
||||
msgid "Remaining Time:"
|
||||
msgstr "剩余时间:"
|
||||
|
||||
msgid "Remove network"
|
||||
msgstr "删除网络"
|
||||
|
||||
@ -844,6 +877,12 @@ msgstr "重命名/移动:"
|
||||
msgid "Reprint"
|
||||
msgstr "再次打印"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
msgid "Reset successfully"
|
||||
msgstr "重置成功"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "重启"
|
||||
|
||||
@ -923,6 +962,9 @@ msgstr "选择型号"
|
||||
msgid "Send"
|
||||
msgstr "发送"
|
||||
|
||||
msgid "Serial Number:"
|
||||
msgstr "序列号:"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "设置"
|
||||
|
||||
@ -944,6 +986,9 @@ msgstr "散热风扇"
|
||||
msgid "Size"
|
||||
msgstr "大小"
|
||||
|
||||
msgid "Skip"
|
||||
msgstr "跳过"
|
||||
|
||||
msgid "Slicer"
|
||||
msgstr "切片软件"
|
||||
|
||||
@ -983,15 +1028,15 @@ msgstr "开始"
|
||||
msgid "Start testing the Z offset value of the second nozzle?\n"
|
||||
msgstr "开始测试第二个喷嘴的 Z 偏移值吗?\n"
|
||||
|
||||
msgid "Starting WiFi Association"
|
||||
msgstr "开始WiFi连接"
|
||||
|
||||
msgid "Starting recovery for"
|
||||
msgstr "开始恢复"
|
||||
|
||||
msgid "Starting update for"
|
||||
msgstr "开始更新"
|
||||
|
||||
msgid "State:"
|
||||
msgstr "状态:"
|
||||
|
||||
msgid "System"
|
||||
msgstr "系统"
|
||||
|
||||
@ -1019,6 +1064,9 @@ msgstr ""
|
||||
msgid "The system will reboot!"
|
||||
msgstr "系统将重新启动!"
|
||||
|
||||
msgid "This device is not activated and is available for trial use only"
|
||||
msgstr "此设备尚未激活,仅供试用"
|
||||
|
||||
msgid "This operation is about to print the model"
|
||||
msgstr "此操作即将开始打印模型"
|
||||
|
||||
@ -1037,6 +1085,15 @@ msgstr "打印过程中超过倒计时后关闭屏幕"
|
||||
msgid "Total:"
|
||||
msgstr "合计:"
|
||||
|
||||
msgid "Trial"
|
||||
msgstr "试用"
|
||||
|
||||
msgid "Trial Time:"
|
||||
msgstr "试用时间:"
|
||||
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
msgid "Unknown Heater"
|
||||
msgstr "未知加热器"
|
||||
|
||||
@ -1136,6 +1193,9 @@ msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "小时"
|
||||
|
||||
msgid "license"
|
||||
msgstr "许可证"
|
||||
|
||||
msgid "macros that use 'rename_existing' are hidden"
|
||||
msgstr "使用\"rename_existing\"的宏将被隐藏"
|
||||
|
||||
@ -1168,6 +1228,15 @@ msgid "second"
|
||||
msgid_plural "seconds"
|
||||
msgstr[0] "秒"
|
||||
|
||||
#~ msgid "Connected"
|
||||
#~ msgstr "已连接"
|
||||
|
||||
#~ msgid "Hostname"
|
||||
#~ msgstr "主机名"
|
||||
|
||||
#~ msgid "Starting WiFi Association"
|
||||
#~ msgstr "开始WiFi连接"
|
||||
|
||||
#~ msgid "Restoring Left extruder temperature, this may take some time"
|
||||
#~ msgstr "恢复左挤出机温度,这可能需要一些时间"
|
||||
|
||||
@ -1243,9 +1312,6 @@ msgstr[0] "秒"
|
||||
#~ msgid "KlipperScreen will drop support in June 2024"
|
||||
#~ msgstr "KlipperScreen将于2024 年6月停止适配"
|
||||
|
||||
#~ msgid "Print Time"
|
||||
#~ msgstr "打印用时"
|
||||
|
||||
#~ msgid "Warning"
|
||||
#~ msgstr "警告"
|
||||
|
||||
@ -1420,6 +1486,3 @@ msgstr[0] "秒"
|
||||
|
||||
#~ msgid "Load Average"
|
||||
#~ msgstr "平均负载"
|
||||
|
||||
#~ msgid "Screen Blanking Time"
|
||||
#~ msgstr "关闭屏幕时间"
|
||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: KlipperScreen\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-07 17:11+0800\n"
|
||||
"POT-Creation-Date: 2025-02-26 11:12+0800\n"
|
||||
"PO-Revision-Date: 2024-06-01 12:09+0000\n"
|
||||
"Last-Translator: 峻瑜哥 <a728728728@gmail.com>\n"
|
||||
"Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/"
|
||||
@ -71,6 +71,9 @@ msgstr "加速度:"
|
||||
msgid "Accept"
|
||||
msgstr "接受"
|
||||
|
||||
msgid "Activate"
|
||||
msgstr "激活"
|
||||
|
||||
msgid "Adaptive Bed Leveling"
|
||||
msgstr "自適應床調平"
|
||||
|
||||
@ -235,9 +238,6 @@ msgstr "緊急停止需確認"
|
||||
msgid "Confirm factory reset?\n"
|
||||
msgstr "確定恢復出廠設置?\n"
|
||||
|
||||
msgid "Connected"
|
||||
msgstr "已連線"
|
||||
|
||||
msgid "Connecting"
|
||||
msgstr "連接中"
|
||||
|
||||
@ -307,15 +307,24 @@ msgstr "您要忘記或斷開%s的連接嗎?"
|
||||
msgid "Do you want to recover %s?"
|
||||
msgstr "你想恢復 %s 嗎?"
|
||||
|
||||
msgid "Elapsed trial time:"
|
||||
msgstr "已用時間"
|
||||
|
||||
msgid "Elapsed:"
|
||||
msgstr "已用時間:"
|
||||
|
||||
msgid "Emergency Stop"
|
||||
msgstr "緊急停止"
|
||||
|
||||
msgid "Enable Registration Code"
|
||||
msgstr "啟用註冊碼"
|
||||
|
||||
msgid "Enable screen power management"
|
||||
msgstr "開啟螢幕功率管理功能"
|
||||
|
||||
msgid "Enabled successfully"
|
||||
msgstr "啟用成功"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "錯誤"
|
||||
|
||||
@ -463,9 +472,6 @@ msgstr "Z軸歸零"
|
||||
msgid "Host"
|
||||
msgstr "主機"
|
||||
|
||||
msgid "Hostname"
|
||||
msgstr "主機名"
|
||||
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
@ -508,6 +514,18 @@ msgstr "設置文件可能存在錯誤"
|
||||
msgid "Job Status"
|
||||
msgstr "工作狀態"
|
||||
|
||||
msgid "Key is empty"
|
||||
msgstr "密鑰為空"
|
||||
|
||||
msgid "Key is invalid"
|
||||
msgstr "密鑰無效"
|
||||
|
||||
msgid "Key is valid"
|
||||
msgstr "密鑰有效"
|
||||
|
||||
msgid "Key:"
|
||||
msgstr "密鑰:"
|
||||
|
||||
msgid "Klipper Restart"
|
||||
msgstr "重啟Klipper"
|
||||
|
||||
@ -560,6 +578,9 @@ msgstr "剩餘時間:"
|
||||
msgid "Leveling only in the actual print area"
|
||||
msgstr "僅在實際打印區域進行床調平"
|
||||
|
||||
msgid "License key"
|
||||
msgstr "許可證密鑰"
|
||||
|
||||
msgid "Limits"
|
||||
msgstr "列印機限制"
|
||||
|
||||
@ -683,6 +704,9 @@ msgstr "未啟用"
|
||||
msgid "Not Inserted"
|
||||
msgstr "未裝載"
|
||||
|
||||
msgid "Not activated"
|
||||
msgstr "未激活"
|
||||
|
||||
msgid "Not all screens support this"
|
||||
msgstr "不一定所有屏幕都支持這項功能"
|
||||
|
||||
@ -745,12 +769,18 @@ msgstr "已暫停"
|
||||
msgid "Perform a full upgrade?"
|
||||
msgstr "是否全部升級?"
|
||||
|
||||
msgid "Permanent Activation"
|
||||
msgstr "永久激活"
|
||||
|
||||
msgid "Pins"
|
||||
msgstr "引腳"
|
||||
|
||||
msgid "Please ensure that the Probe Calibrate has been performed"
|
||||
msgstr "請確保已完成探針偏移校準"
|
||||
|
||||
msgid "Please enter a key to activate"
|
||||
msgstr "請輸入密鑰激活"
|
||||
|
||||
msgid "Please enter a valid number"
|
||||
msgstr "請輸入有效的數字"
|
||||
|
||||
@ -835,6 +865,9 @@ msgstr "參考"
|
||||
msgid "Refresh"
|
||||
msgstr "重新整理"
|
||||
|
||||
msgid "Remaining Time:"
|
||||
msgstr "剩余時間:"
|
||||
|
||||
msgid "Remove network"
|
||||
msgstr "刪除網絡"
|
||||
|
||||
@ -844,6 +877,12 @@ msgstr "重新命名/移動:"
|
||||
msgid "Reprint"
|
||||
msgstr "再次打印"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
msgid "Reset successfully"
|
||||
msgstr "重置成功"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "重啟"
|
||||
|
||||
@ -923,6 +962,9 @@ msgstr "選擇型號"
|
||||
msgid "Send"
|
||||
msgstr "發送"
|
||||
|
||||
msgid "Serial Number:"
|
||||
msgstr "序列號"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "設定"
|
||||
|
||||
@ -944,6 +986,9 @@ msgstr "散熱風扇"
|
||||
msgid "Size"
|
||||
msgstr "大小"
|
||||
|
||||
msgid "Skip"
|
||||
msgstr "跳過"
|
||||
|
||||
msgid "Slicer"
|
||||
msgstr "切片軟體"
|
||||
|
||||
@ -983,15 +1028,15 @@ msgstr "開始"
|
||||
msgid "Start testing the Z offset value of the second nozzle?\n"
|
||||
msgstr "開始測試第二個噴嘴的 Z 偏移值嗎?\n"
|
||||
|
||||
msgid "Starting WiFi Association"
|
||||
msgstr "開始WiFi連線"
|
||||
|
||||
msgid "Starting recovery for"
|
||||
msgstr "開始恢復"
|
||||
|
||||
msgid "Starting update for"
|
||||
msgstr "開始更新"
|
||||
|
||||
msgid "State:"
|
||||
msgstr "狀態:"
|
||||
|
||||
msgid "System"
|
||||
msgstr "系統"
|
||||
|
||||
@ -1019,6 +1064,9 @@ msgstr ""
|
||||
msgid "The system will reboot!"
|
||||
msgstr "系統將重新啟動!"
|
||||
|
||||
msgid "This device is not activated and is available for trial use only"
|
||||
msgstr "此設備尚未激活,僅供試用"
|
||||
|
||||
msgid "This operation is about to print the model"
|
||||
msgstr "此操作即將開始打印模型"
|
||||
|
||||
@ -1037,6 +1085,15 @@ msgstr "打印過程中超過倒計時後關閉屏幕"
|
||||
msgid "Total:"
|
||||
msgstr "合計:"
|
||||
|
||||
msgid "Trial"
|
||||
msgstr "試用"
|
||||
|
||||
msgid "Trial Time:"
|
||||
msgstr "試用時間:"
|
||||
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
msgid "Unknown Heater"
|
||||
msgstr "未知加熱器"
|
||||
|
||||
@ -1136,6 +1193,9 @@ msgid "hour"
|
||||
msgid_plural "hours"
|
||||
msgstr[0] "小時"
|
||||
|
||||
msgid "license"
|
||||
msgstr "許可證"
|
||||
|
||||
msgid "macros that use 'rename_existing' are hidden"
|
||||
msgstr "使用\"rename_existing\"的宏將被隱藏"
|
||||
|
||||
@ -1168,6 +1228,15 @@ msgid "second"
|
||||
msgid_plural "seconds"
|
||||
msgstr[0] "秒"
|
||||
|
||||
#~ msgid "Connected"
|
||||
#~ msgstr "已連線"
|
||||
|
||||
#~ msgid "Hostname"
|
||||
#~ msgstr "主機名"
|
||||
|
||||
#~ msgid "Starting WiFi Association"
|
||||
#~ msgstr "開始WiFi連線"
|
||||
|
||||
#~ msgid "Restoring Left extruder temperature, this may take some time"
|
||||
#~ msgstr "恢復左擠出機溫度,這可能需要一些時間"
|
||||
|
||||
@ -1216,9 +1285,6 @@ msgstr[0] "秒"
|
||||
#~ msgid "Hidden"
|
||||
#~ msgstr "隱藏的"
|
||||
|
||||
#~ msgid "Print Time"
|
||||
#~ msgstr "列印時間"
|
||||
|
||||
#~ msgid "dBm"
|
||||
#~ msgstr "dBm"
|
||||
|
||||
@ -1390,6 +1456,3 @@ msgstr[0] "秒"
|
||||
|
||||
#~ msgid "Load Average"
|
||||
#~ msgstr "平均负载"
|
||||
|
||||
#~ msgid "Screen Blanking Time"
|
||||
#~ msgstr "关闭屏幕时间"
|
||||
|
@ -230,6 +230,10 @@ class SdbusNm:
|
||||
else None
|
||||
)
|
||||
|
||||
def get_signal_strength(self):
|
||||
ap = self.get_connected_ap()
|
||||
return ap.strength if ap else None
|
||||
|
||||
def get_security_type(self, ssid):
|
||||
return next(
|
||||
(
|
||||
|
165
ks_includes/sdbus_reg.py
Normal file
165
ks_includes/sdbus_reg.py
Normal file
@ -0,0 +1,165 @@
|
||||
from asyncio import new_event_loop, run_coroutine_threadsafe
|
||||
from threading import Thread
|
||||
import ast
|
||||
import logging
|
||||
from typing import Optional
|
||||
from sdbus import DbusInterfaceCommonAsync, dbus_method_async, dbus_property_async
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RegistrationInterface(DbusInterfaceCommonAsync, interface_name="org.registration.interface"):
|
||||
|
||||
def __init__(self, bus_name: str, object_path: str):
|
||||
super().__init__()
|
||||
self.proxy = self.new_proxy(bus_name, object_path)
|
||||
|
||||
@dbus_property_async(property_signature="s")
|
||||
def get_unique_id(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_property_async(property_signature="a{sv}")
|
||||
def get_time_info(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_property_async(property_signature="b")
|
||||
def is_active(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_property_async(property_signature="b")
|
||||
def is_trial_active(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_method_async(input_signature="s", result_signature="b")
|
||||
async def verify_activation_code(self, val: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_method_async(input_signature="s", result_signature="b")
|
||||
async def reset_registration(self, val: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@dbus_property_async(property_signature="b")
|
||||
def enabled_registration(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class LicenseManager:
|
||||
|
||||
def __init__(self, bus_name: str = "org.registration.link", object_path: str = "/"):
|
||||
self.loop = new_event_loop()
|
||||
self.registration_interface: Optional[RegistrationInterface] = None
|
||||
self.interface_valid = False
|
||||
self._thread: Optional[Thread] = None
|
||||
self.callback = None
|
||||
|
||||
try:
|
||||
self.registration_interface = RegistrationInterface(bus_name, object_path)
|
||||
self.interface_valid = True
|
||||
logger.info("DBus connection established successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize DBus connection: {e}")
|
||||
self._cleanup_resources()
|
||||
return
|
||||
|
||||
self._thread = Thread(target=self._run_loop, daemon=True)
|
||||
self._thread.start()
|
||||
|
||||
def _run_loop(self) -> None:
|
||||
try:
|
||||
self.loop.run_forever()
|
||||
finally:
|
||||
self.loop.close()
|
||||
|
||||
def _cleanup_resources(self) -> None:
|
||||
if self.loop.is_running():
|
||||
self.loop.call_soon_threadsafe(self.loop.stop)
|
||||
if self._thread and self._thread.is_alive():
|
||||
self._thread.join(timeout=1)
|
||||
self.loop.close()
|
||||
|
||||
def is_interface_valid(self) -> bool:
|
||||
return self.interface_valid
|
||||
|
||||
def _async_call(self, coroutine_func, default=None):
|
||||
if not self.is_interface_valid():
|
||||
logger.warning("Attempting to use invalid DBus interface")
|
||||
return default
|
||||
|
||||
try:
|
||||
future = run_coroutine_threadsafe(coroutine_func(), self.loop)
|
||||
return future.result()
|
||||
except Exception as e:
|
||||
logger.error(f"DBus operation failed: {e}")
|
||||
self.interface_valid = False
|
||||
return default
|
||||
|
||||
def get_unique_id(self) -> str:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.get_unique_id
|
||||
|
||||
return self._async_call(_get, default="")
|
||||
|
||||
def get_trial_time(self) -> int:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.get_time_info
|
||||
|
||||
result = self._async_call(_get, default="{}")
|
||||
try:
|
||||
data_dict = ast.literal_eval(result)
|
||||
return data_dict.get("trial_time", 0)
|
||||
except Exception as e:
|
||||
logger.error(f"Parse time info failed: {e}")
|
||||
return 0
|
||||
|
||||
def get_total_printed_time(self) -> int:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.get_time_info
|
||||
|
||||
result = self._async_call(_get, default="{}")
|
||||
try:
|
||||
data_dict = ast.literal_eval(result)
|
||||
return int(data_dict.get("total_printed_time", 0))
|
||||
except Exception as e:
|
||||
logger.error(f"Parse time info failed: {e}")
|
||||
return 0
|
||||
|
||||
def is_active(self) -> bool:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.is_active
|
||||
|
||||
return self._async_call(_get, default=False)
|
||||
|
||||
def is_trial_active(self) -> bool:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.is_trial_active
|
||||
|
||||
return self._async_call(_get, default=False)
|
||||
|
||||
def is_time_sufficient(self, required_seconds: int = 40 * 3600) -> bool:
|
||||
trial_time = self.get_trial_time()
|
||||
printed_time = self.get_total_printed_time()
|
||||
return (trial_time - printed_time) > required_seconds
|
||||
|
||||
def verify_activation_code(self, code: str) -> bool:
|
||||
async def _verify():
|
||||
return await self.registration_interface.proxy.verify_activation_code(code)
|
||||
|
||||
return self._async_call(_verify, default=False)
|
||||
|
||||
def reset_registration(self, code: str) -> bool:
|
||||
async def _reset():
|
||||
return await self.registration_interface.proxy.reset_registration(code)
|
||||
|
||||
return self._async_call(_reset, default=False)
|
||||
|
||||
def enabled_registration(self) -> bool:
|
||||
async def _get():
|
||||
return await self.registration_interface.proxy.enabled_registration
|
||||
|
||||
return self._async_call(_get, default=False)
|
||||
|
||||
def close(self) -> None:
|
||||
if self.is_interface_valid():
|
||||
logger.info("Closing DBus connection...")
|
||||
self._cleanup_resources()
|
||||
self.interface_valid = False
|
@ -8,6 +8,7 @@ from gi.repository import GLib, Gtk, Pango
|
||||
from jinja2 import Environment
|
||||
from datetime import datetime
|
||||
from math import log
|
||||
from ks_includes.sdbus_nm import SdbusNm
|
||||
from ks_includes.screen_panel import ScreenPanel
|
||||
|
||||
|
||||
@ -18,11 +19,37 @@ class BasePanel(ScreenPanel):
|
||||
self.time_min = -1
|
||||
self.time_format = self._config.get_main_config().getboolean("24htime", True)
|
||||
self.time_update = None
|
||||
self.network_update = None
|
||||
self.titlebar_items = []
|
||||
self.titlebar_name_type = None
|
||||
self.current_extruder = None
|
||||
self.last_usage_report = datetime.now()
|
||||
self.usage_report = 0
|
||||
|
||||
icon_size_width = self._gtk.content_width * 0.05
|
||||
icon_size_height = self._gtk.content_height * 0.05
|
||||
|
||||
network_icons_map = {
|
||||
"excellent": "wifi_excellent",
|
||||
"good": "wifi_good",
|
||||
"fair": "wifi_fair",
|
||||
"weak": "wifi_weak",
|
||||
"ethernet": "ethernet",
|
||||
}
|
||||
|
||||
|
||||
self.network_icons = {
|
||||
key: self._gtk.PixbufFromIcon(value, width=icon_size_width, height=icon_size_height)
|
||||
for key, value in network_icons_map.items()
|
||||
}
|
||||
|
||||
try:
|
||||
self.sdbus_nm = SdbusNm(self.network_interface_refresh)
|
||||
except Exception as e:
|
||||
logging.exception("Failed to initialize SdbusNm: %s", e)
|
||||
self.sdbus_nm = None
|
||||
|
||||
|
||||
# Action bar buttons
|
||||
abscale = self.bts * 1.1
|
||||
self.control['back'] = self._gtk.Button('back', scale=abscale)
|
||||
@ -100,6 +127,26 @@ class BasePanel(ScreenPanel):
|
||||
|
||||
self.titlelbl = Gtk.Label(hexpand=True, halign=Gtk.Align.CENTER, ellipsize=Pango.EllipsizeMode.END)
|
||||
|
||||
if self._screen.license.is_interface_valid() and not self._screen.license.is_active():
|
||||
img_size = self._gtk.img_scale * self.bts
|
||||
self.control["license"] = self._gtk.Image("license", img_size, img_size)
|
||||
license_eventbox = Gtk.EventBox()
|
||||
license_eventbox.add(self.control["license"])
|
||||
license_eventbox.connect("button-press-event", self.show_license_key_page)
|
||||
self.control["license_box"] = Gtk.Box(halign=Gtk.Align.END)
|
||||
self.control["license_box"].pack_end(license_eventbox, True, True, 5)
|
||||
|
||||
if self.sdbus_nm:
|
||||
img_size = self._gtk.img_scale * self.bts
|
||||
self.control["network_ico"] = self._gtk.Image("wifi_excellent", img_size, img_size)
|
||||
network_eventbox = Gtk.EventBox()
|
||||
network_eventbox.add(self.control["network_ico"])
|
||||
network_eventbox.connect("button-press-event", self.show_network_page)
|
||||
self.control["network_box"] = Gtk.Box(halign=Gtk.Align.END)
|
||||
self.control["network_box"].pack_end(network_eventbox, True, True, 5)
|
||||
self.control["network_ico"].set_no_show_all(True)
|
||||
self.control["network_ico"].set_visible(False)
|
||||
|
||||
self.control['time'] = Gtk.Label(label="00:00 AM")
|
||||
self.control['time_box'] = Gtk.Box(halign=Gtk.Align.END)
|
||||
self.control['time_box'].pack_end(self.control['time'], True, True, 10)
|
||||
@ -108,6 +155,10 @@ class BasePanel(ScreenPanel):
|
||||
self.titlebar.get_style_context().add_class("title_bar")
|
||||
self.titlebar.add(self.control['temp_box'])
|
||||
self.titlebar.add(self.titlelbl)
|
||||
if self._screen.license.is_interface_valid() and not self._screen.license.is_active():
|
||||
self.titlebar.add(self.control["license_box"])
|
||||
if self.sdbus_nm:
|
||||
self.titlebar.add(self.control["network_box"])
|
||||
self.titlebar.add(self.control['time_box'])
|
||||
self.set_title(title)
|
||||
|
||||
@ -127,6 +178,14 @@ class BasePanel(ScreenPanel):
|
||||
|
||||
self.update_time()
|
||||
|
||||
def show_license_key_page(self, widget, event):
|
||||
if "license" not in self._screen._cur_panels:
|
||||
self._screen.show_panel("license", remove_all=False)
|
||||
|
||||
def show_network_page(self, widget, event):
|
||||
if "network" not in self._screen._cur_panels:
|
||||
self._screen.show_panel("network", remove_all=False)
|
||||
|
||||
def reload_icons(self):
|
||||
button: Gtk.Button
|
||||
for button in self.action_bar.get_children():
|
||||
@ -212,6 +271,8 @@ class BasePanel(ScreenPanel):
|
||||
def activate(self):
|
||||
if self.time_update is None:
|
||||
self.time_update = GLib.timeout_add_seconds(1, self.update_time)
|
||||
if self.sdbus_nm and self.network_update is None:
|
||||
self.network_update = GLib.timeout_add_seconds(5, self.network_interface_refresh)
|
||||
|
||||
def add_content(self, panel):
|
||||
printing = self._printer and self._printer.state in {"printing", "paused"}
|
||||
@ -381,6 +442,35 @@ class BasePanel(ScreenPanel):
|
||||
self.time_format = confopt
|
||||
return True
|
||||
|
||||
def network_interface_refresh(self, msg=None, level=3):
|
||||
if self.sdbus_nm:
|
||||
self.interface = self.sdbus_nm.get_primary_interface()
|
||||
if self.interface:
|
||||
if '?' not in self.sdbus_nm.get_ip_address():
|
||||
if self.interface == "eth0":
|
||||
self.control["network_ico"].set_from_pixbuf(self.network_icons["ethernet"])
|
||||
self.control["network_ico"].set_visible(True)
|
||||
elif self.interface == "wlan0":
|
||||
strength = self.sdbus_nm.get_signal_strength()
|
||||
if strength:
|
||||
self.control["network_ico"].set_from_pixbuf(self.get_signal_strength_icon(strength))
|
||||
self.control["network_ico"].set_visible(True)
|
||||
else:
|
||||
self.control["network_ico"].set_visible(False)
|
||||
else:
|
||||
self.control["network_ico"].set_visible(False)
|
||||
return True
|
||||
|
||||
def get_signal_strength_icon(self, signal_level):
|
||||
if signal_level > 75:
|
||||
return self.network_icons["excellent"]
|
||||
elif signal_level > 60:
|
||||
return self.network_icons["good"]
|
||||
elif signal_level > 30:
|
||||
return self.network_icons["fair"]
|
||||
else:
|
||||
return self.network_icons["weak"]
|
||||
|
||||
def set_ks_printer_cfg(self, printer):
|
||||
ScreenPanel.ks_printer_cfg = self._config.get_printer_config(printer)
|
||||
if self.ks_printer_cfg is not None:
|
||||
|
@ -37,6 +37,14 @@ class Panel(ScreenPanel):
|
||||
"callback": self.reset_factory_settings,
|
||||
}
|
||||
},
|
||||
{
|
||||
"License key": {
|
||||
"section": "main",
|
||||
"name": _("License key"),
|
||||
"type": "button",
|
||||
"callback": self.license_key,
|
||||
}
|
||||
},
|
||||
{
|
||||
"version_info": {
|
||||
"section": "main",
|
||||
@ -132,19 +140,47 @@ class Panel(ScreenPanel):
|
||||
self.content.show_all()
|
||||
self.select_model = False
|
||||
|
||||
def license_key(self, *args):
|
||||
self._screen.show_panel("license", title="license", remove_all=False, full=True)
|
||||
|
||||
def reset_factory_settings(self, *args):
|
||||
text = _("Are you sure?\n") + "\n\n" + _("The system will reboot!")
|
||||
label = Gtk.Label(wrap=True, vexpand=True)
|
||||
label.set_markup(text)
|
||||
buttons = [
|
||||
{"name": _("Accept"), "response": Gtk.ResponseType.OK, "style": "dialog-error"},
|
||||
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": "dialog-info"},
|
||||
]
|
||||
self._gtk.Dialog(_("factory settings"), buttons, label, self.confirm_factory_reset_production)
|
||||
|
||||
text = _("Are you sure?\n") + "\n\n" + _("The system will reboot!")
|
||||
label = Gtk.Label(wrap=True, vexpand=True)
|
||||
label.set_markup(text)
|
||||
label.set_margin_top(100)
|
||||
|
||||
def confirm_factory_reset_production(self, dialog, response_id):
|
||||
checkbox = Gtk.CheckButton(label=" " + _("Enable Registration Code"))
|
||||
checkbox.set_halign(Gtk.Align.CENTER)
|
||||
checkbox.set_valign(Gtk.Align.CENTER)
|
||||
|
||||
|
||||
grid = Gtk.Grid(row_homogeneous=True, column_homogeneous=True)
|
||||
grid.set_row_spacing(20)
|
||||
grid.set_column_spacing(0)
|
||||
grid.attach(label, 0, 0, 1, 1)
|
||||
if self._screen.license.is_interface_valid() and self._screen.license.is_active():
|
||||
grid.attach(checkbox, 0, 1, 1, 1)
|
||||
|
||||
self._gtk.Dialog(
|
||||
_("factory settings"),
|
||||
buttons,
|
||||
grid,
|
||||
self.confirm_factory_reset_production,
|
||||
checkbox,
|
||||
)
|
||||
|
||||
|
||||
def confirm_factory_reset_production(self, dialog, response_id, checkbox):
|
||||
self._gtk.remove_dialog(dialog)
|
||||
if response_id == Gtk.ResponseType.OK:
|
||||
if checkbox.get_active():
|
||||
if self._screen.license.is_interface_valid():
|
||||
self._screen.license.enabled_registration()
|
||||
KlippyFactory.production_factory_reset(self._screen._ws.klippy, self._config)
|
||||
|
||||
def version_selection(self, val):
|
||||
|
@ -341,6 +341,17 @@ class Panel(ScreenPanel):
|
||||
def confirm_print_response(self, dialog, response_id, filename):
|
||||
self._gtk.remove_dialog(dialog)
|
||||
if response_id == Gtk.ResponseType.OK:
|
||||
if self._screen.license.is_interface_valid() and not self._screen.license.is_active():
|
||||
if not self._screen.license.is_time_sufficient():
|
||||
if "license" not in self._screen._cur_panels:
|
||||
self._screen.show_panel(
|
||||
"license",
|
||||
title="license",
|
||||
remove_all=False,
|
||||
func=self._screen._ws.klippy.print_start,
|
||||
file=filename,
|
||||
)
|
||||
return
|
||||
logging.info(f"Starting print: {filename}")
|
||||
self._screen._ws.klippy.print_start(filename)
|
||||
|
||||
|
234
panels/license.py
Normal file
234
panels/license.py
Normal file
@ -0,0 +1,234 @@
|
||||
import logging
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, Pango
|
||||
from ks_includes.screen_panel import ScreenPanel
|
||||
|
||||
|
||||
class Panel(ScreenPanel):
|
||||
|
||||
def __init__(self, screen, title, **kwargs):
|
||||
title = title or _("license")
|
||||
super().__init__(screen, title)
|
||||
self.title_text = (
|
||||
_("This device is not activated and is available for trial use only")
|
||||
+ "\n"
|
||||
+ _("Please enter a key to activate")
|
||||
)
|
||||
self.key_len = 15
|
||||
self.full = False
|
||||
self.interface = screen.license
|
||||
self.serial_num = self.interface.get_unique_id() or _("Unknown")
|
||||
self.is_active = self.interface.is_active()
|
||||
self.args = {}
|
||||
|
||||
def update_time(self):
|
||||
total_printed_time = max(0, self.interface.get_total_printed_time())
|
||||
trial_time = max(0, self.interface.get_trial_time())
|
||||
remaining_time = max(0, trial_time - total_printed_time)
|
||||
|
||||
self.license_box["elapsed_trial_time_value"].set_text(self.seconds_to_hms(total_printed_time))
|
||||
self.license_box["trial_time_value"].set_text(self.seconds_to_hms(trial_time))
|
||||
self.license_box["remain_time_value"].set_text(self.seconds_to_hms(remaining_time))
|
||||
|
||||
def seconds_to_hms(self, seconds):
|
||||
|
||||
if not isinstance(seconds, (int, float)) or seconds < 0:
|
||||
raise ValueError(f"seconds must be a non-negative number, got {seconds}")
|
||||
|
||||
hours, remainder = divmod(int(seconds), 3600)
|
||||
minutes, secs = divmod(remainder, 60)
|
||||
|
||||
return f"{hours:03}:{minutes:02}:{secs:02}"
|
||||
|
||||
def state_update(self, text):
|
||||
self.license_box["state_text_value"].set_text(text)
|
||||
|
||||
def verify_key(self, key):
|
||||
try:
|
||||
res = self.interface.verify_activation_code(key)
|
||||
if res:
|
||||
if self.interface.is_active():
|
||||
self.is_active = True
|
||||
self.state_update(_("Permanent Activation"))
|
||||
else:
|
||||
self.state_update(_("Key is valid"))
|
||||
else:
|
||||
self.state_update(_("Key is invalid"))
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
|
||||
def active_refresh(self, **args):
|
||||
self.args["full"] = args.get("full")
|
||||
self.args["callback"] = args.get("func")
|
||||
self.args["file"] = args.get("file")
|
||||
self.args["onboarding"] = args.get("onboarding")
|
||||
self.display_dialog()
|
||||
|
||||
def deactivate(self):
|
||||
self._screen.remove_keyboard()
|
||||
for child in self.content.get_children():
|
||||
self.content.remove(child)
|
||||
|
||||
def display_dialog(self, full=False, key=""):
|
||||
BUTTON_CONFIGS = {
|
||||
"trial_with_callback": [
|
||||
{"name": _("Activate"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
|
||||
{"name": _("Skip"), "response": Gtk.ResponseType.CANCEL, "style": "dialog-error"},
|
||||
],
|
||||
"full_features": [
|
||||
{"name": _("Reset"), "response": Gtk.ResponseType.APPLY, "style": "dialog-secondary"},
|
||||
{"name": _("Activate"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
|
||||
{"name": _("Close"), "response": Gtk.ResponseType.CLOSE, "style": "dialog-error"},
|
||||
],
|
||||
"Trial": [
|
||||
{"name": _("Activate"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
|
||||
{"name": _("Trial"), "response": Gtk.ResponseType.CLOSE, "style": "dialog-error"},
|
||||
],
|
||||
"default": [
|
||||
{"name": _("Activate"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
|
||||
{"name": _("Close"), "response": Gtk.ResponseType.CLOSE, "style": "dialog-error"},
|
||||
],
|
||||
}
|
||||
|
||||
if self.args.get("callback") and self.interface.is_trial_active():
|
||||
buttons = BUTTON_CONFIGS["trial_with_callback"]
|
||||
elif self.args.get("full"):
|
||||
buttons = BUTTON_CONFIGS["full_features"]
|
||||
elif self.args.get("onboarding"):
|
||||
buttons = BUTTON_CONFIGS["Trial"]
|
||||
else:
|
||||
buttons = BUTTON_CONFIGS["default"]
|
||||
|
||||
self.create_license_key_dialog(buttons=buttons, key=key)
|
||||
|
||||
def create_license_key_dialog(self, buttons=None, key=""):
|
||||
|
||||
if buttons is None:
|
||||
buttons = [
|
||||
{"name": _("Activate"), "response": Gtk.ResponseType.OK, "style": "dialog-info"},
|
||||
{"name": _("Close"), "response": Gtk.ResponseType.CLOSE, "style": "dialog-error"},
|
||||
]
|
||||
|
||||
self.title_label = Gtk.Label(hexpand=True, vexpand=False, wrap=True, wrap_mode=Pango.WrapMode.WORD_CHAR)
|
||||
self.title_label.set_markup(f"<big>{self.title_text}</big>\n")
|
||||
self.title_label.set_margin_top(50)
|
||||
self.title_label.set_margin_start(20)
|
||||
self.title_label.set_halign(Gtk.Align.START)
|
||||
|
||||
self.license_box = {}
|
||||
|
||||
self.grid = Gtk.Grid(column_spacing=20, row_spacing=20, hexpand=True, vexpand=True)
|
||||
|
||||
def add_labeled_value(row, label_text, value_text):
|
||||
label = Gtk.Label(label=label_text, use_markup=True, xalign=0, wrap=True)
|
||||
value = Gtk.Label(label=value_text, use_markup=True, xalign=0, wrap=True)
|
||||
self.grid.attach(label, 0, row, 1, 1)
|
||||
self.grid.attach(value, 1, row, 1, 1)
|
||||
return label, value
|
||||
|
||||
status_text = _("Not activated")
|
||||
if not self.interface.is_interface_valid():
|
||||
status_text = _("Unknown")
|
||||
elif self.is_active:
|
||||
status_text = _("Permanent Activation")
|
||||
self.license_box["state_text"], self.license_box["state_text_value"] = add_labeled_value(
|
||||
0, _("State:"), status_text
|
||||
)
|
||||
self.license_box["serial_num_text"], self.license_box["serial_num_value"] = add_labeled_value(
|
||||
1, _("Serial Number:"), self.serial_num
|
||||
)
|
||||
self.license_box["trial_time_text"], self.license_box["trial_time_value"] = add_labeled_value(
|
||||
2, _("Trial Time:"), "000:00:00"
|
||||
)
|
||||
self.license_box["elapsed_trial_time_text"], self.license_box["elapsed_trial_time_value"] = add_labeled_value(
|
||||
3, _("Elapsed trial time:"), "000:00:00"
|
||||
)
|
||||
self.license_box["remain_time_text"], self.license_box["remain_time_value"] = add_labeled_value(
|
||||
4, _("Remaining Time:"), "000:00:00"
|
||||
)
|
||||
|
||||
self.license_box["key_text"] = Gtk.Label(label=_("Key:"), use_markup=True, xalign=0, wrap=True)
|
||||
self.grid.attach(self.license_box["key_text"], 0, 5, 1, 1)
|
||||
|
||||
self.license_box["key_input"] = Gtk.Entry(hexpand=False, vexpand=False)
|
||||
self.license_box["key_input"].set_max_length(self.key_len)
|
||||
self.license_box["key_input"].set_text(key)
|
||||
self.license_box["key_input"].connect("button-press-event", self.on_show_keyboard)
|
||||
self.grid.attach(self.license_box["key_input"], 1, 5, 1, 1)
|
||||
|
||||
image = self._gtk.Image("license", self._gtk.content_width * 0.4, self._gtk.content_height * 0.4)
|
||||
image.set_margin_start(60)
|
||||
image.set_margin_end(20)
|
||||
|
||||
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, vexpand=True)
|
||||
horizontal_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, vexpand=True)
|
||||
horizontal_box.set_margin_top(20)
|
||||
|
||||
self.grid.set_margin_start(60)
|
||||
horizontal_box.pack_start(image, False, True, 0)
|
||||
horizontal_box.pack_start(self.grid, True, True, 0)
|
||||
|
||||
main_box.pack_start(self.title_label, False, True, 0)
|
||||
main_box.pack_start(horizontal_box, True, True, 0)
|
||||
|
||||
self.dialog = self._gtk.Dialog("License", buttons, main_box, self.confirm_license_response)
|
||||
self.update_time()
|
||||
|
||||
def confirm_license_response(self, dialog, response_id):
|
||||
if response_id == Gtk.ResponseType.YES:
|
||||
if self.interface.enabled_registration():
|
||||
self.state_update(_("Enabled successfully"))
|
||||
elif response_id == Gtk.ResponseType.APPLY:
|
||||
if len(self.license_box["key_input"].get_text()) == 0:
|
||||
self.state_update(_("Key is empty"))
|
||||
return
|
||||
else:
|
||||
if self.interface.reset_registration(self.license_box["key_input"].get_text()):
|
||||
self.update_time()
|
||||
self.state_update(_("Reset successfully"))
|
||||
else:
|
||||
self.state_update(_("Key is invalid"))
|
||||
elif response_id == Gtk.ResponseType.CLOSE:
|
||||
self._gtk.remove_dialog(dialog)
|
||||
self._screen._menu_go_back()
|
||||
elif response_id == Gtk.ResponseType.CANCEL:
|
||||
self._gtk.remove_dialog(dialog)
|
||||
self._screen._menu_go_back()
|
||||
if not self.interface.is_active() and self.interface.is_trial_active():
|
||||
if self.args.get("callback"):
|
||||
self.args["callback"](self.args["file"])
|
||||
elif response_id == Gtk.ResponseType.OK:
|
||||
if len(self.license_box["key_input"].get_text()) == 0:
|
||||
self.state_update(_("Key is empty"))
|
||||
return
|
||||
self.verify_key(self.license_box["key_input"].get_text())
|
||||
self.update_time()
|
||||
|
||||
def on_show_keyboard(self, entry=None, event=None):
|
||||
self._gtk.remove_dialog(self.dialog)
|
||||
lbl = Gtk.Label(_("Please enter a key to activate"), halign=Gtk.Align.START, hexpand=False)
|
||||
self.labels["entry"] = Gtk.Entry(hexpand=True)
|
||||
self.labels["entry"].set_max_length(self.key_len)
|
||||
self.labels["entry"].connect("focus-in-event", self._screen.show_keyboard)
|
||||
save = self._gtk.Button("complete", _("Save"), "color3")
|
||||
save.set_hexpand(False)
|
||||
save.connect("clicked", self.on_save_key)
|
||||
input_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
input_box.pack_start(self.labels["entry"], True, True, 5)
|
||||
input_box.pack_start(save, False, False, 5)
|
||||
self.labels["input_box"] = Gtk.Box(
|
||||
orientation=Gtk.Orientation.VERTICAL, spacing=5, hexpand=True, vexpand=True, valign=Gtk.Align.CENTER
|
||||
)
|
||||
self.labels["input_box"].pack_start(lbl, True, True, 5)
|
||||
self.labels["input_box"].pack_start(input_box, True, True, 5)
|
||||
self.content.add(self.labels["input_box"])
|
||||
self.labels["entry"].grab_focus_without_selecting()
|
||||
|
||||
def on_save_key(self, dialog):
|
||||
key_text = self.labels["entry"].get_text()
|
||||
self._screen.remove_keyboard()
|
||||
for child in self.content.get_children():
|
||||
self.content.remove(child)
|
||||
self.display_dialog(self.full, key=key_text)
|
@ -77,9 +77,7 @@ class Panel(ScreenPanel):
|
||||
|
||||
self.labels['interface'] = Gtk.Label(hexpand=True)
|
||||
self.labels['ip'] = Gtk.Label(hexpand=True)
|
||||
if self.interface is not None:
|
||||
self.labels['interface'].set_text(_("Interface") + f': {self.interface}')
|
||||
self.labels['ip'].set_text(f"IP: {self.sdbus_nm.get_ip_address()}")
|
||||
self.network_interface_refresh()
|
||||
|
||||
self.reload_button = self._gtk.Button("refresh", None, "custom-icon-button", self.bts)
|
||||
self.reload_button.set_no_show_all(True)
|
||||
@ -120,6 +118,7 @@ class Panel(ScreenPanel):
|
||||
self.network_list.connect("row-activated", self.handle_wifi_selection)
|
||||
|
||||
def popup_callback(self, msg, level=3):
|
||||
self.network_interface_refresh()
|
||||
if not self.refresh_status(msg):
|
||||
for item in self.network_rows:
|
||||
if self.network_rows[item]["label_state"] is not None:
|
||||
@ -127,6 +126,12 @@ class Panel(ScreenPanel):
|
||||
self.network_rows[item]["label_state"].hide()
|
||||
self._screen.show_popup_message(msg, level)
|
||||
|
||||
def network_interface_refresh(self):
|
||||
if self.interface is not None:
|
||||
self.interface = self.sdbus_nm.get_primary_interface()
|
||||
self.labels['interface'].set_text(_("Interface") + f': {self.interface}')
|
||||
self.labels['ip'].set_text(f"IP: {self.sdbus_nm.get_ip_address()}")
|
||||
|
||||
def handle_wifi_selection(self, list_box, row):
|
||||
index = row.get_index()
|
||||
logging.info(f"clicked SSID is {self.networks[index]['SSID']}")
|
||||
@ -407,6 +412,7 @@ class Panel(ScreenPanel):
|
||||
if self.delay_reload_timer_id:
|
||||
GLib.source_remove(self.delay_reload_timer_id)
|
||||
self.delay_reload_timer_id = None
|
||||
self.network_interface_refresh()
|
||||
|
||||
return self.sdbus_nm.nm.wireless_enabled
|
||||
|
||||
@ -438,7 +444,7 @@ class Panel(ScreenPanel):
|
||||
return
|
||||
if self.sdbus_nm.wifi:
|
||||
if self.sdbus_nm.is_wifi_enabled():
|
||||
self.delay_reload_networks(1000)
|
||||
self.delay_reload_networks(2000)
|
||||
self.start_refresh_timer()
|
||||
|
||||
def deactivate(self):
|
||||
@ -464,3 +470,4 @@ class Panel(ScreenPanel):
|
||||
self.reload_button.hide()
|
||||
self.network_list.set_no_show_all(True)
|
||||
self.network_list.hide()
|
||||
self.network_interface_refresh()
|
@ -67,3 +67,6 @@ class Panel(ScreenPanel):
|
||||
self._screen.show_panel("main_menu", remove_all=True, items=self._config.get_menu_items("__main"))
|
||||
self._config.set("main", "onboarding", "False")
|
||||
self._config.save_user_config_options()
|
||||
if self._screen.license.is_interface_valid() and not self._screen.license.is_active():
|
||||
self._gtk.remove_dialog(dialog)
|
||||
self._screen.show_panel("license", remove_all=False, onboarding=True)
|
||||
|
@ -28,6 +28,7 @@ from ks_includes.printer import Printer
|
||||
from ks_includes.widgets.keyboard import Keyboard
|
||||
from ks_includes.widgets.prompts import Prompt
|
||||
from ks_includes.config import KlipperScreenConfig
|
||||
from ks_includes.sdbus_reg import LicenseManager
|
||||
from panels.base_panel import BasePanel
|
||||
|
||||
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||
@ -89,6 +90,8 @@ class KlipperScreen(Gtk.Window):
|
||||
self.confirm = None
|
||||
self.panels_reinit = []
|
||||
self.last_popup_time = datetime.now()
|
||||
self.license = LicenseManager()
|
||||
logging.info(f"license interface:{self.license}")
|
||||
|
||||
configfile = os.path.normpath(os.path.expanduser(args.configfile))
|
||||
|
||||
|
3
styles/dark/images/license.svg
Normal file
3
styles/dark/images/license.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M37.4 25.6C37 21.2 33.6 18.2 29 18H26C25.8 15.4 26.8 11.8 31 11.4C32.8 11.2 34.4 11.6 35.8 13C37 14.4 37.4 16 37.4 17.8C37.4 18 37.4 18.4 37.6 18.6L39.4 20.4C40 18.6 40 16.8 39.6 15C38.6 11.4 35.6 9.2 31.8 9C29.6 9 27.6 9.4 26 11C23.2 13.6 22.2 19.4 26.2 23.2C26.8 23.8 27.6 24.2 28.4 24.6C29.4 25 30.2 24.2 30.2 23.4C30.2 22.6 29.8 22 28.8 21.8C28.6 21.8 28.4 21.6 28.2 21.4V21.2C29 21 30 21.2 30.6 21.8C31.2 22.4 31.4 23.2 31.2 24.2C30.8 25.6 29.6 26.2 28 25.8C25.8 25.2 24.4 23.8 23.4 21.8C23.2 21.2 22.8 20.6 22.6 20C21.2 21.4 20 22.8 19.6 24.8C18.8 28.4 19.8 31.4 22.4 33.8C22.6 34 22.8 34.2 22.6 34.6C22.4 36.2 22.2 37.6 21.8 39.2C21.2 42.6 20.8 45.8 20.2 49.2C20 50.2 20 51.4 20.2 52.4C20.4 53.2 20.8 53.8 21.8 54.2C22 53.4 22 52.6 22.2 51.8C22.8 48.4 23.2 45.2 23.8 41.8C24.2 39.8 24.4 37.6 24.8 35.6C24.8 35.2 25 35 25.4 35C25.8 35 25.8 35.4 25.8 35.8C25.6 37.8 25.2 39.8 24.8 41.6C24.2 45.8 23.4 50 22.8 54.2C22.8 54.4 23 54.6 23 54.6C24 54.6 24.8 54.8 25.8 54.8C26.8 54.8 27.6 54 28.2 53.2C28.8 52.6 28.8 51.8 28.4 51C28.2 50.8 28 50.6 28 50.4C27.4 49.6 27.6 49 28.4 48.6C28.6 48.4 29 48.4 29.2 48.2C29.6 48 29.8 47.4 29.4 47C28.8 46.4 28.4 45.8 27.8 45.4C27.4 45 27.6 44.2 28 44C28.6 43.6 29.4 43.4 30 43C30.4 42.8 30.6 42.4 30.2 42C30 41.8 29.8 41.6 29.6 41.2C29 40.6 29.2 39.6 29.8 39.2C30 39 30.2 38.8 30.6 38.8C31 38.6 31.2 38.4 31.2 38C31.2 37.4 31.4 36.8 31.4 36.2C31.4 36 31.6 35.6 31.8 35.4C32.4 34.8 33.2 34.4 34 34C36.8 32 37.8 29 37.4 25.6ZM44.6 48.4C44.6 48 44.4 47.6 43.8 47.6C43.4 47.6 42.8 47.6 42.6 47C42.4 46.4 42.4 45.8 42.6 45.2C43 44.4 42.8 43.8 41.8 43.6C41.4 43.6 41 43.4 40.4 43.4C40 43.4 39.8 43.2 39.8 42.8C39.8 42.4 39.6 42 39.6 41.6C39.6 41 40 40.6 40.4 40.2C40.8 39.6 40.8 39.4 40.2 39C40 38.8 39.6 38.6 39.4 38.4C38.6 38 38.6 37.6 39 36.8C39.2 36.6 39.4 36.4 39.4 36.2C39.8 35.8 39.8 35.4 39.6 35C39.4 34.6 39.2 34 38.8 33.6C38.6 33.2 38.6 32.8 38.8 32.4C40.4 29.2 40.8 26 39.4 22.6C38.4 20.4 36.6 19 34.2 18.4L34 18.6C34.2 18.8 34.4 19.2 34.6 19.4C37 21.4 38.4 24 38.4 27.2C38.4 30.6 37 33.4 34 35.6C33.6 35.8 33.4 36.2 33.6 36.6C34.4 38.6 35 40.4 35.8 42.4C37.4 46.2 39 49.8 40.6 53.6C40.8 53.8 40.8 54 41.2 54C43.6 52.8 45 50.6 44.6 48.4ZM32.6 36.6C32.4 37.4 32.4 38 32.2 38.6C32.2 39 32 39.4 32.2 39.8C33.6 43.2 35 46.8 36.4 50.2C36.8 51.2 37.4 52.2 38.4 53C38.8 53.2 39.2 53.4 39.4 53.6L39.6 53.4C37.4 47.8 35 42.2 32.6 36.6Z" fill="#DBCD68"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
3
styles/light/images/license.svg
Normal file
3
styles/light/images/license.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M37.4 25.6C37 21.2 33.6 18.2 29 18H26C25.8 15.4 26.8 11.8 31 11.4C32.8 11.2 34.4 11.6 35.8 13C37 14.4 37.4 16 37.4 17.8C37.4 18 37.4 18.4 37.6 18.6L39.4 20.4C40 18.6 40 16.8 39.6 15C38.6 11.4 35.6 9.2 31.8 9C29.6 9 27.6 9.4 26 11C23.2 13.6 22.2 19.4 26.2 23.2C26.8 23.8 27.6 24.2 28.4 24.6C29.4 25 30.2 24.2 30.2 23.4C30.2 22.6 29.8 22 28.8 21.8C28.6 21.8 28.4 21.6 28.2 21.4V21.2C29 21 30 21.2 30.6 21.8C31.2 22.4 31.4 23.2 31.2 24.2C30.8 25.6 29.6 26.2 28 25.8C25.8 25.2 24.4 23.8 23.4 21.8C23.2 21.2 22.8 20.6 22.6 20C21.2 21.4 20 22.8 19.6 24.8C18.8 28.4 19.8 31.4 22.4 33.8C22.6 34 22.8 34.2 22.6 34.6C22.4 36.2 22.2 37.6 21.8 39.2C21.2 42.6 20.8 45.8 20.2 49.2C20 50.2 20 51.4 20.2 52.4C20.4 53.2 20.8 53.8 21.8 54.2C22 53.4 22 52.6 22.2 51.8C22.8 48.4 23.2 45.2 23.8 41.8C24.2 39.8 24.4 37.6 24.8 35.6C24.8 35.2 25 35 25.4 35C25.8 35 25.8 35.4 25.8 35.8C25.6 37.8 25.2 39.8 24.8 41.6C24.2 45.8 23.4 50 22.8 54.2C22.8 54.4 23 54.6 23 54.6C24 54.6 24.8 54.8 25.8 54.8C26.8 54.8 27.6 54 28.2 53.2C28.8 52.6 28.8 51.8 28.4 51C28.2 50.8 28 50.6 28 50.4C27.4 49.6 27.6 49 28.4 48.6C28.6 48.4 29 48.4 29.2 48.2C29.6 48 29.8 47.4 29.4 47C28.8 46.4 28.4 45.8 27.8 45.4C27.4 45 27.6 44.2 28 44C28.6 43.6 29.4 43.4 30 43C30.4 42.8 30.6 42.4 30.2 42C30 41.8 29.8 41.6 29.6 41.2C29 40.6 29.2 39.6 29.8 39.2C30 39 30.2 38.8 30.6 38.8C31 38.6 31.2 38.4 31.2 38C31.2 37.4 31.4 36.8 31.4 36.2C31.4 36 31.6 35.6 31.8 35.4C32.4 34.8 33.2 34.4 34 34C36.8 32 37.8 29 37.4 25.6ZM44.6 48.4C44.6 48 44.4 47.6 43.8 47.6C43.4 47.6 42.8 47.6 42.6 47C42.4 46.4 42.4 45.8 42.6 45.2C43 44.4 42.8 43.8 41.8 43.6C41.4 43.6 41 43.4 40.4 43.4C40 43.4 39.8 43.2 39.8 42.8C39.8 42.4 39.6 42 39.6 41.6C39.6 41 40 40.6 40.4 40.2C40.8 39.6 40.8 39.4 40.2 39C40 38.8 39.6 38.6 39.4 38.4C38.6 38 38.6 37.6 39 36.8C39.2 36.6 39.4 36.4 39.4 36.2C39.8 35.8 39.8 35.4 39.6 35C39.4 34.6 39.2 34 38.8 33.6C38.6 33.2 38.6 32.8 38.8 32.4C40.4 29.2 40.8 26 39.4 22.6C38.4 20.4 36.6 19 34.2 18.4L34 18.6C34.2 18.8 34.4 19.2 34.6 19.4C37 21.4 38.4 24 38.4 27.2C38.4 30.6 37 33.4 34 35.6C33.6 35.8 33.4 36.2 33.6 36.6C34.4 38.6 35 40.4 35.8 42.4C37.4 46.2 39 49.8 40.6 53.6C40.8 53.8 40.8 54 41.2 54C43.6 52.8 45 50.6 44.6 48.4ZM32.6 36.6C32.4 37.4 32.4 38 32.2 38.6C32.2 39 32 39.4 32.2 39.8C33.6 43.2 35 46.8 36.4 50.2C36.8 51.2 37.4 52.2 38.4 53C38.8 53.2 39.2 53.4 39.4 53.6L39.6 53.4C37.4 47.8 35 42.2 32.6 36.6Z" fill="#DBCD68"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
Loading…
x
Reference in New Issue
Block a user