Orange的擴展插件Widgets開發(一)-快速入門

親手翻譯,歡迎轉載。動態修訂,請附原址: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

_images/widgettoolbox.png

每個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

定義一個輸入widget

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.controlAreaself.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看起來有點無趣(%%%)。

定義一個輸出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輸入而另一個來顯示輸入的結果。

定義一個加法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這個目錄,將裏面的內容拖放到右側的窗口,輸入數據、查看結果。

相關文章
相關標籤/搜索