原创Odoo开发文档学习之:构建接口扩展(BuildingInterfaceExtensions)(边Google翻译边学习)A·DONG

本指南是关于为Odoo的web客户创建模块。

警告:该指南需要以下知识:Javascript、jQuery、Underscore.js同时也需要安装Odoo和Git。一个简单的模型让我们从一个简单的Odoo模块开始,它包含基本的web组件配置,并让我们测试web框架。示例模块可以在线下载,可以使用以下命令下载:

如果您浏览petstore文件夹,您应该看到以下内容:

在Odoo模块的“web”端中使用的文件必须放置在静态文件夹中,这样它们就可以在web浏览器中使用,而浏览器之外的文件也不能被浏览器获取。src/css、src/js和src/xml子文件夹是常规的,并不是绝对必要的。

oepetstore/static/css/petstore.css目前为空,将为宠物店(petstore)内容保留CSS。

oepetstore/static/xml/petstore.xml大部分也是空的,将保存QWeb模板。

oepetstore/static/js/petstore.js最重要(也是最有趣的)部分,包含javascript应用程序的逻辑(或者至少是它的web浏览器端)。它现在应该是:

openerp.oepetstore=function(instance,local){//特别注意:红色部分在开发文档中10.0版本中用odoo关键字,但是测试时无法通过,必须是openerp,估计是尚未完全支持odoo关键字var_t=instance.web._t,_lt=instance.web._lt;varQWeb=instance.web.qweb;local.HomePage=instance.Widget.extend({start:function(){console.log("petstorehomepageloaded");},});instance.web.client_actions.add('petstore.homepage','instance.oepetstore.HomePage');}它只在浏览器的控制台打印一个小消息。

静态文件夹中的文件,需要在模块中定义,以便正确加载它们。src/xml中的所有内容都在__manifest.__中定义。在petstore.xml或类似的文件中定义或引用src/css和src/js的内容。

当Web客户端加载你的模块时,它会调用根函数并提供两个参数:

第一个参数(instance)是OdooWeb客户端的当前实例,它允许访问由Odoo(网络服务)定义的各种功能以及由内核或其他模块定义的对象。

第二个参数(local)是您自己的本地名称空间,由Web客户端自动创建。应该可以从模块外部访问的对象和变量(无论是因为OdooWeb客户端需要调用它们,还是因为其他人可能想要定制它们)应该在该名称空间内设置。

就像模块一样,并且与大多数面向对象的语言相反,JavaScript不会构建在classes中,尽管它提供了大致相同(如果是较低级别和更详细的)机制。

为了简单和开发人员友好,Odooweb提供了一个基于JohnResig的简单JavaScript继承的类系统。

通过调用odoo.web.Class()的extend()方法来定义新的类:

varMyClass=instance.web.Class.extend({say_hello:function(){console.log("hello");},});extend()方法需要一个描述新类的内容(方法和静态属性)的字典。在这种情况下,它只会有一个不带参数的say_hello方法。

类使用new运算符实例化:

varmy_object=newMyClass();my_object.say_hello();//print"hello"intheconsole实例的属性可以通过以下方式this访问:

varMyClass=instance.web.Class.extend({say_hello:function(){console.log("hello",this.name);},});varmy_object=newMyClass();my_object.name="Bob";my_object.say_hello();//print"helloBob"intheconsole通过定义init()方法,类可以提供初始化程序来执行实例的初始设置。初始化程序接收使用新运算符时传递的参数:

varMyClass=instance.web.Class.extend({init:function(name){this.name=name;},say_hello:function(){console.log("hello",this.name);},});varmy_object=newMyClass("Bob");my_object.say_hello();//print"helloBob"intheconsole也可以通过在父类上调用extend()来创建现有(使用定义的)类的子类,如同子类Class()所做的那样:

varMySpanishClass=MyClass.extend({say_hello:function(){console.log("hola",this.name);},});varmy_object=newMySpanishClass("Bob");my_object.say_hello();//print"holaBob"intheconsole当使用继承覆盖方法时,可以使用this._super()调用原始方法:

varMySpanishClass=MyClass.extend({say_hello:function(){//已覆盖的方法this._super();//调用父类中的原始方法,即“hello。。。”console.log("translationinSpanish:hola",this.name);},});varmy_object=newMySpanishClass("Bob");my_object.say_hello();//print"helloBob\ntranslationinSpanish:holaBob"intheconsole警告_super不是一个标准的方法,它被设置为当前继承链中的一个方法(如果有的话)。它只在方法调用的同步部分中定义,用于异步处理程序(在网络调用或setTimeout回调之后)应该保留对其值的引用,因此不应通过以下方式访问它:

//以下调用会产生错误say_hello:function(){setTimeout(function(){this._super();}.bind(this),0);}//以下方式正确say_hello:function(){//不能忘记.bind()var_super=this._super.bind(this);setTimeout(function(){_super();}.bind(this),0);}Widgets基础Odooweb客户端捆绑了jQuery以实现简单的DOM操作。它比标准的W3CDOM2更有用,并且提供了更好的API,但不足以构成复杂的应用程序,导致难以维护。很像面向对象的桌面UI工具包(例如Qt,Cocoa或GTK),OdooWeb使特定组件负责页面的各个部分。在Odoo网站中,这些组件的基础是Widget()类,它是专门处理页面部分并显示用户信息的组件。

初始演示模块已经提供了一个基本的widget:

local.HomePage=instance.Widget.extend({start:function(){console.log("petstorehomepageloaded");},});它扩展了Widget()并重载了标准方法start(),它与之前的MyClass很像,现在做的很少。

该行在文件末尾:

instance.web.client_actions.add('petstore.homepage','instance.oepetstore.HomePage');将我们的widget注册为客户端操作。客户端操作将在稍后解释,现在这只是当我们选择PetStorePetStoreHomePage菜单时,可以调用和显示我们的窗口小部件。

警告

由于该组件将从我们的模块外部调用,Web客户端需要其“完全限定(规范)”名称,而不是任意名称。

local.HomePage=instance.Widget.extend({start:function(){this.$el.append("

HellodearOdoouser!
");},});当您打开PetStorePetStoreHomePage时,此消息将显示。

注意

要刷新OdooWeb中加载的JavaScript代码,您需要重新加载页面(升级一下模块)。没有必要重新启动Odoo服务器。

HomePageWidget由OdooWeb使用并自动管理。要学习如何从头开始使用Widget,我们来创建一个新Widget:

local.GreetingsWidget=instance.Widget.extend({start:function(){this.$el.append("

Wearesohappytoseeyouagaininthismenu!
");},});现在我们可以使用GreetingsWidget的appendTo()方法将我们的GreetingsWidget添加到主页:

local.HomePage=instance.Widget.extend({start:function(){this.$el.append("

HellodearOdoouser!
");vargreeting=newlocal.GreetingsWidget(this);returngreeting.appendTo(this.$el);},});HomePage首先将其自己的内容添加到其DOM根目录;

HomePage然后实例化GreetingsWidget;

最后,它告诉GreetingsWidget将自己的部分插入到GreetingsWidget中。

当调用appendTo()方法时,它会要求小部件(widget,以下将的小部件就是widget)将自身插入指定位置并显示其内容。在调用appendTo()期间,将调用start()方法。

要查看显示界面下发生了什么,我们将使用浏览器的DOMExplorer。但首先让我们稍微修改我们的小部件,以便通过向它们的根元素添加一个类来更轻松地找到它们的位置:

HellodearOdoouser!
Wearesohappytoseeyouagaininthismenu!
它清楚地显示了由Widget()自动创建的两个
元素,因为我们在它们上面添加了一些类。

我们也可以看到我们自己添加的两个消息控制器。

最后,注意GreetingsWidget实例的元素位于代表HomePage实例的中,这是因为我们追加了该元素。

在上一部分中,我们使用以下语法实例化了一个小部件:

newlocal.GreetingsWidget(this);//括号内对象是指greetingswidget实例化后归谁所有。第一个参数是this,在这种情况下是一个HomePage实例。这告诉小部件被创建,其他小部件是其父项。

正如我们所看到的,小部件通常由另一个小部件插入到DOM中,并在其他小部件的根元素内插入。这意味着大多数小部件是另一个小部件的“部分”,并代表它存在。我们将容器称为父项,并将包含的小部件称为子项。

由于技术和概念上的多重原因,小部件有必要知道谁是其父类以及谁是子类。

getParent()可以用来获取小部件的父级:

local.GreetingsWidget=instance.Widget.extend({start:function(){console.log(this.getParent().$el);//willprint"div.oe_petstore_homepage"intheconsole},});getChildren()可以用来获取其子女的名单:

local.HomePage=instance.Widget.extend({start:function(){vargreeting=newlocal.GreetingsWidget(this);greeting.appendTo(this.$el);console.log(this.getChildren()[0].$el);//willprint"div.oe_petstore_greetings"intheconsole},});当重写小部件的init()方法时,将父项传递给this._super()调用是非常重要的,否则关系将无法正确设置:

local.GreetingsWidget=instance.Widget.extend({init:function(parent,name){this._super(parent);this.name=name;},});最后,如果小部件没有父项(例如,因为它是应用程序的根小部件),则可以将null作为父项提供:

newlocal.GreetingsWidget(null);销毁Widget如果您可以向用户显示内容,则应该也可以将其删除。这是通过destroy()方法完成的:

greeting.destroy();当一个小部件被销毁时,它将首先对其所有子项调用destroy()。然后它从DOM中删除自己。如果你已经在init()或start()中设置了永久结构,必须明确清除它们(因为垃圾回收器不会处理它们),你可以重写destroy()。

危险

当覆盖destroy()时,必须始终调用_super(),否则即使没有显示错误,小部件及其子项也没有正确清理,从而可能会发生内存泄漏和“意想不到的事件”。

在上一节中,我们通过直接操作(并添加)DOM来将内容添加到我们的小部件:

this.$el.append("

HellodearOdoouser!
");这允许生成和显示任何类型的内容,但在生成大量DOM时会很难处理(大量重复,引用问题......)。

与许多其他环境一样,Odoo的解决方案是使用模板引擎。Odoo的模板引擎被称为QWeb。

QWeb是一种基于XML的模板语言,与Genshi,Thymeleaf或Facelets类似。它具有以下特点:

使用QWeb代替现有的JavaScript模板引擎的原理是预先存在的(第三方)模板的可扩展性,就像Odoo视图一样。

大多数JavaScript模板引擎是基于文本的,这排除了容易的结构可扩展性,其中基于XML的模板引擎可以通过使用例如通用数据库XPath或CSS以及树型变更DSL(甚至只是XSLT)。这种灵活性和可扩展性是Odoo的核心特征,丢失它被认为是不可接受的。

首先让我们在几乎空白的地方定义一个简单的QWeb模板,在以下文件进行操作:

oepetstore/static/src/xml/petstore.xml

ThisissomesimpleHTML

现在我们可以在HomePage小部件中使用这个模板。使用页面顶部定义的QWeb加载器变量,我们可以调用XML文件中定义的模板:local.HomePage=instance.Widget.extend({start:function(){this.$el.append(QWeb.render("HomePageTemplate"));},});QWeb.render()查找指定的模板,将其呈现为一个字符串并返回结果。

但是,因为Widget()对QWeb有特殊的集成,所以模板可以通过它的模板属性直接设置在Widget上:

local.HomePage=instance.Widget.extend({template:"HomePageTemplate",start:function(){...},});尽管结果看起来相似,但这些用法之间有两点区别:

QWeb模板可以被赋予数据并且可以包含基本的显示逻辑。

对于显式调用QWeb.render(),模板数据作为第二个参数传递:

QWeb.render("HomePageTemplate",{name:"Klaus"});将模板修改为:

Hello
最终结果为:

HelloKlaus
当使用Widget()的集成时,不可能为模板提供额外的数据。该模板将被赋予一个单一的窗口小部件上下文变量,引用在start()被调用之前被渲染的窗口小部件(窗口小部件的状态基本上是由init()设置的):

Hello
local.HomePage=instance.Widget.extend({template:"HomePageTemplate",init:function(parent){this._super(parent);this.name="Mordecai";},start:function(){},});结果为:

t-esc指令可用于输出文本:

Hello
它需要一个经过评估的Javascript表达式,然后表达式的结果被HTML转义并插入到文档中。由于它是一个表达式,因此可以像上面那样仅提供一个变量名称,或者像计算这样的更复杂的表达式:

或方法调用:

输出HTML要在呈现的页面中注入HTML,请使用t-raw。像t-esc一样,它以一个任意的Javascript表达式作为参数,但它不执行HTML转义步骤。

QWeb可以使用t-if的条件块。该指令采用任意表达式,如果表达式为falsy(false,null,0或空字符串),则整个块将被抑制,否则将显示该表达式。

trueistruetrueisnottrue
注意QWeb没有“else”结构,如果原始条件反转,则使用第二个t。如果它是复杂或昂贵的表达式,您可能需要将条件存储在局部变量中。

要在列表上迭代,请使用t-foreach和t-as。t-foreach需要一个表达式返回一个列表来迭代t-因为在迭代过程中需要一个变量名来绑定到每个项目。

Hello
注意t-foreach也可以用于数字和对象(字典)。

t-att-接受一个javascript表达式,其结果被设置为属性的值,如果计算该属性的所有值,则它是非常有用的:

Inputyourname:
t-attf-采用格式字符串。格式字符串是带有插值块的文本文本,插值块是{{和}}之间的javascript表达式,它将被表达式的结果替换。对于部分文字和部分计算的属性(如类),这是最有用的:

insertcontenthere

调用其他模板模板可以拆分成子模板(为了简单,可维护性,可重用性或避免过多的标记嵌套)。

这是通过使用t-call指令完成的,该指令采用要呈现的模板的名称:

渲染A模板将导致:

子模板继承其调用者的渲染上下文。

在Widgets创建一个构件除了parent:product_names和color之外还有两个参数的构件。

odoo.oepetstore=function(instance,local){var_t=instance.web._t,_lt=instance.web._lt;varQWeb=instance.web.qweb;local.HomePage=instance.Widget.extend({start:function(){varproducts=newlocal.ProductsWidget(this,["cpu","mouse","keyboard","graphiccard","screen"],"#00FF00");products.appendTo(this.$el);},});local.ProductsWidget=instance.Widget.extend({template:"ProductsWidget",init:function(parent,products,color){this._super(parent);this.products=products;this.color=color;},});instance.web.client_actions.add('petstore.homepage','instance.oepetstore.HomePage');}

小部件的jQuery选择器

在窗口小部件中选择DOM元素可以通过调用窗口小部件的DOM根目录上的find()方法来执行:

this.$el.find("input.my_input")...但是由于这是一种常见的操作,Widget()通过$()方法提供了一个等效的快捷方式:

local.MyWidget=instance.Widget.extend({start:function(){this.$("input.my_input")...},});警告全局jQuery函数$()应该永远不会被使用(不是this.$()),除非它是绝对必要的:对一个小部件的根进行选择的范围是小部件,对本地来说是本地的,但是使用$()的选择对于页面/应用程序是全局的,并且可以匹配部分其他小部件和视图,导致奇怪或危险的副作用。由于小部件通常只应用于其拥有的DOM部分,因此没有全局选择的原因。

我们以前使用常规jQuery事件处理程序(例如,.click()或.change())在窗口小部件元素上绑定了DOM事件:

local.MyWidget=instance.Widget.extend({start:function(){varself=this;this.$(".my_button").click(function(){self.button_clicked();});},button_clicked:function(){..},});虽然这有效,但它有一些问题:

小部件因此提供了通过事件绑定DOM事件的捷径:

local.MyWidget=instance.Widget.extend({events:{"click.my_button":"button_clicked",},button_clicked:function(){..}});event是事件触发时调用的函数或方法的对象(映射):

关键是一个事件名称,可能使用CSS选择器进行优化,在这种情况下,只有当事件发生在选定的子元素上时,函数或方法才会运行:点击将处理小部件内的所有点击,但单击.my_button将只处理点击含有my_button类的元素。

该值是触发事件时要执行的操作。

它也可以这样描述:

events:{'click':function(e){/*codehere*/}}或对象上方法的名称(请参见上面的示例)。

无论哪种情况,这都是小部件实例,并且处理程序被赋予一个参数,即事件的jQuery事件对象。

小部件提供了一个事件系统(与上面描述的DOM/jQuery事件系统分开):一个小部件可以触发自身的事件,其他小部件(或其本身)可以绑定自己并监听这些事件:

local.ConfirmWidget=instance.Widget.extend({events:{'clickbutton.ok_button':function(){this.trigger('user_chose',true);},'clickbutton.cancel_button':function(){this.trigger('user_chose',false);}},start:function(){this.$el.append("

Areyousureyouwanttoperformthisaction
"+"Ok"+"Cancel");},});trigger()将触发事件的名称作为其第一个(必需)参数,任何其他参数都视为事件数据并直接传递给侦听器。

然后,我们可以设置一个父事件来实例化我们的通用小部件,并使用on()来监听user_chose事件:

start:function(){varwidget=...widget.on("my_event",this,this.my_event_triggered);widget.trigger("my_event",1,2,3);},my_event_triggered:function(a,b,c){console.log(a,b,c);//willprint"123"}提示:

触发其他小部件上的事件通常是一个坏主意。该规则的主要例外是odoo.web.bus,它专门用于广播任何小部件可能对整个OdooWeb应用程序感兴趣的平台。

THE END
1.人类什么时候才能听懂动物的语言?我们发现,低配版宠物翻译器运用的技术原理就是对狗的叫声、动作等生物信号进行采样,对获取的数据进行频谱分析,把得到的翻译语言以中文形式语音播报出来。但是由于采样的范围和机器内存等局限性,这种低配版宠物翻译器在翻译的准确度和丰富性方面尚有待提高。 https://www.jianshu.com/p/2b01075606bb
2.研究生英语系列教程多维教程熟谙全文翻译完整版或在工厂里开机器。这种经历是非常宝贵的。另外,自下而上的成功会增加雇员们和管理部门对他们的尊敬。据我们的经验来看,以 这种 方式成长的孩子们成功的可能性是不劳而获得到总裁职位的孩子们的两倍。 建立和保持家庭的和谐 对抗和嫉妒是相当耗时的,更不用说具有毁灭 ...http://www.360doc.com/document/23/1212/15/82785916_1107296105.shtml
3.排名前十的宠物翻译器推荐导读宠物翻译器是一个神奇的工具,可以帮助我们理解和交流宠物的思想和情感。它可以将我们宠物的叫声或动作转换成可以理解的语言,让我们更好地关心和陪伴宠物,增进我们与宠物之间的感情和信任。市面上的宠物翻译机有很... 宠物翻译器是一个神奇的工具,可以帮助我们理解和交流宠物的思想和情感。它可以将我们宠物的叫声...https://www.9game.cn/news/9628015.html
1.狗语翻译器能把狗狗的叫声翻译成“人话”?主人可别太当真近年来,市场上各类宠物智能设备越来越多,一款名为“狗语翻译器”的产品格外吸引人眼球,号称能把狗狗的叫声翻译成“人话”,甚至在狗狗不发声的时候,也可根据其肢体动作,把其心中所想“翻译”出来。“狗语翻译器”的原理是什么?真的这么神吗?记者对此进行了走访调查。 https://news.lyd.com.cn/system/2016/07/19/010754753.shtml
2.AppStore上的“人狗猫交流器《人狗猫交流器》是专为养宠物人士开发的动物翻译器,是宠物达人的必备软件!让你知道你的爱犬、猫咪在想些什么。 这款软件是集狗狗交流、人狗交流、狗语翻译和猫咪交流、人猫交流、猫语翻译于一体的动物语言交流器!让主人-狗狗-猫咪的沟通更顺畅,关系更亲密! https://itunes.apple.com/cn/app/id1332698015
3.人类想听懂动物的语言,AI还需克服哪些困难自动语音识别技术和语音翻译技术助力,宠物情绪传达不再是镜中花、水中月 低配版宠物翻译器运用的技术原理就是对狗狗的叫声、动作等生物信号进行采样,对获取的数据进行频谱分析,对姿势动作DSP运算数字化处理,把得到的翻译语言以中文形式语音播报出来,但是由于采样的范围和机器内存等的局限性,这种低配宠物翻译器在翻译的准...https://blog.csdn.net/fzhlee/article/details/79155137/
4.狗语翻译机的原理是什么如何读懂狗狗的“语言”→MAIGOO知识3、香港威斯特收集狗声和肢体动作的样品,经动物学家翻译后,储存在狗语数据库里通过复杂的算法及不断对比。 4、通过DSP复杂的算法及不断对比识别宠物的叫声和肢体语言、翻译成人类语言,并以语音形式播放。 狗语翻译器原理说起来很简单就是叫声 肢体动作识别分析,但我们通过复杂的算法,经过反复测试得出的结果。 https://www.maigoo.com/goomai/217107.html
5.sensitization是什么意思sensitization的翻译音标读音...尘螨霉菌混合致敏及尘螨宠物混合致敏在哮喘和变应性鼻炎中最为常见. 互联网 Results The isobutyl chitosan has no cytotoxicity,sensitization, irritation and systemic acute toxicity. 结果:异丁基壳聚糖无细胞毒性 、 无致敏 、 刺激及全身毒性作用. 互联网 ...https://www.iciba.com/sensitization
6.人工智能系列经典图书翻译可解释机器学习(第二版)第9章局部...最好直观地解释一下采样和局部模型训练的工作原理: 图9.5:表格数据的 LIME 算法。A) 给定特征 x1 和 x2 的随机森林预测。预测类别:1(暗)或 0(亮)。B) 感兴趣的实例(大点)和从正态分布中采样的数据(小点)。C) 为感兴趣的实例附近的点分配更高的权重。D)网格的符号显示了从加权样本中本地学习模型的分类...https://blog.51cto.com/jiazhen/10241687
7.万能动物翻译器app万能动物翻译器官方手机版v1.2.0万能动物翻译器是很优质的宠物翻译软件,这款软件功能强大,用户随时可以在线进行语言翻译,直接在线进行语音识别,帮助你更好的了解自己的宠物,感兴趣的朋友可以下载https://www.doyo.cn/app/90914.html
8.猫狗翻译器免费版有哪些猫狗翻译器app哪个好用猫狗翻译器是一款非常好玩的娱乐软件,使用猫狗翻译器,就能让你轻松与你的猫或者狗狗交流哦,有需要的小伙伴快来下载吧!https://www.qqtn.com/qqkey/mgfyq/
9.猫狗翻译器app下载猫狗翻译器(宠物趣味互动软件)v1.6.6安卓版下...猫狗翻译器是一款能够进行狗语和猫语翻译的软件,它能够帮助用户更好地了解宠物猫和宠物狗的心情,通过狗语以及猫语的翻译,可以让用户明白和理解猫猫和狗狗的语言,进而更好地和它们进行沟通,为猫猫和狗狗解决它们遇到的问题或是想办法让它们变得开心。 应用功能 ...https://www.jb51.net/softs/916467.html
10.动物语言翻译器安卓最新版下载动物语言翻译器安卓最新版下载软件给宠物主人们提供了一个用户友好的软件界面,让宠物主人可以轻松地记录和翻译他们宠物的叫声,同时可能还包括训练指南、行为分析和健康监测等附加功能。软件开发者还会收集各种各样不同种类动物的叫声数据,作为训练机器学习模型的基础。 动物语言翻译器软件特色 1. 详细的宠物语言设置,每个...https://app.3dmgame.com/android/402811.html