技术分享 | 2023-03-29
跨平台窗体与组件系统架构设计

01 

概述


允许通过跨平台窗体与组件接口轻松构建图形用户界面,是成熟的软件开发工具的重要特性之一。图形用户界面的开发是异常复杂的。操作系统之间差异巨大,它们各自提供不同的底层图形接口,很多时候,简单地使用这些接口恐怕难以构建出我们所熟悉的图形界面。这就是为什么我们需要基础开发工具库来完成这部分底层工作的原因之一。经由开发工具对底层接口的封装,用户无需直接接触复杂的窗口系统API,就可以用简洁明了、具备清晰语义且自解释的代码像搭积木一样构建图形用户界面。

因此,一套设计成熟的图形界面开发工具库就显得十分重要了。其需要提供易于理解、便于使用的接口,以便降低用户的理解与学习成本,同时也要保证足够的优化能力,让用户在使用开发库的过程中无需付出过高的性能代价。截止目前,业界已有较多的图形界面工具库,其中比较经典的有:Swing、WinForm、Qt Widgets、Tkinter等。


名称

语言

描述

Swing

Java

为Java所设计的GUI工具包,属于Java基础类的一部分。

WinForm

主要是C#

用于生成Windows桌面应用的UI框架,属于.NET的一部分。

Qt Widgets

C++

作为Qt的一个重要模块,提供一整套传统风格的跨平台UI框架。

Tkinter

Python

Python的标准Tk GUI工具包接口。


不同的图形界面工具库拥有各自独特的设计逻辑。如大家比较熟悉的Qt Widgets,其核心是QWidget类,既可以描述一个顶层窗体,又可以通过其派生类描述窗体内的组件;而Winform则使用System.Windows.Forms命名空间中的Form类来表示窗体或对话框,而使用System.Windows.Controls命名空间中的其他类来单独描述组件。

虽然不同的库遵循不同的设计哲学,使用不同的术语,但我们可以提炼出两个核心概念,即窗体和组件:

窗体是指在图形界面下,用户和操作系统交互的基本工具。通常的视觉效果为屏幕上一个带边框的矩形区域。一般上边框内还留有一定高度的区域作为标题栏。用户可以通过拖拽边框或角落来改变窗体的大小,也可以通过拖拽标题栏移动窗体的位置。标题栏通常还会在中央显示一段文本作为窗体的标题,在左侧或右侧提供若干按钮允许用户对窗体进行最小化、最大化和关闭操作。一个图形界面应用程序通常由一个或多个窗体组成,其中一般有一个窗体作为主窗体存在。

组件是内嵌于窗体内部的一个可交互图形实体。其通常占据一块矩形区域,实现一定的用户交互功能。组件可以接受包括鼠标移入移出、单击双击甚至拖拽、键盘操作如按键输入(需要焦点)等。组件也负责自己的视觉效果渲染,如标签组件负责显示一段文本、按钮组件负责接受用户鼠标点击输入并响应等。


02 

窗体与组件树


经过实践与摸索,目前合迅智灵已经完成了一套基于窗体和组件树的架构。该架构将窗体和组件进行了严格的划分,组件依附在窗体上,组件之间存在树形的关系结构。每个组件都有其父组件,每个组件都管理自己下属的零个或多个子组件。这允许组件不仅可以单独使用,还能通过嵌套组合构成更复杂的组件,不同的复杂组件甚至可以对构成自身的“零件”进行“复用”。例如菜单组件LListView可以单独使用,同时也能作为下拉菜单LDropdown的菜单部分使用。

同时,组件树的根节点称为根组件,一个窗体只有一个根组件。在默认情况下,根组件始终填满窗体,该窗体内所有组件,在组件树中都是根组件的直接或间接子节点。

也允许构建无父组件的“独立”组件,但通常都是临时的情况。例如合迅智灵的表格组件LTableView,支持将其他组件挂载到表格的任意单元格上。若我们想朝表格组件的某个单元格中添加一个按钮,就需要先构建一个无父组件的按钮,再调用表格组件的相关接口,将该按钮绑定到指定的单元格。而绑定后按钮,其父组件将被立即设置为表格本身。

在合迅智灵国产化基础开发套件中,通过LWindow类及其派生类可以构建窗体,通过LComponent类及其派生类可以构建组件。窗体与组件树的关系大致可以用下图描述:


窗体与组件树



03 

模态与多窗体


现代应用程序允许多窗体,在多窗体的场景下,每一个窗体都负责管理自己的组件树。

应用程序中还存在一种“模态”窗体,其特点是当模态窗体存在时,用户必须完成与模态窗体的交互,才可以返回应用程序或上一级窗体。因此模态窗体分为两类:

全局模态:用户必须完成与当前模态窗体的交互并关闭之,才可继续与应用程序内其他窗体交互;

窗体模态:模态窗体指定另一个非模态窗体为临时(Transient)父窗体,用户必须完成与当前模态窗体的交互并关闭之,才可继续与其临时父窗体交互。期间不影响用户与该模态窗体的临时父窗体之外的窗体交互。

模态窗体带来了另一种窗体之间的关系。通常用于临时的对话框等场合:若以某普通窗体为基础弹出一个对话框,则用户在完成与对话框的交互之前,其下的普通窗体都处于不可交互的状态。使用合迅智灵提供的LDialog类可以直接创建一个天生以模态形式存在的对话框。当然,用户可以通过手动设置LWindow对象的相关属性,来构建自定义的模态窗体。


多窗体的情形





04 

组件定位与布局


如果我们定义屏幕上向右的方向为X轴正向,向下的方向为Y轴正向,在屏幕坐标系中,每个组件均会记录自身相对于其父组件的坐标位置。例如一个组件的位置在 (0, 0),则表示该组件的左上角和父组件的左上角重合;如果位置在 (20, 10) 则表示该组件的左上角位置相对于其父组件的左上角,X轴向右偏移 20 像素而Y轴向下偏移 10 像素。

在无布局情形(通常也称为绝对定位方式)下,组件的位置不会改变(除非手动设置)。绘制时,从窗体的根组件开始,一层层往下绘制:首先完成自身的绘制,再完成其下子组件的绘制。绘制子组件时,通过自身的绝对位置和子组件相对于自身的位置,可以计算出子组件的绝对位置,从而保证子组件可以绘制到窗体的正确位置上。



组件位置的描述

当然,为了实现复杂的图形用户界面,仅仅使用绝对定位来控制组件的位置,是远远不够的。绝对定位的最大问题是无法实现组件位置和尺寸的自适应。因此我们需要布局系统的协助。在布局语境中,父组件称为子组件的“容器(Container)”,子组件则称为父组件的“元素(Element)”。容器的大小尺寸改变后,其内部的元素将会根据容器的布局策略自动计算并更新自身的位置,从而提高图形用户界面对不同显示设备的适应性。



05 

小结


正如前面所述,想要构建出图形界面应用程序并不是件容易的事情。对于国产操作系统而言,若不借助开发工具的帮助,单纯依靠系统图形接口,开发图形界面应用程序的效率将变得不可接受。长期以来我们都使用国外开发工具进行应用软件开发。这将毋庸置疑地引入严重的软件风险和适配性等问题,大大不利于软件的自主可控性。


作为100%自研的国产C++软件开发平台,合迅智灵在吸收业界先进经验的同时,保持产品的简洁和优雅。其诞生和发展,正是致力于改善对国外开发工具的重度依赖,在极大程度上弥补了国产软件在开发工具方面的空白。软件国产化任重而道远,涉及到底层的研发更是容不得半点马虎。我们的愿景不仅是做“可用”,更要做“好用”、“易用”的开发工具。怀揣着技术报国的雄心,我们必将一步步在这条路上坚定不移地迈进。

推荐新闻
返回列表
试用申请
立即申请
试用申请