如何透过扩展现有类的能力来创建用户自定义界面的控件

本文介绍了如何利用ExtJS框架创建自定义UI控件,包括选择合适的基类、使用模板方法模式来定制行为等关键步骤。
组合或扩展

当创建一个新类,往往要作出这么的一个选择:要么拥有某个工具类的实例来扮演首要的角色,要么扩展那个类。

使用ExtJs过程中,推荐从最靠近的基类开始扩展,实现所需的功能即可。这是因为Ext提供的自动生存周期引入了自动渲染的机制、自动大小调整和承担接受来自布局管理器的UI组件布局调控,还有在容器(Container)中自动销毁的功能。

组织一个新类,它就是ExtJs的类,实现起来是很方便的,这就会导致了Container→Component层次的形成,相比较,新类拥有一个ExtJs类的话,必须从外围对其渲染和组织。
The Template method Pattern模板方法模式

Ext Js的Component的继承层次采用Template Method pattern(模板方法模式)来委派给子类,交由子类来负责其特定的行为。

其意义在于,继承链中的每个子类得以在,组件生存周期的某个阶段内,“提交(contribute)”额外的逻辑功能。这样每一个类拥有属于其自身的行为;其他类加入自己的行为时也不会相互造成影响。

其中一个例子是render(渲染)函数。render函数不能够被覆盖,应该是一层一层地在各子类的实现中加入onRender方法,那么render函数就会在执行的时候把各onRender方法访问调用。每一个onRender方法必须调用其父类的onRender方法继而再“埋头处理(contribute)”自己(子类)的逻辑部分。

下面的图例演示了模板方法onRender的机能过程。

render是由容器的布局管理器(Container’s layout manager)负责调用。该方法的实现不能被“冲掉(overridden)”,它是由Ext基类提供的。this.onRender表示当前子类所写的实现(如有提供的话)。它会访问父类的版本、父类的版本又会调用父、父类的版本……最终,每个类完成了其功能,render函数的返回值对生存周期进行控制。

Image:Classflowchart.PNG

在ExtJS组件(Component)生存周期中,提供了若干有意义的模板方法以实现类特定的逻辑功能。

强调: 当编写子类时,模板方法应在实例化的过程中调用,属于生存周期内的一部分的动作,而不应是事件的一部分。事件有可能会由handler挂起,或中止。

以下是Component子类都可享有的模板方法:

* onRender

允许渲染期间加入特定的行为。父类的onRender被调用后,可以确定组件的Element元素。可以在此阶段中执行剩余DOM任务以完成结构上的控制(HTML结构)。

* afterRender

允许渲染完成后加入特定的行为。此阶段的组件元素会根据配置的要求(configuartion)设置样式,会引入已配置的CSS样式名称所指定的样式名称,并会配置可见性(visibility)和配置可激活(enable)情况。

* onShow

允许在显示组件的同时加入特定的行为。父类的onShow执行过后,Componenet将会显示。

* onHide

允许在隐藏组件的同时加入特定的行为。父类的onHide执行过后,Componenet将会隐藏。

* onDisable

允许在禁用组件的同时加入特定的行为。父类的onDisable执行过后,Componenet将会禁用。

* onEnable

允许在激活组件的同时加入特定的行为。父类的onEnable执行过后,Componenet将会激活启用。

* onDestroy

允许在销毁组件的同时加入特定的行为。父类的onDestroy执行过后,Componenet将会被销毁。

Ext组件类的各层次中的均有其自身的模板方法,我们可以打开来看看,这些都是根据自身不同的需求而作出的设计。

提示: 当调用父类的模板方法时,最简洁的方法就是使用Function.apply,保证所有的参数都可以接受得到,传送给那个模板方法:

Ext.ux.Subclass.superclass.onRender.apply(this, arguments);

要扩展哪个类

选择适合的类来扩展不但要考虑基类提供哪些功能,而且对性能方面也要着重考虑。无论有多少个UI控件被渲染或调控,Ext.Panel常常就是被衍生(extend)的对象。

Panel类拥有许多的能力:

* Border(躯干)
* Header(头部)
* Header工具条
* Footer(底部)
* Footer按钮
* Top toolbar(顶部工具条)
* Bottom toolbar(底部工具条)
* 承托和管理子组件

如果这些派不上用场,那使用Panel便是资源浪费。
Component(组件类)

如果要求的UI控件不需要其他的细节的控件,也就是,仅仅是封装某部分的HTML元素的话,那么可取的扩展对象就是Ext.BoxComponent或Ext.Component。如果再缩窄一步,我不需要听凭父容器提供的大小调控功能,那么使用Ext.Component就可以了。

强调: Component类并不会内省而得知哪一种元素作为holder。因此为了创建所需的元素(Element),应设定autoEl的配置项。

例如,要把一张图片封装为Component,我们于是乎这样定义:

Ext.ux.Image = Ext.extend(Ext.Component, {
autoEl: {
tag: 'img',
src: Ext.BLANK_IMAGE_URL,
cls: 'tng-managed-image'
},

// Add our custom processing to the onRender phase.
// We add a ‘load’ listener to our element.
onRender: function() {
Ext.ux.Image.superclass.onRender.apply(this, arguments);
this.el.on('load', this.onLoad, this);
},

onLoad: function() {
this.fireEvent('load', this);
},

setSrc: function(src) {
this.el.dom.src = src;
}
});

这是一个可封装图片的Ext Component类,可参与非箱子方寸模型(non box-sizing)的布局。
BoxComponent

如果要求的UI控件不需要其他的细节的控件,也就是,仅仅是封装某部分的HTML元素的话,还要听凭布局管理器提供的大小尺寸、布局的调控,那么这个的扩展对象就是Ext.BoxComponent。

例如,假设一个Logger类打算是简单地显示log信息,就必须嵌入某种布局的风格,例如插入到一个layout:’fit’窗体,可以这样定义:

Ext.ux.Logger = Ext.extend(Ext.BoxComponent, {
tpl: new Ext.Template("<li class='x-log-entry x-log-{0:lowercase}-entry'>",
"<div class='x-log-level'>",
"{0:capitalize}",
"</div>",
"<span class='x-log-time'>",
"{2:date('H:i:s.u')}",
"</span>",
"<span class='x-log-message'>",
"{1}",
"</span>",
"</li>"),

autoEl: {
tag: 'ul',
cls: 'x-logger'
},

onRender: function() {
Ext.ux.Logger.superclass.onRender.apply(this, arguments);
this.contextMenu = new Ext.menu.Menu({
items: [new Ext.menu.CheckItem({
id: 'debug',
text: 'Debug',
checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
scope: this
}), new Ext.menu.CheckItem({
id: 'info',
text: 'Info',
checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
scope: this
}), new Ext.menu.CheckItem({
id: 'warning',
text: 'Warning',
checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
scope: this
}), new Ext.menu.CheckItem({
id: 'error',
text: 'Error',
checkHandler: Ext.ux.Logger.prototype.onMenuCheck,
scope: this
})]
});
this.el.on('contextmenu', this.onContextMenu, this, {stopEvent: true});
},

onContextMenu: function(e) {
this.contextMenu.logger = this;
this.contextMenu.showAt(e.getXY());
},

onMenuCheck: function(checkItem, state) {
var logger = checkItem.parentMenu.logger;
var cls = 'x-log-show-' + checkItem.id;
if (state) {
logger.el.addClass(cls);
} else {
logger.el.removeClass(cls);
}
},

debug: function(msg) {
this.tpl.insertFirst(this.el, ['debug', msg, new Date()]);
this.el.scrollTo("top", 0, true);
},

info: function(msg) {
this.tpl.insertFirst(this.el, ['info', msg, new Date()]);
this.el.scrollTo("top", 0, true);
},

warning: function(msg) {
this.tpl.insertFirst(this.el, ['warning', msg, new Date()]);
this.el.scrollTo("top", 0, true);
},

error: function(msg) {
this.tpl.insertFirst(this.el, ['error', msg, new Date()]);
this.el.scrollTo("top", 0, true);
}
});

接着是CSS:

.x-logger {
overflow: auto;
}
.x-log-entry .x-log-level {
float: left;
width: 4em;
text-align: center;
margin-right: 3px;
}
.x-log-entry .x-log-time {
margin-right: 3px;
}
.x-log-entry .x-log-message {
margin-right: 3px;
}
.x-log-debug-entry, .x-log-info-entry, .x-log-warning-entry, .x-log-error-entry {
display: none;
}

.x-log-show-debug .x-log-debug-entry { display: block }
.x-log-show-info .x-log-info-entry { display: block }
.x-log-show-warning .x-log-warning-entry { display: block }
.x-log-show-error .x-log-error-entry { display: block }

.x-log-debug-entry .x-log-level { background-color: #46c }
.x-log-info-entry .x-log-level { background-color: green }
.x-log-warning-entry .x-log-level { background-color: yellow }
.x-log-error-entry .x-log-level { background-color: red }

我们吧log的信息的HTML列表均放置在一个布局中。我们在onRender的阶段加入处理,使得右键菜单可以根据CSS样式类的名称操控logged条目的可见性。位于该层次的对象还提供了特别的模板方法:

* onResize

此时此刻,BoxComponent的大小已经发生变化,此时可执行剩余的任务。

* onPosition

此时此刻,BoxComponent的定位已经发生变化,此时可执行剩余的任务。

Container(容器类)

如果要求的UI控件将用于承载(Contain)其他UI元素在其身上,但并不需要前文提及到的Ext.Panel那么多的功能,为避免臃肿,应采用Ext.Container容器类来继承。同样地,autoEl指定元素的配置项亦必不可少,将用于容器在某个元素之上进行渲染。同样,在视觉控制方面,滚动条是否显示方面(即overflow属性),用户都可以使用Style配置项,或容器元素的class属性的两种方式进行CSS样式制定。

注意: 对于Container层次,不要忘记哪种布局类是被用于渲染和调控子组件的。

示例中的类封装了条件命令的查询,允许用户对Store基于测试字段的数据筛选。除了功能上的封装外,还把查询任务作统一布局,封装在一个可控类中,可方便从容器身上自动添加或移除查询的条目,灵活性更高:

Ext.ux.FilterCondition = Ext.extend(Ext.Container, {
layout: 'table',

layoutConfig: {
columns: 7
},

autoEl: {
cls: 'x-filter-condition'
},

Field: Ext.data.Record.create(['name', 'type']),

initComponent: function() {
this.fields = this.store.reader.recordType.prototype.fields;
this.fieldStore = new Ext.data.Store();

// Create a Store containing the field names and types
// in the passed Store.
this.fields.each(function(f) {
this.fieldStore.add(new this.Field(f))
}, this);

// Create a Combo which allows selection of a field
this.fieldCombo = new Ext.form.ComboBox({
triggerAction: 'all',
store: this.fieldStore,
valueField: 'name',
displayField: 'name',
editable: false,
forceSelection: true,
mode: 'local',
listeners: {
select: this.onFieldSelect,
scope: this
}
});

// Create a Combo which allows selection of a test
this.testCombo = new Ext.form.ComboBox({
triggerAction: 'all',
store: ['<', '<=', '=', '!=', '>=', '>']
});

// Inputs for each type of field. Hidden and shown as necessary
this.booleanInput = new Ext.form.Checkbox({
hideParent: true,
hidden: true
});
this.intInput = new Ext.form.NumberField({
allowDecimals: false,
hideParent: true,
hidden: true
});
this.floatInput = new Ext.form.NumberField({
hideParent: true,
hidden: true
});
this.textInput = new Ext.form.TextField({
hideParent: true,
hidden: true
});
this.dateInput = new Ext.form.DateField({
hideParent: true,
hidden: true
});

this.items = [ this.fieldCombo, this.testCombo, this.booleanInput, this.intInput, this.floatInput, this.textInput, this.dateInput];
Ext.ux.FilterCondition.superclass.initComponent.apply(this, arguments);
},

onFieldSelect: function(combo, rec, index) {
this.booleanInput.hide();
this.intInput.hide();
this.floatInput.hide();
this.textInput.hide();
this.dateInput.hide();
var t = rec.get('type');
if (t == 'boolean') {
this.booleanInput.show();
this.valueInput = this.booleanInput;
} else if (t == 'int') {
this.intInput.show();
this.valueInput = this.intInput;
} else if (t == 'float') {
this.floatInput.show();
this.valueInput = this.floatInput;
} else if (t == 'date') {
this.dateInput.show();
this.valueInput = this.dateInput;
} else {
this.textInput.show();
this.valueInput = this.textInput;
}
},

getValue: function() {
return {
field: this.fieldCombo.getValue(),
test: this.testCombo.getValue(),
value: this.valueInput.getValue()
};
}
});

此类管理了其包含的输入字段,可以精确的布局-大小调整,外补丁等等——都是通过CSS样式分配到元素身上这样来起作用的。

位于该层次的对象还提供了特别的模板方法:

* onBeforeAdd

当添加新的子组件的时候,就会调用该方法。这时会有新组件作为参数传入,或者可修改它,或者以特别的方式准备好Container。返回false表示终止添加的操作。

Panel

如果所需的UI控件要求头部、底部、或工具条之类的元素,那么Ext.Panel就是一个很不错的类给予继承了。

注意: Panel是容器的一种,不要忘记哪种布局类是被用于渲染和调控子组件的。

通常Ext.Panel所实现的类会有很高的程序结合性,一般用于与其他UI控件协调使用(通常Containers,或表单字段),并对其有特定配置的布局风格。另外,要对在其内的组件提供操作的命令,可以从tbar(顶部工具栏),bbar(底部工具栏)的两方面设置加以控制。
Field

如果所需的UI控件要求为用户交互,可以把程序的数据显示给用户,或修改进而发生给服务器的功能,那么要被扩展的类应该是Ext.form.TextField,或Ext.Form.NumberField。另外,如果要求轮换按钮(Trigger button),以备键盘按键的轮换,那就是Ext.form.TriggerField。

位于该层次的对象还提供了特别的模板方法:

* onFocus:input输入框得到焦点后即会触发该方法的执行。
* onBlur:input输入框失去焦点后即会触发该方法的执行。

什么时候不需要子类

有些时候,滥用子类无异于“杀鸡用牛刀”。在一些特定应用场合,某个现有的类它的方法被添加、被重写,是由这个类的构造器实例化过程中依靠参数传入的。这时候模板方法依然可用但不能够用superclass的方式,去访问父类对象;这时,我们就要使用类自身的方式得到父类对象,调用模板方法,实质上它是通过构造器的原型的路径寻址(见this.constructor.prototype)。

例如我们要一次性地在一个实例中创建带有按钮的文本框以测试输入的web地址是否正确:

new Ext.form.TextField({
name: 'associatedUrl',
fieldLabel: 'Associated page',

// Wrap the input, and render the test button
onRender: function() {
this.constructor.prototype.onRender.apply(this, arguments);
this.wrap = this.el.wrap({
cls: 'x-form-field-wrap'
});
this.testButton = new Ext.Button({
renderTo: this.wrap,
buttonSelector: 'em',
cls: 'x-trigger-button',
style: {
position: 'absolute',
right: 0,
top: 0
},
text: 'Test',
handler: this.testUrl,
scope: this
});
if(!this.width){
this.wrap.setWidth(this.el.getWidth() +
this.testButton.getEl().getWidth() + 5);
}
},

// Keep the wrap size correct
onResize : function(w, h){
this.constructor.prototype.onResize.call(this, w, h);
if(typeof w == 'number'){
this.el.setWidth(this.adjustWidth('input', w -
(this.testButton.getEl().getWidth() + this.buttonSpacing)));
}
this.wrap.setWidth(this.el.getWidth() +
this.testButton.getEl().getWidth() + this.buttonSpacing);
},

// Perform the test by opening the fields value as a web page
testUrl: function(button, event) {
if (this.isValid()) {
var e = this.testButton.getEl();
var w = window.open(this.getValue(), this.id.replace('-',''), 'status=true');
}
}
})

综述

* 扩展现有的Ext组件类以便发挥可控生存周期的能力。
* 从基础功能的类起实现扩展。
* 用模板方法加入和重写行为。
* 创建实例指定的模板方法,用在一次性的设计中。

转http://www.extjs.com/learn/Tutorial:Creating_new_UI_controls_%28Chinese%29
安全帽与口罩检测数据集 一、基础信息 数据集名称:安全帽与口罩检测数据集 图片数量: - 训练集:1690张图片 - 验证集:212张图片 - 测试集:211张图片 - 总计:2113张实际场景图片 分别: - HelmetHelmet:戴安全帽的人员,用于安全防护场景的检测。 - personwithmask:戴口罩的人员,适用于公共卫生监测。 - personwith_outmask:未戴口罩的人员,用于识别未遵守口罩佩戴规定的情况。 标注格式:YOLO格式,包含边界框和别标签,适用于目标检测任务。 数据格式:JPEG/PNG图片,来源于实际监控和场景采集,细节清晰。 二、适用场景 工业安全监控系统开发: 数据集支持目标检测任务,帮助构建自动检测人员是否佩戴安全帽的AI模型,适用于建筑工地、工厂等环境,提升安全管理效率。 公共卫生管理应用: 集成至公共场所监控系统,实时监测口罩佩戴情况,为疫情防控提供自动化支持,辅助合规检查。 智能安防与合规检查: 用于企业和机构的自动化安全审计,减少人工干预,提高检查准确性和响应速度。 学术研究与AI创: 支持计算机视觉目标检测领域的研究,适用于安全与健康相关的AI模型开发和论文发表。 三、数据集优势 精准标注与实用性: 每张图片均经过标注,边界框定位准确,别定义清晰,确保模型训练的高效性和可靠性。 场景多样性与覆盖性: 包含安全帽和口罩相关别,覆盖工业、公共场所以及多种实际环境,样本丰富,提升模型的泛化能力和适应性。 任务适配性强: 标注兼容主流深度学习框架(如YOLO),可直接用于目标检测任务,便于快速集成和部署。 实际应用价值突出: 专注于工业安全和公共健康领域,为自动化监控、合规管理以及疫情防护提供可靠数据支撑,具有较高的社会和经济价值。
内容概要:本文围绕FOC电机控制代码实现与调试技巧在计算机竞赛中的应用,系统阐述了从基础理论到多场景优化的完整技术链条。文章深入解析了磁链观测器、前馈控制、代码可移植性等关键概念,并结合FreeRTOS多任务调度、滑动窗口滤波、数据校验与热仿真等核心技巧,展示了高实时性与稳定性的电机控制系统设计方法。通过服务机器人、工业机械臂、能源赛车等典型应用场景,论证了FOC在复杂系统协同中的关键技术价值。配套的千行级代码案例聚焦分层架构与任务同步机制,强化工程实践能力。最后展望数字孪生、低代码平台与边缘AI等未来趋势,体现技术前瞻性。; 适合人群:具备嵌入式开发基础、熟悉C语言与实时操作系统(如FreeRTOS)的高校学生或参赛开发者,尤其适合参与智能车、机器人等综合性竞赛的研发人员(经验1-3年为佳)。; 使用场景及目标:① 掌握FOC在多任务环境下的实时控制实现;② 学习抗干扰滤波、无传感器控制、跨平台调试等竞赛实用技术;③ 提升复杂机电系统的问题分析与优化能力; 阅读建议:此资源强调实战导向,建议结合STM32等开发平台边学边练,重点关注任务优先级设置、滤波算法性能权衡与观测器稳定性优化,并利用Tracealyzer等工具进行可视化调试,深入理解代码与系统动态行为的关系。
【场景削减】拉丁超立方抽样方法场景削减(Matlab代码实现)内容概要:本文介绍了基于拉丁超立方抽样(Latin Hypercube Sampling, LHS)方法的场景削减技术,并提供了相应的Matlab代码实现。该方法主要用于处理不确定性问题,特别是在电力系统、可再生能源等领域中,通过对大量可能场景进行高效抽样并削减冗余场景,从而降低计算复杂度,提高优化调度等分析工作的效率。文中强调了拉丁超立方抽样在保持样本代表性的同时提升抽样精度的优势,并结合实际科研背景阐述了其应用场景与价值。此外,文档还附带多个相关科研方向的Matlab仿真案例和资源下载链接,涵盖风电、光伏、电动汽车、微电网优化等多个领域,突出其实用性和可复现性。; 适合人群:具备一定Matlab编程基础,从事电力系统、可再生能源、优化调度等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于含高比例可再生能源的电力系统不确定性建模;②用于风电、光伏出力等随机变量的场景生成与削减;③支撑优化调度、风险评估、低碳运行等研究中的数据预处理环节;④帮助科研人员快速实现LHS抽样与场景削减算法,提升仿真效率与模型准确性。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,理解拉丁超立方抽样的原理与实现步骤,并参考附带的其他科研案例拓展应用思路;同时注意区分场景生成与场景削减两个阶段,确保在实际项目中正确应用该方法。
道路坑洞目标检测数据集 一、基础信息 • 数据集名称:道路坑洞目标检测数据集 • 图片数量: 训练集:708张图片 验证集:158张图片 总计:866张图片 • 训练集:708张图片 • 验证集:158张图片 • 总计:866张图片 • 分别: CirEllPothole CrackPothole IrrPothole • CirEllPothole • CrackPothole • IrrPothole • 标注格式:YOLO格式,包含边界框和别标签,适用于目标检测任务。 • 数据格式:图片为常见格式(如JPEG/PNG),来源于相关数据采集。 二、适用场景 • 智能交通监控系统开发:用于自动检测道路坑洞,实现实时预警和维护响应,提升道路安全。 • 自动驾驶与辅助驾驶系统:帮助车辆识别道路缺陷,避免潜在事故,增强行驶稳定性。 • 城市基础设施管理:用于道路状况评估和定期检查,优化维护资源分配和规划。 • 学术研究与创:支持计算机视觉在公共安全和交通领域的应用,推动算法优化和模型开发。 三、数据集优势 • 精准标注与别覆盖:标注高质量,包含三种常见坑洞型(CirEllPothole、CrackPothole、IrrPothole),覆盖不同形态道路缺陷。 • 数据多样性:数据集涵盖多种场景,提升模型在复杂环境下的泛化能力和鲁棒性。 • 任务适配性强:标注兼容主流深度学习框架(如YOLO),可直接用于目标检测任务,支持快速模型迭代。 • 实际应用价值:专注于道路安全与维护,为智能交通和城市管理提供可靠数据支撑,促进效率提升。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值