class Soul: def __init__(self, ghost, soulid, surfaceID=0): self.ID = soulid self._ghost = ghost self._menu = None self._menustyle = None self._shell_window = None self._dialog_window = None self._size = None self._default_surfaceID = surfaceID self._surface = None self._animations = {} self._clothes = {} self._draw_offset = [] self._base_image = None self._surface_image = None self._soul_image = None self._center_point = QPoint() self._base_rect = QRect() self.init() def init(self): self._shell_window = ShellWindow(self, self.ID) self._dialog_window = DialogWindow(self, self.ID) if self.ID == 0: self._menu = kikka.menu.createSoulMainMenu(self._ghost) else: self._menu = kikka.menu.createSoulDefaultMenu(self._ghost) self.loadClothBind() shellmenu = self._menu.getSubMenu("Shells") if shellmenu is not None: act = shellmenu.getAction(self._ghost.getShell().unicode_name) act.setChecked(True) balloon = self._ghost.getBalloon() if balloon is not None: self._dialog_window.setBalloon(balloon) balloonmenu = self._menu.getSubMenu("Balloons") if balloonmenu is not None: act = balloonmenu.getAction(balloon.name) act.setChecked(True) self._size = self._shell_window.size() self.resetWindowsPosition(False, self._ghost.getIsLockOnTaskbar()) self.setSurface(self._default_surfaceID) self.updateClothesMenu() kikka.menu.updateTestSurface(self._menu, self._ghost, self._default_surfaceID) def show(self): self._shell_window.show() def hide(self): self._shell_window.hide() self._dialog_window.hide() def move(self, *__args): self._shell_window.move(*__args) def pos(self): return self._shell_window.pos() def showMenu(self, pos): if self._menu is not None: self._menu.setPosition(pos) self._menu.show() pass def getGhost(self): return self._ghost def getShellWindow(self): return self._shell_window def getDialog(self): return self._dialog_window def setBalloon(self, balloon): self._dialog_window.setBalloon(balloon) self._dialog_window.repaint() def setMenu(self, Menu): self._menu = Menu def getMenu(self): return self._menu def updateClothesMenu(self): shell = self._ghost.getShell() setting = shell.setting[self.ID] clothesmenu = OrderedDict(sorted(setting.clothesmenu.items())) clothesbind = shell.getBind(self.ID) bindgroups = setting.bindgroups bindoption = setting.bindoption menu = None for act in self._menu.actions(): if act.text() == 'Clothes': menu = act.menu() break if menu is None: return menu.clear() self._clothes.clear() if len(clothesmenu) == 0: menu.setEnabled(False) return menu.setEnabled(True) group = {} for bindgroup in bindgroups.values(): if bindgroup.type not in group: group[bindgroup.type] = QActionGroup(menu.parent()) if bindgroup.type in bindoption: option = bindoption[bindgroup.type] if option == 'multiple': group[bindgroup.type].setExclusive(False) # logging.info("%s %s" % (bindgroup.type, option)) pass for aid in clothesmenu.values(): if aid == -1: menu.addSeparator() elif aid in bindgroups.keys(): bindgroup = bindgroups[aid] text = "%s - %s" % (bindgroup.type, bindgroup.title) act = menu.addMenuItem(text, group=group[bindgroup.type]) callbackfunc = lambda checked, act=act, bindgroup=bindgroup: self.clickClothesMenuItem( checked, act, bindgroup) act.triggered.connect(callbackfunc) act.setCheckable(True) act.setData(aid) if bindgroup.type not in self._clothes.keys(): self._clothes[bindgroup.type] = -1 if (len(clothesbind) == 0 and bindgroup.default is True) \ or (len(clothesbind) > 0 and aid in clothesbind): self._clothes[bindgroup.type] = bindgroup.aID act.setChecked(True) self.setClothes(bindgroup.aID, True) pass pass def clickClothesMenuItem(self, checked, act, bindgroup): shell = self._ghost.getShell() setting = shell.setting[self.ID] # bindgroups = setting.bindgroups bindoption = setting.bindoption # group = act.actionGroup() lastcloth = self._clothes[bindgroup.type] if lastcloth == bindgroup.aID: if (bindgroup.type in bindoption and bindoption[bindgroup.type] != 'mustselect') \ or bindgroup.type not in bindoption: self._clothes[bindgroup.type] = -1 self.setClothes(bindgroup.aID, False) act.setChecked(not act.isChecked()) else: self.setClothes(lastcloth, False) self._clothes[bindgroup.type] = bindgroup.aID self.setClothes(bindgroup.aID, True) self.setSurface(-1) self.saveClothBind() logging.info("clickClothesMenuItem: %s %s", act.text(), act.isChecked()) def saveClothBind(self): data = {} count = kikka.shell.getShellCount() for i in range(count): shell = kikka.shell.getShellByIndex(i) if len(shell.bind) > 0: data[shell.name] = shell.bind[self.ID] self.memoryWrite('ClothBind', data) def loadClothBind(self): data = self.memoryRead('ClothBind', {}) if len(data) <= 0: return for name in data.keys(): shell = kikka.shell.getShell(name) if shell is None: continue shell.bind[self.ID] = data[name] pass def resetAnimation(self, animations): self._animations.clear() for aid, ani in animations.items(): self._animations[aid] = Animation(self, self.ID, ani) self._animations[aid].updateDrawRect() def getAnimation(self): return self._animations def getRunningAnimation(self): runningAni = [] for aid, ani in self._animations.items(): if ani.isRunning is True: runningAni.append(aid) return runningAni def animationStart(self, aid): if aid in self._animations.keys(): self._animations[aid].start() else: logging.warning("animation %d NOT exist!" % aid) def animationStop(self, aid): if aid in self._animations.keys(): self._animations[aid].stop() else: logging.warning("animation %d NOT exist!" % aid) def setSurface(self, surfaceID=-1): if self._surface is None: surfaceID = surfaceID if surfaceID != -1 else self._default_surfaceID elif surfaceID == -1: surfaceID = self._surface.ID elif surfaceID == self._surface.ID: return shell = self._ghost.getShell() if surfaceID in shell.alias.keys(): surfaceID = random.choice(shell.alias[surfaceID]) surface = shell.getSurface(surfaceID) if surface is None and surfaceID != self._default_surfaceID: logging.info("get default surface %d", self._default_surfaceID) surface = shell.getSurface(self._default_surfaceID) surfaceID = self._default_surfaceID if surface is None: logging.error("setSurface FAIL") self._surface = None surfaceID = -1 self.resetAnimation({}) else: logging.info("setSurface: %3d - %s(%s)", surface.ID, surface.name, surface.unicode_name) self._surface = surface self.resetAnimation(surface.animations) self.updateDrawRect() self.repaint() self._shell_window.setBoxes(shell.getCollisionBoxes(surfaceID), self._draw_offset) kikka.menu.updateTestSurface(self._menu, self._ghost, surfaceID) def setDefaultSurface(self): self.setSurface(self._default_surfaceID) def getCurrentSurface(self): return self._surface def getCurrentSurfaceID(self): return self._surface.ID if self._surface is not None else -1 def setClothes(self, aid, isEnable=True): self._ghost.getShell().setClothes(self.ID, aid, isEnable) self.repaint() def getSize(self): return QSize(self._size) def getDrawOffset(self): return QPoint(self._draw_offset) def getCenterPoint(self): return QPoint(self._center_point) def getBaseRect(self): return QRect(self._base_rect) def resetWindowsPosition(self, usingDefaultPos=True, isLockTaskBar=True, rightOffset=0): shell = self._ghost.getShell() w, h = kikka.helper.getScreenClientRect() if usingDefaultPos is True: pos = QPoint(shell.setting[self.ID].position) if pos.x() == WindowConst.UNSET.x(): pos.setX(w - self._size.width() - rightOffset) else: pos = self._shell_window.pos() if isLockTaskBar is True or pos.y() == WindowConst.UNSET.y(): pos.setY(h - self._size.height()) self._shell_window.move(pos) self._shell_window.saveShellRect() def memoryRead(self, key, default): return self._ghost.memoryRead(key, default, self.ID) def memoryWrite(self, key, value): self._ghost.memoryWrite(key, value, self.ID) # ################################################################ def onUpdate(self, updatetime): isNeedUpdate = False for aid, ani in self._animations.items(): if ani.onUpdate(updatetime) is True: isNeedUpdate = True if isNeedUpdate is True: self.repaint() return isNeedUpdate def repaint(self): self.repaintBaseImage() self.repaintSoulImage() self._shell_window.setImage(self._soul_image) self._dialog_window.repaint() def getShellImage(self, faceID): shell_image = self._ghost.getShellImage() filename1 = "surface%04d.png" % faceID filename2 = "surface%d.png" % faceID if filename1 in shell_image: return shell_image[filename1] if filename2 in shell_image: return shell_image[filename2] else: logging.warning("Image lost: %s or %s" % (filename1, filename2)) return kikka.helper.getDefaultImage() def updateDrawRect(self): if self._surface is None or self._surface.ID == -1: self._draw_offset = self._ghost.getShell().getOffset(self.ID) self._size = kikka.const.WindowConst.ShellWindowDefaultSize self._center_point = kikka.const.WindowConst.ShellWindowDefaultCenter self._base_rect = QRect(self._draw_offset, self._size) else: shell_image = self._ghost.getShellImage() baserect = QRect() if len(self._surface.elements) > 0: for i, ele in self._surface.elements.items(): if ele.filename in shell_image: baserect = baserect.united( QRect(ele.offset, shell_image[ele.filename].size())) else: img = self.getShellImage(self._surface.ID) baserect = QRect(0, 0, img.width(), img.height()) self._base_rect = QRect(baserect) baserect.translate(self._ghost.getShell().getOffset(self.ID)) rect = QRect(baserect) for aid, ani in self._animations.items(): rect = rect.united(ani.rect) self._draw_offset = QPoint(baserect.x() - rect.x(), baserect.y() - rect.y()) self._size = rect.size() if self._surface.basepos != WindowConst.UNSET: self._center_point = self._surface.basepos else: self._center_point = QPoint(self._size.width() / 2, self._size.height()) pass def repaintBaseImage(self): shell_image = self._ghost.getShellImage() self._base_image = QImage(self._size, QImage.Format_ARGB32_Premultiplied) painter = QPainter(self._base_image) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.fillRect(self._base_image.rect(), Qt.transparent) painter.end() del painter if self._surface is None: return if len(self._surface.elements) > 0: for i, ele in self._surface.elements.items(): if ele.filename in shell_image: offset = self._draw_offset + ele.offset kikka.helper.drawImage(self._base_image, shell_image[ele.filename], offset.x(), offset.y(), ele.PaintType) else: img = self.getShellImage(self._surface.ID) painter = QPainter(self._base_image) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.drawImage(self._draw_offset, img) painter.end() # self._base_image.save("_base_image.png") pass def repaintSoulImage(self): self._soul_image = QImage(self._base_image) for aid, ani in self._animations.items(): ani.draw(self._soul_image) pass