updata.py 后台下载服务器文件
updata 文件列表添加右键菜单,查看文件详情
This commit is contained in:
@@ -92,4 +92,7 @@
|
|||||||
每次赋码结果采用不同的颜色
|
每次赋码结果采用不同的颜色
|
||||||
2023.10.27
|
2023.10.27
|
||||||
修改updata工具按钮描述
|
修改updata工具按钮描述
|
||||||
|
2023.10.28
|
||||||
|
updata.py 后台下载服务器文件
|
||||||
|
2023.10.29
|
||||||
|
updata 文件列表添加右键菜单,查看文件详情
|
||||||
|
144
updata/file_detail.py
Normal file
144
updata/file_detail.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# a=os.path.split("E:\\abc/def.bin")
|
||||||
|
# a=os.path.splitext("E:\\abc/def/")
|
||||||
|
# print(a)
|
||||||
|
|
||||||
|
|
||||||
|
def _bytes_to_int(d:bytearray):
|
||||||
|
ret=0
|
||||||
|
num=len(d)
|
||||||
|
for i in d:
|
||||||
|
ret>>=8
|
||||||
|
ret|=i<<(8*(num-1))
|
||||||
|
return ret
|
||||||
|
def _bytes_to_str(d:bytearray):
|
||||||
|
d=d.replace(b'\0xff',b'\0x00')
|
||||||
|
text=d.strip(b'\0x00').decode("utf-8")
|
||||||
|
return text
|
||||||
|
def _detail_elf(name:str):
|
||||||
|
with open(name,"rb") as f:
|
||||||
|
data=f.read()
|
||||||
|
off_index=16+4*4
|
||||||
|
sh_off=_bytes_to_int(data[off_index:off_index+4])
|
||||||
|
off_index+=2*4+3*2
|
||||||
|
sh_size=_bytes_to_int(data[off_index:off_index+2])
|
||||||
|
off_index+=2
|
||||||
|
sh_num=_bytes_to_int(data[off_index:off_index+2])
|
||||||
|
addr=sh_off+sh_num*sh_size
|
||||||
|
size=len(data)
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪主板程序"]
|
||||||
|
ret+=[" |打包文件为:"]
|
||||||
|
while (addr<size):
|
||||||
|
d_size=_bytes_to_int(data[addr:addr+4])
|
||||||
|
item_name=data[addr+4:addr+256].decode("utf-8")
|
||||||
|
ret.append(" |-"+item_name)
|
||||||
|
addr+=d_size
|
||||||
|
def get_file(name:str):
|
||||||
|
addr=sh_off+sh_num*sh_size
|
||||||
|
while (addr<size):
|
||||||
|
d_size=_bytes_to_int(data[addr:addr+4])
|
||||||
|
item_name=_bytes_to_str(data[addr+4:addr+256])
|
||||||
|
if(item_name==name):
|
||||||
|
return data[addr+256:addr+d_size]
|
||||||
|
addr+=d_size
|
||||||
|
return bytearray()
|
||||||
|
def decode_info():
|
||||||
|
d=get_file("info.json")
|
||||||
|
j=json.loads(d)
|
||||||
|
ret.append(" |编译时间:{d}".format(d=j["build_date"]))
|
||||||
|
ret.append(" |软件版本:{d}".format(d=j["soft_version"]))
|
||||||
|
decode_info()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def _detail_axf(name:str):
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪主板协处理器程序"]
|
||||||
|
return ret
|
||||||
|
def _detail_bin(name:str):
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪小板程序"]
|
||||||
|
return ret
|
||||||
|
def _detail_json(name:str):
|
||||||
|
file_name=os.path.split(name)
|
||||||
|
def scheme_decode_id(id:int):
|
||||||
|
# 日bit0~bit4 月bit5~bit8 年bit9~bit15
|
||||||
|
sid=(id>>0)&0x7f
|
||||||
|
model=(id>>7)&0x1f
|
||||||
|
chip=(id>>12)&0xf
|
||||||
|
day=(id>>16)&0x1f
|
||||||
|
month=(id>>21)&0xf
|
||||||
|
year=((id>>25)&0x7f)+2022
|
||||||
|
s="{y}-{m}-{d},chip:{chip},model:{model},id:{id}".format(y=year,m=month,d=day,chip=chip,model=model,id=sid)
|
||||||
|
return s
|
||||||
|
with open(name,"rb") as f:
|
||||||
|
j=json.loads(f.read())
|
||||||
|
if(file_name!="cfg.json"):
|
||||||
|
ret=[" |文件类型:检测方案文件"]
|
||||||
|
ret.append(" |方案描述:{d}".format(d=j["PlanBrief"]))
|
||||||
|
ret.append(" |方案ID:{id}".format(id=j["PlanID"]))
|
||||||
|
ret.append(" |方案ID解析:{s}".format(s=scheme_decode_id(j["PlanID"])))
|
||||||
|
else:
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪主板配置文件"]
|
||||||
|
return ret
|
||||||
|
def _detail_sh(name:str):
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪主板程序启动脚本"]
|
||||||
|
return ret
|
||||||
|
def _detail_lua(name:str):
|
||||||
|
ret=[" |文件类型:批检仪/赋码仪检测结果判定脚本"]
|
||||||
|
return ret
|
||||||
|
def _detail_pkt(name:str):
|
||||||
|
ret=[" |文件类型:MCU程序在线升级文件"]
|
||||||
|
with open(name,"rb") as f:
|
||||||
|
data=f.read()
|
||||||
|
ret.append(" |打包时间:{d}".format(d=_bytes_to_str(data[4:4+20])))
|
||||||
|
ret.append(" |主机接口:{d}".format(d=_bytes_to_str(data[40:40+8])))
|
||||||
|
ret.append(" |设备类型:{d}".format(d=_bytes_to_str(data[48:48+12])))
|
||||||
|
return ret
|
||||||
|
def _detail_py(name:str):
|
||||||
|
return ["暂未实现"]
|
||||||
|
def _detail_service(name:str):
|
||||||
|
return ["暂未实现"]
|
||||||
|
def _detail_dtb(name:str):
|
||||||
|
return ["暂未实现"]
|
||||||
|
|
||||||
|
|
||||||
|
_fun_table={
|
||||||
|
".elf":_detail_elf,
|
||||||
|
".axf":_detail_axf,
|
||||||
|
".bin":_detail_bin,
|
||||||
|
".json":_detail_json,
|
||||||
|
".sh":_detail_sh,
|
||||||
|
".lua":_detail_lua,
|
||||||
|
".pkt":_detail_pkt,
|
||||||
|
".py":_detail_py,
|
||||||
|
".service":_detail_service,
|
||||||
|
".dtb":_detail_dtb
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def detail(path:str):
|
||||||
|
tailfix=os.path.splitext(path)
|
||||||
|
file_name=os.path.split(path)
|
||||||
|
fun=_fun_table[tailfix[-1]]
|
||||||
|
ret=[file_name[-1]]
|
||||||
|
if(fun is not None):
|
||||||
|
ret+=fun(path)
|
||||||
|
return ret
|
||||||
|
ret+=["未找到此类文件的解析器"]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(_bytes_to_int([0x12,0x34,0x56,0x78]))
|
||||||
|
print(_bytes_to_int([0x12,0x34]))
|
||||||
|
|
48
updata/float_lable.py
Normal file
48
updata/float_lable.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from PyQt5.QtCore import *
|
||||||
|
from PyQt5.QtGui import *
|
||||||
|
from PyQt5.QtWidgets import *
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class floatBox(QWidget):
|
||||||
|
def __init__(self,parent:QWidget=None,items:list=None,x:int=0,y:int=0):
|
||||||
|
QWidget.__init__(self,parent)
|
||||||
|
self.setMouseTracking(True)
|
||||||
|
self.setWindowFlags(Qt.WindowType.FramelessWindowHint|Qt.WindowType.NoDropShadowWindowHint|Qt.WindowType.Popup)
|
||||||
|
# self.setWindowFlags(Qt.WindowType.FramelessWindowHint|Qt.WindowType.Popup)
|
||||||
|
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
||||||
|
# self.setBackgroundRole()
|
||||||
|
self.setStyleSheet ("background-color: rgb(245, 180, 245);")
|
||||||
|
y_size=self._add_items(items)
|
||||||
|
x_size=500
|
||||||
|
desk=QApplication.desktop()
|
||||||
|
if(y+y_size>desk.height()):
|
||||||
|
y=desk.height()-y_size
|
||||||
|
if(x+x_size>desk.width()):
|
||||||
|
x=desk.width()-x_size
|
||||||
|
self.setGeometry(QRect(x, y, x_size, y_size))
|
||||||
|
def _add_items(self,items:list):
|
||||||
|
if(items is None):
|
||||||
|
items=["没有要显示的提示"]
|
||||||
|
for i in range(len(items)):
|
||||||
|
lable=QLabel(self)
|
||||||
|
lable.setText(items[i])
|
||||||
|
lable.setGeometry(QRect(5,5+i*25,500,25))
|
||||||
|
return len(items)*25+10
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
widget = QWidget()
|
||||||
|
widget.resize(870, 410)
|
||||||
|
widget.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
||||||
|
label=floatBox(items=["aaaaaa","bbbbbbbb","cccccccc"])
|
||||||
|
widget.destroyed.connect(label.close)
|
||||||
|
widget.show()
|
||||||
|
label.show()
|
||||||
|
a=app.exec()
|
||||||
|
|
||||||
|
|
101
updata/updata.py
101
updata/updata.py
@@ -18,6 +18,8 @@ import select_list
|
|||||||
import dhcp.dhcp as dhcp
|
import dhcp.dhcp as dhcp
|
||||||
import mysql
|
import mysql
|
||||||
import console_uart
|
import console_uart
|
||||||
|
import file_detail
|
||||||
|
import float_lable
|
||||||
|
|
||||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
|
||||||
|
|
||||||
@@ -104,7 +106,8 @@ class updata_dlg(QObject):
|
|||||||
rate_signal =pyqtSignal([str,int])
|
rate_signal =pyqtSignal([str,int])
|
||||||
# 结束信号,ip,成败,描述
|
# 结束信号,ip,成败,描述
|
||||||
end_signal = pyqtSignal([str,bool,str])
|
end_signal = pyqtSignal([str,bool,str])
|
||||||
|
# 数据库下载文件结束信号
|
||||||
|
sql_download_end_signal =pyqtSignal([])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QObject.__init__(self)
|
QObject.__init__(self)
|
||||||
@@ -136,9 +139,11 @@ class updata_dlg(QObject):
|
|||||||
self.ip_prefix_init()
|
self.ip_prefix_init()
|
||||||
self.ip_hand_init()
|
self.ip_hand_init()
|
||||||
self.channel_init()
|
self.channel_init()
|
||||||
self.widget.destroyed.connect(self.quit)
|
|
||||||
self.scan_file()
|
self.scan_file()
|
||||||
self.scan_slave()
|
self.scan_slave()
|
||||||
|
self.infotext_init()
|
||||||
|
self.widget.destroyed.connect(self.quit)
|
||||||
|
self.sql_download_end_signal.connect(self.scan_file)
|
||||||
|
|
||||||
Logo = QPixmap()
|
Logo = QPixmap()
|
||||||
Logo.loadFromData(base64.b64decode(memory_pic.icon_ico))
|
Logo.loadFromData(base64.b64decode(memory_pic.icon_ico))
|
||||||
@@ -165,7 +170,42 @@ class updata_dlg(QObject):
|
|||||||
self.file_list_label.setText("默认文件列表(选中以发送):")
|
self.file_list_label.setText("默认文件列表(选中以发送):")
|
||||||
self.file_list_label.setToolTip("请选择要升级的文件,只有选中的文件会发送到主板中。")
|
self.file_list_label.setToolTip("请选择要升级的文件,只有选中的文件会发送到主板中。")
|
||||||
self.file_list.setToolTip("请选择要升级的文件,只有选中的文件会发送到主板中。")
|
self.file_list.setToolTip("请选择要升级的文件,只有选中的文件会发送到主板中。")
|
||||||
# self.file_list_label.setToolTipDuration(1)
|
ack=self.file_list.setProperty("contextMenuPolicy",Qt.ContextMenuPolicy.CustomContextMenu)
|
||||||
|
self.file_list.customContextMenuRequested.connect(self.file_item_menu_slot)
|
||||||
|
def file_item_menu_slot(self,pos:QPoint):
|
||||||
|
print("show pop menu")
|
||||||
|
menu=QMenu(self.file_list)
|
||||||
|
action=QAction("删除选中条目",self.file_list)
|
||||||
|
action.triggered.connect(self.file_item_delete)
|
||||||
|
menu.addAction(action)
|
||||||
|
action=QAction("查看选中条目详情",self.file_list)
|
||||||
|
action.triggered.connect(self.file_item_detail)
|
||||||
|
menu.addAction(action)
|
||||||
|
action=QAction("发送选中条目到主板",self.file_list)
|
||||||
|
action.triggered.connect(self.save_but_clicked)
|
||||||
|
menu.addAction(action)
|
||||||
|
menu.addAction(action)
|
||||||
|
action=QAction("刷新文件列表",self.file_list)
|
||||||
|
action.triggered.connect(self.scan_file)
|
||||||
|
menu.addAction(action)
|
||||||
|
menu.exec(self.file_list.mapToGlobal(pos))
|
||||||
|
def file_item_delete(self):
|
||||||
|
print("delete slected items.")
|
||||||
|
items=self.file_list.selectedItems()
|
||||||
|
for i in items:
|
||||||
|
os.remove("file\\"+i.text())
|
||||||
|
self.scan_file()
|
||||||
|
def file_item_detail(self):
|
||||||
|
print("show detail with slected items.")
|
||||||
|
items=self.file_list.selectedItems()
|
||||||
|
details=[]
|
||||||
|
for i in items:
|
||||||
|
details+=file_detail.detail("file/"+i.text())
|
||||||
|
if(len(details)==0):
|
||||||
|
return
|
||||||
|
pos=QCursor.pos()
|
||||||
|
lab=float_lable.floatBox(self.widget,details,x=pos.x(),y=pos.y())
|
||||||
|
lab.show()
|
||||||
|
|
||||||
# 初始化从机列表
|
# 初始化从机列表
|
||||||
def slave_list_init(self):
|
def slave_list_init(self):
|
||||||
@@ -260,7 +300,7 @@ class updata_dlg(QObject):
|
|||||||
self.refresh_but.setObjectName(u"save_but")
|
self.refresh_but.setObjectName(u"save_but")
|
||||||
self.refresh_but.setGeometry(QRect(590, self.but_y, 150, 28))
|
self.refresh_but.setGeometry(QRect(590, self.but_y, 150, 28))
|
||||||
self.but_y+=self.but_y_step
|
self.but_y+=self.but_y_step
|
||||||
self.refresh_but.setText("刷新IP地址")
|
self.refresh_but.setText("刷新主板IP地址")
|
||||||
self.refresh_but.clicked.connect(self.refresh_but_clicked)
|
self.refresh_but.clicked.connect(self.refresh_but_clicked)
|
||||||
self.refresh_but.setToolTip("点击此按钮刷新主板列表。")
|
self.refresh_but.setToolTip("点击此按钮刷新主板列表。")
|
||||||
# self.refresh_but.setToolTipDuration(1)
|
# self.refresh_but.setToolTipDuration(1)
|
||||||
@@ -308,6 +348,17 @@ class updata_dlg(QObject):
|
|||||||
self.download_but.setText("从服务器下载文件")
|
self.download_but.setText("从服务器下载文件")
|
||||||
self.download_but.clicked.connect(self.download_but_clicked)
|
self.download_but.clicked.connect(self.download_but_clicked)
|
||||||
|
|
||||||
|
# 提示信息
|
||||||
|
def infotext_init(self):
|
||||||
|
self.infotext=QLabel(self.widget)
|
||||||
|
self.infotext.setGeometry(QRect(25, 380, 800, 15))
|
||||||
|
self.infotext.setText("欢迎使用云铭公司产线设备维护工具.")
|
||||||
|
def set_infotext(self,text:str):
|
||||||
|
try:
|
||||||
|
self.infotext.setText(text)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
# ip前缀
|
# ip前缀
|
||||||
def ip_prefix_init(self):
|
def ip_prefix_init(self):
|
||||||
self.ip_prefix = QLineEdit(self.widget)
|
self.ip_prefix = QLineEdit(self.widget)
|
||||||
@@ -378,7 +429,7 @@ class updata_dlg(QObject):
|
|||||||
if(sp in types):
|
if(sp in types):
|
||||||
file_list.append(i.text())
|
file_list.append(i.text())
|
||||||
if(len(file_list)!=1):
|
if(len(file_list)!=1):
|
||||||
self.show_msg("请选择一个并且只选择一个 "+str(types)+" 文件")
|
self.set_infotext("请选择一个并且只选择一个 "+str(types)+" 文件")
|
||||||
return ""
|
return ""
|
||||||
return file_list[0]
|
return file_list[0]
|
||||||
|
|
||||||
@@ -387,7 +438,7 @@ class updata_dlg(QObject):
|
|||||||
file_list=[]
|
file_list=[]
|
||||||
items=self.file_list.selectedItems()
|
items=self.file_list.selectedItems()
|
||||||
if(len(items)==0):
|
if(len(items)==0):
|
||||||
self.show_msg("请选择至少一个文件")
|
self.set_infotext("请选择至少一个文件")
|
||||||
return []
|
return []
|
||||||
have_elf=False
|
have_elf=False
|
||||||
have_bin=False
|
have_bin=False
|
||||||
@@ -395,17 +446,17 @@ class updata_dlg(QObject):
|
|||||||
for i in items:
|
for i in items:
|
||||||
if(i.text()[-4:]==".elf"):
|
if(i.text()[-4:]==".elf"):
|
||||||
if(have_elf==True):
|
if(have_elf==True):
|
||||||
self.show_msg("只可选择一个 .elf 文件")
|
self.set_infotext("只可选择一个 .elf 文件")
|
||||||
return []
|
return []
|
||||||
have_elf=True
|
have_elf=True
|
||||||
if(i.text()[-4:]==".bin"):
|
if(i.text()[-4:]==".bin"):
|
||||||
if(have_bin==True):
|
if(have_bin==True):
|
||||||
self.show_msg("只可选择一个 .bin 文件")
|
self.set_infotext("只可选择一个 .bin 文件")
|
||||||
return []
|
return []
|
||||||
have_bin=True
|
have_bin=True
|
||||||
if(i.text()[-4:]==".lua"):
|
if(i.text()[-4:]==".lua"):
|
||||||
if(have_lua==True):
|
if(have_lua==True):
|
||||||
self.show_msg("只可选择一个 .lua 文件")
|
self.set_infotext("只可选择一个 .lua 文件")
|
||||||
return []
|
return []
|
||||||
have_lua=True
|
have_lua=True
|
||||||
file_list.append(i.text())
|
file_list.append(i.text())
|
||||||
@@ -415,7 +466,7 @@ class updata_dlg(QObject):
|
|||||||
slave_list=[]
|
slave_list=[]
|
||||||
items=self.slave_list.selectedItems()
|
items=self.slave_list.selectedItems()
|
||||||
if(len(items)==0):
|
if(len(items)==0):
|
||||||
self.show_msg("请选择至少一个ip地址")
|
self.set_infotext("请选择至少一个主板ip地址")
|
||||||
return []
|
return []
|
||||||
for i in items:
|
for i in items:
|
||||||
str_list=i.text().split(",")
|
str_list=i.text().split(",")
|
||||||
@@ -632,10 +683,16 @@ class updata_dlg(QObject):
|
|||||||
print("not select any item,break.")
|
print("not select any item,break.")
|
||||||
return
|
return
|
||||||
item=item.split("|")
|
item=item.split("|")
|
||||||
file_name=sql.download(int(item[0]))
|
self.set_infotext("正在从服务器下载文件,可能需要一些时间,请耐心等待.")
|
||||||
dst="file/"+file_name.split("/")[-1]
|
t = threading.Thread(target=self.sql_download, args=(sql,int(item[0]),))
|
||||||
shutil.copy(file_name,dst)
|
t.start()
|
||||||
self.scan_file()
|
# 从数据库下载文件,在后台运行
|
||||||
|
def sql_download(self,sql:mysql.sql,index:int):
|
||||||
|
file_name=sql.download(index)
|
||||||
|
dst="file/"+file_name.split("/")[-1]
|
||||||
|
shutil.copy(file_name,dst)
|
||||||
|
self.sql_download_end_signal.emit()
|
||||||
|
self.set_infotext("已下载文件:"+dst)
|
||||||
|
|
||||||
# 开始运行
|
# 开始运行
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -644,7 +701,6 @@ class updata_dlg(QObject):
|
|||||||
print("run end.")
|
print("run end.")
|
||||||
if(self.dhcp_server!=None):
|
if(self.dhcp_server!=None):
|
||||||
self.dhcp_server.close()
|
self.dhcp_server.close()
|
||||||
# sys.exit(a)
|
|
||||||
|
|
||||||
# 扫描文件
|
# 扫描文件
|
||||||
def scan_file(self):
|
def scan_file(self):
|
||||||
@@ -653,24 +709,15 @@ class updata_dlg(QObject):
|
|||||||
|
|
||||||
# 扫描从机
|
# 扫描从机
|
||||||
def scan_slave(self):
|
def scan_slave(self):
|
||||||
|
self.set_infotext("正在刷新主板IP地址")
|
||||||
u=udp.myudp(1,255)
|
u=udp.myudp(1,255)
|
||||||
ip_prefix=self.ip_prefix.text()
|
ip_prefix=self.ip_prefix.text()
|
||||||
u.find(ip_prefix)
|
u.find(ip_prefix)
|
||||||
ip_list=u.dst_ip_list
|
|
||||||
# if(len(ip_list)==0):
|
|
||||||
# ret=self.show_question("提示","未扫描到从机,是否自动添加?")
|
|
||||||
# if(ret==True):
|
|
||||||
# ip_list.append((ip_prefix+".81","local_id_1"))
|
|
||||||
# ip_list.append((ip_prefix+".82","local_id_2"))
|
|
||||||
# ip_list.append((ip_prefix+".83","local_id_3"))
|
|
||||||
# ip_list.append((ip_prefix+".84","local_id_4"))
|
|
||||||
# ip_list.append((ip_prefix+".85","local_id_5"))
|
|
||||||
# ip_list.append((ip_prefix+".86","local_id_6"))
|
|
||||||
# ip_list.append((ip_prefix+".87","local_id_7"))
|
|
||||||
# ip_list.append((ip_prefix+".88","local_id_8"))
|
|
||||||
list_str=[]
|
list_str=[]
|
||||||
for i in u.dst_ip_list:
|
for i in u.dst_ip_list:
|
||||||
list_str.append(i[0]+','+i[1])
|
list_str.append(i[0]+','+i[1])
|
||||||
|
if(len(list_str)==0):
|
||||||
|
self.set_infotext("未找到主板IP地址,请确保主板已正常连接并运行")
|
||||||
self.slave_list.addItems(list_str)
|
self.slave_list.addItems(list_str)
|
||||||
|
|
||||||
# 添加单个从机地址
|
# 添加单个从机地址
|
||||||
|
Reference in New Issue
Block a user