亲手翻译,欢迎转载。动态修订,请附原址:http://my.oschina.net/u/2306127/admin/edit-blog?blog=595479html
原文(英)来自于:http://orange-development.readthedocs.org/tutorial.html
python
关于Orange Widgets的开发完整教程参见:http://orange-development.readthedocs.org/shell
注意:原文有一些错误,经做者试验后此文已经修正,并附上一些经验和运行结果图。canvas
Orange Canvas是Orange的可视化程序环境,而Widgets 是Orange Canvas中运行的组件。Canvas提供了Widgets自包含的功能性函数,而且提供了一个图形用户界面,能够经过拖拽来快速构建数据处理流程和数据分析的工做流。Widgets互相之间能够通信、能够传递对象,经过一个通信Channel来实现。框架
这里,咱们将介绍一个简单的例子,而且展现如何构建一个简单的Widgets的方法,而后让它在Canvas中运行起来。svg
本文的例程完整运行的状况(译者注:此处为做者试验的结果,将输入和加法操做执行了两遍):函数
每个Orange widget属于category而且有一个在category中的优先级。当打开Orange Canvas,在Orange的可视化设计环境,widgets就在左边的toolbox列出来:ui
每个widget名字描述和input/outputs集合,这是widgets的元数据。this
该元数据在Orange Canvas应用启动时自动发现和加载。经过setuptools发布的 entry points 协议在python中加载。Orange Canvas使用orange.widgets入口点
来寻找widgets并加载。spa
[译者注:默认安装环境下,第三方的widgets通常位于Orange/orange3env/lib/python3.4/site-packages/orangecontrib/下,而加载该插件的元数据在其上级目录中*.egg_info的目录下的entry_points.txt文件中,该目录的其它文件与python的安装文件彻底相同。Canvas启动时首先寻找*.egg_info文件,而后依据entry_points.txt去加载插件代码。]
典型的entry_points.txt文件内容以下:
[orange.widgets] MyWorker = orangecontrib.myworker.widgets [orange.widgets.tutorials] exampletutorials = orangecontrib.myworker.tutorials [orange3.addon] myworker = orangecontrib.myworker
OWWidget
是能够被Orange Canvas工做流加载的widget的基类。
在Canvas框架中的每个 widget 都须要定义其元数据,包括widget名称和文本描述以及更重要的input/output指定。经过在widget的class namespace定义常量来提供(译者注:这个方式将定义与代码写在一个文件中,更方便使用,与传统的将其写在一个定义文件中有所不一样)。下面开始一个简单的例子,widget 输出一个简单的用户指定的integer。
#译者注:此处有修改,原文的运行不过。 #图标能够从其它目录拷贝,放在./icons/下便可。 from PyQt4 import QtGui from PyQt4 import QtCore from PyQt4.QtGui import * from PyQt4.QtCore import * from Orange.widgets import gui from Orange.widgets.utils import vartype from Orange.widgets.widget import OWWidget from Orange.widgets.settings import Setting, ContextSetting class IntNumber(OWWidget): # Widget's name as displayed in the canvas name = "Integer Number" # Short widget description description = "Lets the user input a number" # An icon resource file path for this widget # (a path relative to the module where this widget is defined) icon = "icons/number.svg" # A list of output definitions (here on output named "Number" # of type int) outputs = [("Number", int)]
按照设计原则,Orange widgets一般有一个界面,由控制和主体区域组成。控制区通常出如今左边,包含各类widgets使用的控制和选项。主体区域则常常包含图形、表格等各类依据选项和操做绘制出来的内容。OWWidget
经过 self.controlArea
和 self.mainArea使其可用。须要注意到的是,这样作可让全部的
widgets有一个统一的好的外观,但实际上能够将其用于任何的用途,即使与Orange的其它widgets彻底不一样。
咱们经过class的属性置顶缺省的外观。下面的代码指定一个单列的只有控制区的GUI外观。
# Basic (convenience) GUI definition: # a simple 'single column' GUI layout want_main_area = False # with a fixed non resizable geometry. resizing_enabled = False
咱们但愿用户输入的数字可以在工做流保存和载入时自动保存和恢复。咱们经过声明一个特殊的property/member 在widget的类定义中完成,以下:
number = Setting(42)
而后,定义GUI和相应的功能:
def __init__(self): super().__init__() gui.lineEdit(self.controlArea, self, "number", "Enter a number", orientation="horizontal", box="Number", callback=self.number_changed, valueType=int, validator=QIntValidator()) self.number_changed()def number_changed(self): # Send the entered number on "Number" output self.send("Number", self.number)
参考:Orange.widgets.gui.lineEdit()
,Orange.widgets.widget.OWWidget.send()
这个widget看起来有点无趣(%%%)。
再加入一些东西,如何显示这个数字呢?再加入一个新的Widgets:
from Orange.widgets import widget, gui class Print(widget.OWWidget): name = "Print" description = "Print out a number" icon = "icons/print.svg" inputs = [("Number", int, "set_number")] outputs = [] want_main_area = False def __init__(self): super().__init__() self.number = None self.label = gui.widgetLabel(self.controlArea, "The number is: ??") def set_number(self, number): """Set the input number.""" self.number = number if self.number is None: self.label.setText("The number is: ??") else: self.label.setText("The number is {}".format(self.number))
注意,set_number方法如何被检查,是否number是None。当两个链接的Widgets被移除或者内部被清空等改变时,None被发送给widget。
如今咱们使用一个widget输入而另一个来显示输入的结果。
如今,咱们要加入一个加法操做的Widgets:
from Orange.widgets import widget class Adder(widget.OWWidget): name = "Add two integers" description = "Add two numbers" icon = "icons/add.svg" inputs = [("A", int, "set_A"), ("B", int, "set_B")] outputs = [("A + B", int)] want_main_area = False def __init__(self): super().__init__() self.a = None self.b = None def set_A(self, a): """Set input 'A'.""" self.a = a def set_B(self, b): """Set input 'B'.""" self.b = b def handleNewSignals(self): """Reimplemeted from OWWidget.""" if self.a is not None and self.b is not None: self.send("A + B", self.a + self.b) else: # Clear the channel by sending `None` self.send("A + B", None)
参考:handleNewSignals()
嗯,到如今为止,添加几个widgets,而后相互组合、传递数据、在数据改变时进行通知等操做,所有均可以搞定了。
运行以前,须要到Orange/orange3env/lib/python3.4/site-packages/目录下,将Orange3_spark-0.2.2.dist-info拷贝一份,更名为Orange3_First-0.1.0.dist-info,而后进去将entry_points.txt改成一下内容:
[orange.addons] First = orangecontrib.first [orange.widgets] First = orangecontrib.first.widgets.owfirst Show = orangecontrib.first.widgets.owshow Add = orangecontrib.first.widgets.owadd [orange.widgets.tutorials] exampletutorials = orangecontrib.first.tutorials
而后,Orange重启,便可看到Widgets这个目录,将里面的内容拖放到右侧的窗口,输入数据、查看结果。