1. 概述
本文档主要介绍XSConsole的界面显示所使用的主要几个类,以及它们相互之间的关系。
2 XSConsole主界面介绍
Xsconsole主界面元素介绍,如下图所示。主界面可分为两部分,TopWindow(顶部)和MainWindow,这两个都是属于CursesWindow类。(后面介绍各个类以及之间的关系)MainWindow所占据的区域是整个下半部分,包含左侧的MenuPane和右侧的StatusPane,但实际上这一部分界面是有RootDialogue负责的。MenuPane是一个DialoguePane的对象,界面上主要包含有TitleField(显示Customize System),MenuField(菜单列表)和KeyHelpField(按键帮助信息)。StatusPane也是一个DialoguePane的对象,界面上包含TitleField(显示Network Configuration),WrappedTextField(显示描述信息,可自动换行)和StatusField(显示属性名,属性值的关系列表)。
3 XSConsole界面的主要类介绍
如下图所示,主画面所使用的类主要包括:Layout, Dialogue,CursesWindow,DialoguePane,Fields。
3.1 CursesWindow类(XSConsoleCurses.py)
CursesWindow类实际上继承于CursesPane类,用于创建子窗口,可以指定窗口的大小和坐标,以及Title和边框等。实际上,XSConsole是使用curses模块提供的功能来实现字符显示,颜色控制,按键捕获等。首先,调用curses.initscr()创建整个屏幕。然后,调用subwin()创建CursesWindow的子窗口。
AddWrappedText()用于显示字符,可以指定起始坐标,字符串及字符颜色等。
GetKey()用于捕获按键信息。
3.2 Layout类(XSConsoleLayout.py)
Layout,负责控制整个显示画面。
首先,由主程序XSConsoleTerm的Enter()创建一个Layout对象。
self.layout = Layout.NewInst()
然后,调用self.layout.Create()创建TopWindow及MainWindow这两个CursesWindow对象,将主画面分成上下两个子窗口。
最后,self.layout.CreateRootDialogue(…))创建RootDialogue,由RootDialogue(继承于Dialogue类)负责生成MenuPane和StatusPane,并完成其他界面显示的任务。
Layout类可以包含多个CursesWindow和多个Dialogue对象。当界面需要刷新的时候,主程序会调用Layout的Refresh(),如下所示。Layout会依次调用其所包含的CursesWindow和Dialogues的刷新方法。
def Refresh(self):
for window in self.windows:
window.Refresh()
for dialogue in self.dialogues:
dialogue.Render()
Layout管理Dialogue对象的方法是使用堆栈的方式,可以使用PushDialogue(),PopDialogue()和TopDialogue()进行操作。最先创建的Dialogue,即RootDialogue,处于堆栈的底部,在刷新画面时会最先被绘制。而堆栈顶部的Dialogue在最后被绘制,所以总显示在最上面。
3.3 Dialogue类(XSConsoleDialogueBases.py)
Dialogue类是XSConsole最主要的界面管理单元。可以说,XSConsole就是基于Dialogue来管理界面显示,按键捕获处理等。
每个Dialogue可以包含多个DialoguePane,每个DialoguePane其实就是一个子窗口,将整个Dialogue分成几个部分,每个部分的界面显示由各个DialoguePane控制。UpdateFields()用于刷新Dialogue的画面。而HandleKey()用于处理主程序传递过来的按键信息。
Dialogue类还包含了几个辅助子类,如FieldArranger,FieldGroup和InputTracker。FieldArranger类用于排列计算Dialogue包含的所有Fields对象的坐标值。FieldGroup类负责将Fields分为BodyFields,StaticFields和InputFields,以区分显示和处理。InputTracker类用于控制光标在不同Field之间的切换。
3.4 DialoguePane类(XSConsoleDialoguePane.py)
每个DialoguePane类都包含一个子窗口,以及多个Fields。当需要在DialoguePane上显示一串字符信息时,可以调用DialoguePane的AddWrappedTextField()方法。如下所示。
def AddWrappedTextField(self, inText, inFlow = None):
self.AddBodyFieldObj(WrappedTextField(inText, self.selectedColour, FirstValue(inFlow, Field.FLOW_RETURN)))
AddWrappedTextField方法增加一个WrappedTextField对象。当DialoguePane调用Render()进行刷新界面时,会调用每个Field对象的Render方法,例如field.Render(win, xpos, ypos),其中win为DialoguePane所指向的子窗口对象。Field的Render方法会在win子窗口的(xpos,ypos)显示其内容。
3.5 Fields类(XSConsoleFields.py)
Fields类算得上是界面显示的最小控制单元。每种不同的Field实现不同的显示效果。
TitleField类,实现标题栏显示。
MenuField类,实现菜单列表的显示。
KeyHelpField类,在窗口底部实现按键信息显示。
WrappedTextField类,可以显示一段字符串信息,根据窗口大小自动换行。
3. LoginDialogue类分析
以LoginDialogue类说明如何创建一个新的Dialogue类,Layout如何管理Dialogue,以及Dialogue是如何工作的。
在未登陆的情况下,执行某些操作时XSConsole会要求用户做帐户验证。这时会弹出LoginDialogue对话框。如下图所示:
启动LoginDialogue的代码如下所示,调用Layout的PushDialogue将新建的LoginDialogue放进Dialogue堆栈。
Layout.Inst().PushDialogue(LoginDialogue(Lang(‘Please log in to perform this function’), inFunc))
再看LoginDialogue的实现代码,主要也就是需要UpdateFields()和HandleKey(),分别负责界面显示和按键处理。__init__()初始化,获得输入参数信息,新建DialoguePane,然后调用UpdateFields()。UpdateFields()分别增加TitleField,用户名输入Field,密码输入Field和按键信息Field,完成整个画面的显示。
当用户触发按键输入时,HandleKey()被调用,并根据不同按键执行相应的操作。当收到‘Escape’按键时,将调用Layout.Inst().PopDialogue()退出该对话框。PopDialogue()将该LoginDialogue从Layout的Dialogue堆栈中弹出。
class LoginDialogue(Dialogue):
def __init__(self, inText = None, inSuccessFunc = None):
Dialogue.__init__(self)
self.text = inText
self.successFunc = inSuccessFunc
pane = self.NewPane(DialoguePane(self.parent))
pane.TitleSet(“Login”)
pane.AddBox()
self.UpdateFields()
pane.InputIndexSet(0)
def UpdateFields(self):
pane = self.Pane()
pane.ResetFields()
if self.text is not None:
pane.AddTitleField(self.text)
pane.AddInputField(Lang(“Username”, 14), “root”, ‘username’)
pane.AddPasswordField(Lang(“Password”, 14), Auth.Inst().DefaultPassword(), ‘password’)
pane.AddKeyHelpField( {
Lang(“[Esc]”) : Lang(“Cancel”),
Lang(“[Enter]”) : Lang(“Next/OK”),
Lang(“[Tab]”) : Lang(“Next”)
})
def HandleKey(self, inKey):
handled = True
pane = self.Pane()
if inKey == ‘KEY_ESCAPE’:
Layout.Inst().PopDialogue()