# 使用Python编写界面逻辑

在上一章,我们已经使用逻辑编辑器,制作了一个简单的弹出界面,用来发送title消息。

本节将会继续使用上次所制作的界面json文件,使用Python零件开发来重新实现一次功能。

在开始编写之前,我们需要找到上一章使用逻辑编辑器制作界面逻辑的时候,所编辑的项目,然后选择导出资源,找到资源包,ui文件夹中,当时所编辑的文件。

导出并保存到一个能找得到的位置,再新建一个空白附加包,将刚刚导入的界面文件导入。

导入完成之后,可以自行修改命名空间。继续新建一个界面预设,给它命名为TitleScreen。创建完成后,勾选预加载,切换到PushScreen方式打开界面。

到这里的操作和之前使用逻辑编辑器是完全一致的。

接下来我们就可以打开PyCharm,打开这个项目的文件夹,进行代码编辑,第一次打开需要设置Sources Root,这里不再截图展示。

找到脚本文件夹/uiScript/TitleScreen.py,就是我们的ui逻辑文件。

默认第二行的代码是

import client.extraClientApi as clientApi

我们这里可以将其修改为

import mod.client.extraClientApi as clientApi

这样就可以正常的使用补全功能,解决自动提示的报错。

观察这个类,我们可以看到其实和逻辑编辑器中的蓝图零件,可以重写的函数基本一致,同样拥有4个函数。其中OnActivateOnDeactive是只有PushScreen方式创建的界面才会被调用的函数。

使用Python代码创建的UI,有两种逻辑编写的方式。一种是像逻辑编辑器一样,在初始化时为按钮添加回调函数的绑定。还有一种是,使用数据绑定,在Json文件中提前定义好需要调用的Python文件,和代码中的函数和变量进行绑定。

# 方法一

我们首先先介绍第一种方法

那么现在我们就可以在__init__函数中,在初始化时将一些常量定义好。比如我们需要使用到的ui控件的路径。

    def __init__(self, namespace, name, param):
        ScreenNode.__init__(self, namespace, name, param)
        self.mMainPanel = "/main_panel"
        self.mTitleText = self.mMainPanel + "/title_text"
        self.mConfirmButton = self.mMainPanel + "/confirm_button"

这样就定义好了所有我们可能要用到的控件的路径,方便后面的调用。

接下来重写Create函数,为按钮添加回调函数。

    def Create(self):
        """
        @description UI创建成功时调用
        """
        buttonControl = self.GetBaseUIControl(self.mConfirmButton).asButton()
        buttonControl.AddTouchEventParams({"isSwallow": True})
        buttonControl.SetButtonTouchUpCallback(self.OnConfirmButtonClick)

    def OnConfirmButtonClick(self, args):
        pass

这样我们的在按钮点击后,就会触发OnConfirmButtonClick函数

接下来我们回到预设编辑器,新建一个空零件,命名为UILogic,并将其挂接到TitleScreen这个界面预设下。

我们在这个零件中,监听客户端发送过来的发送Title事件。首先需要给我们的这个零件改个好记的名字,这里叫做界面服务端监听,因为我们等会儿还需要在界面逻辑文件中,通过名字获取这个零件的示例,来给自己的服务端发送通知消息。同时在初始化服务器的时候,监听TitleEvent事件,并发送Title指令。

代码参考如下:

@registerGenericClass("UILogicPart")
class UILogicPart(PartBase):
    def __init__(self):
        PartBase.__init__(self)
        self.name = "界面服务端监听"

    def OnRecvTitle(self, args):
        self.SetCommand("/title @a title {}".format(args["text"]))

    def InitServer(self):
        """
        @description 服务端的零件对象初始化入口
        """
        self.ListenSelfEvent("TitleEvent", self, self.OnRecvTitle)

编写完界面服务端监听这个零件后,我们就可以回到界面逻辑的代码文件中,修改按钮回调函数。

在py文件开头处,先引入预设API

import Preset.Controller.PresetApi as presetApi

然后修改按钮回调函数,先获取TitleScreen这个预设,再获取它的零件界面服务端监听,随后调用NotifyToServer函数,发送我们的事件。

发送完成后关闭这个界面。

    def OnConfirmButtonClick(self, args):
        text = self.GetBaseUIControl(self.mTitleText).asTextEditBox().GetEditText()
        presetApi.GetPresetByName("TitleScreen").GetPartByName("界面服务端监听").NotifyToServer("TitleEvent", {"text": text})
        clientApi.PopScreen()

完整代码如下:

# -*- coding: utf-8 -*-
import Preset.Controller.PresetApi as presetApi
import mod.client.extraClientApi as clientApi

ViewBinder = clientApi.GetViewBinderCls()
ViewRequest = clientApi.GetViewViewRequestCls()
ScreenNode = clientApi.GetScreenNodeCls()


class TitleScreen(ScreenNode):
    def __init__(self, namespace, name, param):
        ScreenNode.__init__(self, namespace, name, param)
        self.mMainPanel = "/main_panel"
        self.mTitleText = self.mMainPanel + "/title_text"
        self.mConfirmButton = self.mMainPanel + "/confirm_button"

    def Create(self):
        """
        @description UI创建成功时调用
        """
        buttonControl = self.GetBaseUIControl(self.mConfirmButton).asButton()
        buttonControl.AddTouchEventParams({"isSwallow": True})
        buttonControl.SetButtonTouchUpCallback(self.OnConfirmButtonClick)

    def OnConfirmButtonClick(self, args):
        text = self.GetBaseUIControl(self.mTitleText).asTextEditBox().GetEditText()
        presetApi.GetPresetByName("TitleScreen").GetPartByName("界面服务端监听").NotifyToServer("TitleEvent", {"text": text})
        clientApi.PopScreen()

# 方法二

接下来介绍如何通过数据绑定的方式来获取文本框的数据,绑定按钮的回调函数。

在跟着方法一的步骤操作之后,方法二的区别就主要在uiScript/TitleScreen.py这个界面逻辑文件上。

数据绑定的官方说明文档 点我,可以配合本教程食用。

我们查阅UI说明文档,找到我们所使用的界面控件,查看他的详细Json参数。

例如我们这里使用了按钮文本编辑框

# 按钮

我们主要看文档中的参数解释的最后几个。

可以看到$pressed_button_name这个参数,对应了python的类名和对应的函数。button_mappings代表了具体映射。

$pressed_button_name fpsBattle代表编写UI逻辑的python类名,click代表按钮按下时会执行该python类的click函数。也可以使用API AddTouchEventHandler 动态注册按钮回调
is_handle_button_move_event 表示按钮是否可以响应按钮移动事件,需置true配合API AddTouchEventHandler使用
button_mappings 表示按钮事件响应映射网,需置[]配合API AddTouchEventHandler使用

我们再打开资源包/ui/soldier_title_screen.json这个文件,搜索common.button,找到我们的按钮控件。

可以看到,按钮的json配置中已经存在这两个值,接下来我们按照要求修改,删除button_mappings这个项,并修改$pressed_button_name%文件名.函数名。具体的来说,就是修改为

"$pressed_button_name" : "%TitleScreen.OnConfirmButtonClick",

编辑完这个文件之后,建议不要再次用界面编辑器打开这个界面。否则有可能会被自动覆盖,覆盖后需要再次将button_mappings删除

接下来回到TitleScreen.py,为OnConfirmButtonClick添加绑定,在函数的上一行,添加@ViewBinder.binding(ViewBinder.BF_ButtonClickUp)

同时删除按钮监听的相关函数。

    def Create(self):
        """
        @description UI创建成功时调用
        """
        pass

    @ViewBinder.binding(ViewBinder.BF_ButtonClickUp)
    def OnConfirmButtonClick(self, args):
        text = self.GetBaseUIControl(self.mTitleText).asTextEditBox().GetEditText()
        presetApi.GetPresetByName("TitleScreen").GetPartByName("界面服务端监听").NotifyToServer("TitleEvent", {"text": text})
        clientApi.PopScreen()

# 文本编辑框

接下来继续看文本编辑框的绑定。

绑定的参数主要是$text_edit_box_content_binding_name$text_box_name

我们参考文档中的注1,定义一个变量,存储文本框中的实时内容。

然后定义2个函数,一个赋值,一个返回值,并且修改按钮的回调函数,让按钮从我们定义的这个变量获取文本内容。

class TitleScreen(ScreenNode):
    def __init__(self, namespace, name, param):
        ScreenNode.__init__(self, namespace, name, param)
        self.mText = ""

    @ViewBinder.binding(ViewBinder.BF_EditChanged | ViewBinder.BF_EditFinished)
    def TextBox(self, args):
        self.mText = args["Text"]
        return ViewRequest.Refresh

    @ViewBinder.binding(ViewBinder.BF_BindString)
    def ReturnTextString(self):
        return self.mText

    @ViewBinder.binding(ViewBinder.BF_ButtonClickUp)
    def OnConfirmButtonClick(self, args):
        presetApi.GetPresetByName("TitleScreen").GetPartByName("界面服务端监听").NotifyToServer("TitleEvent", {"text": self.mText})
        clientApi.PopScreen()

那么不难发现,其实数据绑定,只是将一个函数,绑定一个对应的类型,并将其体现到json中。

例如ReturnTextString就返回了一个str类型的变量,并且binding中的类型也是BF_BindString,一个文本变量。

同样的,如果json中需要填写的是int类型的变量,我们也可以定义一个int变量,然后使用binding,绑定BF_BindInt,然后返回这个值,并在json文件中修改对应的函数。

接下来我们再修改Json,将对应的配置和我们的类与函数匹配。

						"$text_box_name" : "%TitleScreen.TextBox",
                        "$text_edit_box_content_binding_name" : "#TitleScreen.ReturnTextString",

上方截取了部分重要的json配置,修改完成后就应该是这样的。

截至目前就修改完成。

界面的完整代码可以在这里下载 (opens new window),其中行为包/uiScript/TitleScreen_V1.py为方法一的代码,供大家参考。

进阶

30分钟

方法一

方法二

按钮

文本编辑框