親手翻譯,歡迎轉載。動態修訂,請附原址: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這個目錄,將裏面的內容拖放到右側的窗口,輸入數據、查看結果。