예제 #1
0
def addName(ID: int, no: int = 0):
    """
    为characterID获取name。
    ID(int):要搜索name的ID。
    no(int):第no个搜索项。
    """
    url = r"https://esi.evetech.net/latest/characters/" + str(
        ID) + r"/?datasource=tranquility"
    Msg = {"addName": []}
    try:
        with rq.get(url, timeout=5) as ret:
            ret = loads(ret.content)
    except Exception as e:
        log("addName:" + str(e), level="error")
        return Msg

    log("addName:ID={ID}已获取到".format(ID=ID))
    if "name" in ret:
        history.update({ret["name"]: {"characterID": ID}})
        Msg["addName"].append([
            ID,
            TMsgEntry(ret["name"],
                      ClickEvent=SearchName,
                      ClickArgs=(ret["name"], ID),
                      style_str=MDStyleStr(
                          color=settings["claddName"],
                          font_size=settings["labelFontSize"]))
        ])
    return Msg
예제 #2
0
    def mousePressEvent(self, e):  # 单击
        global current_thread_set
        self.on_events = True
        self.parent().parent().LabelList_click_no = self.no
        if self.MsgEntry.ClickEvent != None:
            log("label:"+self.text()+",ClickEvent="+str(self.MsgEntry.ClickEvent)+",ClickArgs="+str(self.MsgEntry.ClickArgs))
            current_thread_set=set()
            self.parent().parent().EndSearchEvent()
            if self.MsgEntry.ClickEvent == SearchName:
                self.parent().parent().RefreshLabelList({"Searching": TMsgEntry(
                    text="正在搜索角色{name}...".format(name=str(self.MsgEntry.ClickArgs[0])),
                    style_str=MDStyleStr(
                            color=settings["clHint"],
                            font_size=settings["labelFontSize"]
                        ))})
                self.MsgEntry.ClickReturn = self.parent().parent().MultiThreadRun(func=self.MsgEntry.ClickEvent, args=self.MsgEntry.ClickArgs)
            elif self.MsgEntry.ClickEvent == SearchKM:
                #TODO:增加一个判断,如果已经获取了km则不重复获取
                if self.MsgEntry.ClickReturn == None:
                    self.parent().parent().StatusBar.showMessage("正在获取km...")
                    self.MsgEntry.ClickReturn = self.parent().parent().MultiThreadRun(func=self.MsgEntry.ClickEvent, args=self.MsgEntry.ClickArgs)
                # else:
                #     self.MsgEntry.enable = not self.MsgEntry.enable
                #     self.parent().parent().RefreshLabelList()


        self.on_events=False
예제 #3
0
 def quit(self):
     """
     终止程序。
     """
     self.TrayIcon.hide()
     self.closeEvent(None)
     self.EndSearchEvent()
     log("退出")
     QApplication.quit()
예제 #4
0
    def RefreshLabelList(self, Msg=None):
        """
        刷新LabelList显示。
        Msg(None or dict):作为回调时需要在self.MsgEntryList中添加的信息
        """
        #作为多线程的回调,此进程需要加锁。
        global MutLabelList
        while not MutLabelList.tryLock(1):
            sleep(0.1)
            self.statusBar().showMessage("错误:无法刷新信息列表")
        ret=0

        #由于MsgLabel可能正在响应事件所以不能直接deleteLater
        #使用double buffer
        for i in self.LabelList_buffer:
            while (i.on_events):
                i.on_events=False
                sleep(0.1)
            i.deleteLater()
        for i in self.LabelList:
            i.hide()
        self.LabelList_buffer=self.LabelList[:]
        self.LabelList = []

        if isinstance(Msg,dict):
            self.MsgEntryList.update(Msg)
        #把MsgEntryList展平
        new_label_list = Serialize(self.MsgEntryList)

        count=-1
        for i in new_label_list:
            #每个i都是一个TMsgEntry
            count += 1
            if i.enable:
                self.LabelList.append(TMsgLabel(m=i,no=count))
        for i in self.LabelList:
            i.setParent(self.centralwidget)
            i.setOpenExternalLinks(True)
            i.show()
            i.lower()
        #向上滚动至之前位置
        wheel_pos = self.LabelList_pos
        self.LabelList_pos=0
        for i in range(abs(wheel_pos)):
            if i != -self.LabelList_pos:
                break
            self.wheelEvent(int(wheel_pos / abs(wheel_pos)))
        log("RefreshLabelList:"+','.join([i.text() for i in self.LabelList]))
        MutLabelList.unlock()
        return ret
예제 #5
0
    def StartSearchName(self,name:str=""):
        """
        开始一轮搜索。
        """
        global current_thread_set
        if name=="":
            name = self.EdtName.text()
        #检查这是否是一个合适的name
        name = name.strip(" \n\"\'“”‘’")
        if name == "":
            self.statusBar().showMessage("错误:没有输入名字")
            return - 1
        #如果是一个zkb链接
        fetch = re.search(r"https://zkillboard\.com/character/([0-9]*)/", name)
        if fetch != None:
            log("导入zkb链接"+fetch.group(0))
            self.RefreshLabelList({"LoadFromzkbLink": [TMsgEntry(
                "导入zkb链接 " + fetch.group(0),
                style_str=MDStyleStr(color=settings["clHint"], font_size=settings["labelFontSize"])
            ),TMsgEntry(
                "正在搜索...",
                style_str=MDStyleStr(color=settings["clHint"], font_size=settings["labelFontSize"])
            )]})
            self.MultiThreadRun(func=SearchName, args=(None, int(fetch.group(1))))
        else:
            name=re.search(r"^[a-zA-Z0-9 '\-_]*$", name)
            if name==None:
                #名字中含有非法字符
                self.statusBar().showMessage("错误:名字中含有非法字符")
                return - 1
            else:
                name=name.group(0)

            self.MsgEntryList = {}
            current_thread_set = set()
            log("搜索"+name)
            self.RefreshLabelList({"SearchName": {
                "Searching": [name,
                    TMsgEntry("正在搜索名字包含" + name + '的角色...',
                        style_str=MDStyleStr(
                            color=settings["clHint"],
                            font_size=settings["labelFontSize"]
                        )
                    ),
                ]
            }})
            self.MultiThreadRun(func=SearchName, args=(name,))
        return 0
예제 #6
0
 def run(self):
     log("thread:"+self.__name__+",args="+self.args.__str__())
     self.Msg = self.func(*self.args)
예제 #7
0
    def EndSearchEvent(self,e=None):
        """
        搜索结束事件。
        将传递信息的线程传来的
        """
        if isinstance(self.sender(), TThread):
            log("EndSearch:"+str(self.sender().__name__))
            MutEndSearch.lock()
            Msg = self.sender().Msg
            log("Msg="+str(Msg))

            if id(self.sender()) not in current_thread_set:
                #说明不是此次搜索的返回线程,直接丢弃
                MutEndSearch.unlock()
                return - 1

            #由SearchName返回
            if "getKMList" in Msg:#完成了一轮完整的搜索流程
                self.MsgEntryList = {}
                self.MsgEntryList.update(Msg)
                self.statusBar().showMessage("搜索完成")
                self.EdtName.setStyleSheet("""
                    TEdtName{
                        border: 2px groove white;
                        border-radius: 3px;
                        padding:2px 3px;
                        color:rgb(255,0,0);
                        background-color:rgba(0,0,0,0)
                    }
                    TEdtName:focus{
                        color:rgb(0,255,0);
                        background-color: rgb(0,0,0)
                    } """)
            elif "Error" in Msg:  #SearchName返回错误
                self.MsgEntryList = {}
                self.MsgEntryList.update(Msg)
                if Msg["Error"] == "getKMListError":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("获取KM列表失败",style_str=MDStyleStr(color=settings["clFailed"],font_size=settings["labelFontSize"]))})
                elif Msg["Error"] == "zkbError":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("zkb查询失败",style_str=MDStyleStr(color=settings["clFailed"],font_size=settings["labelFontSize"]))})
                elif Msg["Error"] == "esiError":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("查询角色ID失败", style_str=MDStyleStr(color=settings["clFailed"], font_size=settings["labelFontSize"]))})
                elif Msg["Error"] == "SearchKMError":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("查询KM失败", style_str=MDStyleStr(color=settings["clFailed"], font_size=settings["labelFontSize"]))})
                elif Msg["Error"] == "NoSuchCharacterError":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("无此角色", style_str=MDStyleStr(color=settings["clFailed"], font_size=settings["labelFontSize"]))})
                elif Msg["Error"] == "PlayerNoPVPData":
                    self.MsgEntryList.update({"ErrorLabel": TMsgEntry("没有该角色的统计数据",style_str=MDStyleStr(color=settings["clHint"],font_size=settings["labelFontSize"]))})

            elif "NameList" in Msg:  #多个搜索结果命中
                self.MsgEntryList.update({"MultipleHits": TMsgEntry("命中" + str(len(Msg["NameList"])) + "条搜索结果...",style_str=MDStyleStr(color=settings["clHint"],font_size=settings["labelFontSize"]))})
                NameList = Msg["NameList"][:]
                no=0
                for c in NameList:
                    no+=1
                    self.MultiThreadRun(func=addName, args=(c, no))
            elif "TooManyResults" in Msg:  #搜索结果命中数超过ResultCountLimit
                self.MsgEntryList.update(Msg)
                self.MultiThreadRun(func=SearchName,args=(Msg["name"],-1,True))

            #由addName返回
            elif "addName" in Msg:
                #处理addName返回的情况
                #addName会多并发调用EndSearchEvent,因此需要QMutex
                #addName会返回成对的name和characterID,每个返回都应被添加至self.MsgEntryList["addName"]
                if "addName" not in self.MsgEntryList:
                    self.MsgEntryList["addName"]=[]
                self.MsgEntryList["addName"] += Msg["addName"]

            #由SearchKM返回
            elif "SearchKM" in Msg:
                if self.LabelList_click_no != -1:
                    #需要找到被单击的Label在self.MsgEntryList中的位置
                    for i in range(len(self.MsgEntryList["getKMList"])):
                        #如果getKMList中记录的killmail_id==LabelList中记录的killmail_id
                        if (self.MsgEntryList["getKMList"][i][0][0]==self.LabelList[self.LabelList_click_no].MsgEntry.ClickArgs[1]):
                            self.MsgEntryList["getKMList"][i][2]=Msg
                            break
                self.statusBar().showMessage("KM已获取")
            MutEndSearch.unlock()
            self.RefreshLabelList()
예제 #8
0
    def setupUi(self, MainWindow):
        #载入资源文件
        #载入字体
        for i in font_path:
            if exists(settings["workingDir"]+font_path[i]):
                QtGui.QFontDatabase.addApplicationFont(settings["workingDir"]+font_path[i])
            else:
                log("字体不存在:" + font_path[i] + "(" + settings["lang"] + ")", level="error")
                font_path[i] = "Arial"

        self.setObjectName("MainWindow")
        self.resize(400, 300)
        self.setFixedSize(MainWindow.width(), MainWindow.height())
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setWindowOpacity(settings["WindowOpacity"])
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.WindowStaysOnTopHint)

        #widget
        if exists(settings["workingDir"]+settings["backgroundPath"]):
            backgroundPath=settings["workingDir"]+settings["backgroundPath"]
            log("加载背景图片:" + backgroundPath)
        else:
            backgroundPath = ":/black.png"
            log("未找到背景图片")
        self.RoundWidget = QWidget(parent=MainWindow)
        self.RoundWidget.setGeometry(MainWindow.geometry())
        self.RoundWidget.setStyleSheet("QWidget{border-image: url("+backgroundPath.replace(sep,"/")+r") 100% 100% round;border-radius:15px;}")
        self.RoundWidget.show()

        #拖动功能
        self._startPos,self._endPos=QPoint(0,0),QPoint(0,0)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        QApplication.setQuitOnLastWindowClosed(False)

        self.BtnSearch = QtWidgets.QPushButton(self.centralwidget)
        self.BtnSearch.setGeometry(QtCore.QRect(350, 10, 40, 40))
        self.BtnSearch.setText("")
        self.BtnSearch.setIcon(QIcon(":/AuraProII.ico"))
        self.BtnSearch.setIconSize(QtCore.QSize(40, 40))
        self.BtnSearch.setStyleSheet(r"QPushButton{border:1px solid;padding:5px;border-radius:10px;}")
        self.BtnSearch.setObjectName("BtnSearch")
        QShortcut(QKeySequence(settings["SearchShortCut"]), self.BtnSearch).activated.connect(self.BtnSearchClickEvent)
        self.BtnSearch.setToolTip("快捷键:"+settings["SearchShortCut"])
        self.BtnSearch.clicked.connect(self.EndSearchEvent)
        self.BtnSearch.clicked.connect(self.BtnSearchClickEvent)

        self.StatusBar = QtWidgets.QStatusBar(self.centralwidget)
        self.StatusBar.setStyleSheet("QStatusBar{color:rgba"+settings["clStatusBar"]+";}")
        self.setStatusBar(self.StatusBar)
        self.StatusBar.showMessage("增强型奥拉 II:"+version)

        self.EdtName = TEdtName(self.centralwidget)
        self.EdtName.setGeometry(QtCore.QRect(10, 10, 340, 40))
        self.EdtName.setPlaceholderText("在这里输入名字...")
        self.EdtName.setStyleSheet("""
            TEdtName{
                border:1px grey;
                border-style: none;
                border-radius:10px;
                padding:1px 2px;
                color:rgb(255,0,0);
                background-color:rgba(0,0,0,127)
            }
            """)
        self.EdtName.show()

        self.TrayMenu = QMenu(self)
        self.TrayMenu.addAction(QAction("显示/隐藏主界面", parent=self.TrayMenu,checkable=True,checked=True, triggered=self.ToggleShowHide))
        self.TrayMenu.addAction(QAction("主界面置顶", parent=self.TrayMenu, checkable=True, checked=True, triggered=self.ToggleStayOnTop))
        self.TrayMenu.addSeparator()
        self.TrayMenu.addAction(QAction("关于...", parent=self.TrayMenu, triggered=self.ShowAbout))
        self.TrayMenu.addSeparator()
        self.TrayMenu.addAction(QAction("停用 增强型奥拉II",parent=self.TrayMenu, triggered=self.quit))

        self.TrayIcon = QSystemTrayIcon(self)
        self.TrayIcon.setIcon(QIcon(":/AuraProII.ico"))
        self.TrayIcon.setToolTip(u'增强型奥拉 II:激活中')
        self.TrayIcon.show()
        self.TrayIcon.showMessage(u"增强型奥拉 II:"+version, "Fly safe o/", 0)
        self.TrayIcon.activated.connect(self.TrayIconClickEvent)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(lambda:self.TrayMenu.exec_(QCursor.pos()))
        self.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        #窗体创建完毕
        #信息列表
        self.MsgEntryList = {}
        # MsgEntryList结构
        # 每项为每个过程加入的信息
        # 如,StartSearchKB后,MsgEntryList中的内容应为:
        # {"SearchName":{
        #                   "Label":"正在搜索角色",
        #                   "ID":*******
        #               }
        #  "SearchKB":{...}
        # RefreshLabelList只会显示被SerializeMsgEntryList抽出的TMsgLabel对象

        #用于显示信息列表的Label列表
        self.LabelList = []
        self.LabelList_buffer = []
        self.LabelList_click_no = -1
        self.LabelList_pos=0

        #窗体创建完毕
        log("窗体创建完毕")
예제 #9
0
 def dropEvent(self, e):
     #如果是zkb链接:
     log("拖入"+e.mimeData().text())
     self.setText(e.mimeData().text())
     self.parent().parent().StartSearchName(e.mimeData().text())
예제 #10
0
def SearchName(name: str = '', ID: int = -1, is_strict=False):
    """
    获取角色ID与zkb统计信息。
    name(str):角色名
    ID(int):如果已有角色ID则跳过搜索characterID的步骤
    is_strict(bool):严格模式,只搜索与name完全一致的角色名
    """
    global MutSearchName
    MutSearchName.lock()
    Msg = {}
    log("SearchName:name={name},ID={ID},is_strict={is_strict}".format(
        name=name, ID=ID, is_strict=is_strict))
    #characterID
    if ID < 0:  #如果没有给出characterID
        name = name.strip(" \n")
        for i in history:
            if i.upper() == name.upper():
                name = i
                ID = history[name]["characterID"]
                break
        if ID < 0:  #搜索历史记录未命中
            log("SearchName:历史记录未命中")
            strict = "&strict=true" if is_strict else "&strict=false"
            url = r"https://esi.evetech.net/latest/search/?categories=character&datasource=tranquility&language=en-us&search=" + name.replace(
                " ", "+") + strict
            try:

                with rq.get(url, timeout=5) as ret:
                    ret = loads(ret.content)
            except Exception as e:
                log("SearchName:" + str(e), level="error")
                Msg.update({"Error": "esiError"})
                MutSearchName.unlock()
                return Msg
            if "character" in ret:
                ID = ret["character"]
                if (len(ID) > 1):
                    log("SearchName:命中{l}/{lmax}个结果".format(
                        l=len(ID), lmax=settings["ResultCountLimit"]))
                    #有多个命中结果
                    if len(ID) < settings["ResultCountLimit"]:
                        Msg.update({"NameList": ID})
                        MutSearchName.unlock()
                        return Msg
                    else:
                        #结果过多
                        Msg.update({
                            "TooManyResults": TMsgEntry("搜索结果数量超过" + \
                                str(settings["ResultCountLimit"]) + "个,改为严格模式...",
                                style_str=MDStyleStr(
                                    color=settings["clHint"],
                                    font_size=settings["labelFontSize"]
                            )),
                            "name": name
                            })
                        MutSearchName.unlock()
                        return Msg
                else:
                    ID = ID[0]
            else:
                Msg.update({"Error": "NoSuchCharacterError"})
                log("SearchName:esi查询失败:{name}".format(name=name),
                    level="warning")
                MutSearchName.unlock()
                return Msg
        Msg.update({"SearchName": [name, ID]})

    #zkb
    url = r"https://zkillboard.com/api/stats/characterID/" + str(ID) + r"/"
    try:
        with rq.get(url, timeout=5) as ret:
            ret = loads(ret.content)
    except Exception as e:
        Msg.update({"Error": "zkbError"})
        MutSearchName.unlock()
        return Msg

    #角色信息为None
    if (ret == {}) or (ret["info"] == None):
        Msg.update({"Error": "PlayerNoPVPData"})
        MutSearchName.unlock()
        return Msg

    if "dangerRatio" not in ret:
        ret["dangerRatio"] = 0
    danger_ratio_color = (int(ret["dangerRatio"] / 100 * 255),
                          int((1 - (ret["dangerRatio"] / 100)) * 255), 0)

    if "gangRatio" not in ret:
        ret["gangRatio"] = 100
    solo_ratio_color = (int((1 - (ret["gangRatio"] / 100)) * 255),
                        int(ret["gangRatio"] / 100 * 255), 0)

    Msg.update({
        "SearchKB": {
            "name": [
                ret["info"]["name"],
                TMsgEntry(
                    r"<a href='https://zkillboard.com/character/" + str(ID) +
                    r"' style='color:blue'>" + ret["info"]["name"] + "</a>",
                    style_str=MDStyleStr(color=settings["clURL"],
                                         font_size=settings["labelFontSize"]))
            ],
            "dangerRatio": [
                ret["dangerRatio"],
                TMsgEntry("危险度:" + str(ret["dangerRatio"]) + "%",
                          style_str=MDStyleStr(
                              color=danger_ratio_color,
                              font_size=settings["labelFontSize"]))
            ],
            "soloRatio": [
                ret["gangRatio"],
                TMsgEntry("solo率:" + str(100 - ret["gangRatio"]) + "%",
                          style_str=MDStyleStr(
                              color=solo_ratio_color,
                              font_size=settings["labelFontSize"]))
            ],
            "topShips":
            [[i["shipTypeID"] for i in ret["topLists"][3]["values"][:3]],
             TMsgEntry("最高击杀舰船:" + ','.join([
                 getNamebyID(i["shipTypeID"]) + "(" + str(i["kills"]) + "次)"
                 for i in ret["topLists"][3]["values"][:3]
                 if i["shipName"] != "Capsule"
             ]),
                       style_str=MDStyleStr(
                           color=settings["cltopShips"],
                           font_size=settings["labelFontSize"]))],
            "topSolarSystem":
            [[i["solarSystemName"] for i in ret["topLists"][4]["values"][:3]],
             TMsgEntry("最常出没:" + ','.join([
                 i["solarSystemName"] + "(" + str(i["kills"]) + "次)"
                 for i in ret["topLists"][4]["values"][:3]
             ]),
                       style_str=MDStyleStr(
                           color=settings["cltopSolarSystem"],
                           font_size=settings["labelFontSize"]))]
        }
    })

    history.update({ret["info"]["name"]: {"characterID": ID}})
    SaveFile(history, settings["workingDir"] + "history.json")

    #getKMList
    url = r"https://zkillboard.com/api/kills/characterID/" + str(ID) + r"/"
    try:
        with rq.get(url, timeout=5) as ret:
            ret = loads(ret.content)
    except Exception as e:
        log("getKMList:" + str(e), level="error")
        Msg.update({"Error": "getKMListError"})
        MutSearchName.unlock()
        return Msg

    #玩家可能没有击杀km
    if ret == []:
        Msg.update({"getKMList": []})
        MutSearchName.unlock()
        return Msg

    km_count = min(settings["KMCounts"], len(ret))
    killmail_pairs = [(ret[i]["killmail_id"], ret[i]["zkb"]["hash"])
                      for i in range(km_count)]
    remap = []
    label_list = [
        TMsgEntry(str(i + 1) + ".最近km(" +
                  Valuable(ret[i]["zkb"]["totalValue"]) + ' isk)',
                  style_str=MDStyleStr(color=settings["clKM"],
                                       font_size=settings["labelFontSize"]),
                  ClickEvent=SearchKM,
                  ClickArgs=(ID, killmail_pairs[i][0], killmail_pairs[i][1]))
        for i in range(len(killmail_pairs))
    ]
    for i in killmail_pairs:
        remap.append([i, label_list[0], {}])
        label_list = label_list[1:]
    Msg.update({"getKMList": remap})
    log("getKMList:完成")
    MutSearchName.unlock()
    return Msg
예제 #11
0
def SearchKM(character_id: int, killmail_id: int = 0, killmail_hash: str = ''):
    """
    获取一个特定的km
    character_id(int):角色id
    killmail_id(int):该killmail的id
    killmail_hash(str):由ccp给出的km哈希值
    """
    global MutSearchKM
    MutSearchKM.lock()
    Msg = {}
    log("SearchKM:character_id={character_id},killmail_id={killmail_id},killmail_hash={killmail_hash}"
        .format(character_id=character_id,
                killmail_id=killmail_id,
                killmail_hash=killmail_hash))
    url = r"https://esi.evetech.net/latest/killmails/"\
            + str(killmail_id)\
            + r"/" + killmail_hash + r"/?datasource=tranquility"
    try:
        with rq.get(url, timeout=5) as ret:
            ret = loads(ret.content)
    except Exception as e:
        log("SearchKM:" + str(e), level="error")
        Msg.update({"Error": "SearchKMError"})
        MutSearchKM.unlock()
        return -1

    Msg = {
        "SearchKM": {
            "time": [
                ret["killmail_time"].replace('T', ' ').replace('Z', ''),
                TMsgEntry(
                    "  (" + ret["killmail_time"].replace('T', ' ').replace(
                        'Z', '' + ")"),
                    style_str=MDStyleStr(color=settings["clHint"],
                                         font_size=settings["labelFontSize"]))
            ],
            "victimShip": [
                ret["victim"]["ship_type_id"],
                TMsgEntry(
                    r"&nbsp;&nbsp;<a href='https://zkillboard.com/kill/" +
                    str(killmail_id) + r"/' style='color:blue'>击毁:" +
                    getNamebyID(ret["victim"]["ship_type_id"]) + r"</a>",
                    style_str=MDStyleStr(color=settings["clURL"],
                                         font_size=settings["labelFontSize"]))
            ]
        }
    }
    for i in ret["attackers"]:
        if ("character_id" in i) and (i["character_id"] == character_id):
            ship = getNamebyID(
                i["ship_type_id"]) if "ship_type_id" in i else "(?)"
            weapon = getNamebyID(
                i["weapon_type_id"]) if "weapon_type_id" in i else "(?)"
            if ship == weapon:
                weapon = "(混合)"
            Msg["SearchKM"].update({
                "shipType": [
                    ship,
                    TMsgEntry("   · " + ship,
                              style_str=MDStyleStr(
                                  color=settings["clshipType"],
                                  font_size=settings["labelFontSize"]))
                ],
                "weaponType": [
                    weapon,
                    TMsgEntry("   · " + weapon,
                              style_str=MDStyleStr(
                                  color=settings["clweaponType"],
                                  font_size=settings["labelFontSize"]))
                ]
            })
            break
    log("SearchKM:完成")
    MutSearchKM.unlock()
    return Msg