2016-04-10 138 views
0

以下是Genie中ToolbarButton的工作代碼。目標是獲取所選文件的uri並將其返回到該類的構造/初始化。問題是,在我遇到的所有示例中,使用了全局_變量(如下面的代碼所示)。它看起來不直觀,我擔心,只要代碼變大,刪除錯誤就會變得更加困難,因爲這些變量會開始累積。有沒有其他的方法讓openfile函數將uri返回到類的構造/ init中的常規變量?在Genie中避免全局變量

下面是代碼:

uses 
    Granite.Widgets 
    Gtk 

init 
    Gtk.init (ref args) 

    var app = new Application() 
    app.show_all() 
    Gtk.main() 

// This class holds all the elements from the GUI 
class Application : Gtk.Window 

    _view:Gtk.TextView 
    _uri:string 

    construct() 

     // Prepare Gtk.Window: 
     this.window_position = Gtk.WindowPosition.CENTER 
     this.destroy.connect (Gtk.main_quit) 
     this.set_default_size (400, 400) 


     // Headerbar definition 
     headerbar:Gtk.HeaderBar = new Gtk.HeaderBar() 
     headerbar.show_close_button = true 
     headerbar.set_title("My text editor") 

     // Headerbar buttons 
     open_button:Gtk.ToolButton = new ToolButton.from_stock(Stock.OPEN) 
     open_button.clicked.connect (openfile) 

     // Add everything to the toolbar 
     headerbar.pack_start (open_button) 
     show_all() 
     this.set_titlebar(headerbar) 

     // Box: 
     box:Gtk.Box = new Gtk.Box (Gtk.Orientation.VERTICAL, 1) 
     this.add (box) 

     // A ScrolledWindow: 
     scrolled:Gtk.ScrolledWindow = new Gtk.ScrolledWindow (null, null) 
     box.pack_start (scrolled, true, true, 0) 

     // The TextView: 
     _view = new Gtk.TextView() 
     _view.set_wrap_mode (Gtk.WrapMode.WORD) 
     _view.buffer.text = "Lorem Ipsum" 
     scrolled.add (_view) 

    def openfile (self:ToolButton) 

     var dialog = new FileChooserDialog ("Open file", 
             this, 
             FileChooserAction.OPEN, 
             Stock.OK,  ResponseType.ACCEPT, 
             Stock.CANCEL, ResponseType.CANCEL) 
     //filter.add_pixbuf_formats() 
     //dialog.add_filter (filter) 

     case dialog.run() 
      when ResponseType.ACCEPT 
       var filename = dialog.get_filename() 
       //image.set_from_file(filename) 

     if (dialog.run() == Gtk.ResponseType.ACCEPT) 
      _uri = dialog.get_uri() 
      stdout.printf ("Selection:\n %s", _uri) 

     dialog.destroy() 

或者我不應該擔心在所有關於_variables積累?

回答

3

首先對術語進行說明,然後進行概括。

「程序中的任何地方都可以訪問」全局變量「,所以它的範圍是全局的。你所指的_variables是你的對象範圍內的私有字段。它們只能由該對象中定義的代碼訪問。然而,你應該關心私人工作變量在你的對象中的積累。

設計對象是很難做到的,技術和想法已經發展了幾十年的實踐和研究。 Michael Feathers介紹的SOLID首字母縮略詞總結了面向對象設計的五個原則,爲評估您的設計提供了有用的標準。 Gamma等人的書,Design Patterns: Elements of Reusable Object-Oriented Software。並於1994年首次發佈,爲面向對象編程中的設計提供了一個很好的總結和分類。這本書使用文檔編輯器作爲案例研究來演示如何使用這些模式。本書中的SOLID原則和設計模式都是抽象概念,它們不會告訴你如何編寫程序,但它們確實提供了一系列允許程序員討論和評估的常見思想。所以我會在我的答案中使用這兩種工具,但請注意,近年來已經開發了其他技術來進一步增強軟件開發過程,特別是test driven developmentbehaviour driven development

SOLID中的S代表Single Responsibility Principle,這是查看您的示例的一個很好的起點。通過調用您的對象Application,並將私有工作變量視爲全局變量,則表明您正在將整個應用程序寫入單個對象中。你可以做的是開始將Application分成幾個不同的對象,這些對象更多地集中在單個責任區域。首先雖然我想我會重命名Application對象。我去了EditorWindow。在我的示例EditorWindow下面還有一個Header和一個DocumentView

編譯代碼下面:

valac -X -DGETTEXT_PACKAGE --pkg gtk+-3.0 text_editor_example.gs

採用-X -DGETTEXT_PACKAGE在此答案的端部說明。

[indent=4] 
uses 
    Gtk 

init 
    Intl.setlocale() 
    Gtk.init(ref args) 

    var document = new Text("Lorem Ipsum") 

    var header = new Header("My text editor") 
    var body = new DocumentView(document) 
    var editor = new EditorWindow(header, body) 

    var document_selector = new DocumentFileSelector(editor) 
    var load_new_content_command = new Load(document, document_selector) 
    header.add_item(new OpenButton(load_new_content_command)) 

    editor.show_all() 
    Gtk.main() 

class EditorWindow:Window 
    construct(header:Header, body:DocumentView) 
     this.window_position = WindowPosition.CENTER 
     this.set_default_size(400, 400) 
     this.destroy.connect(Gtk.main_quit) 

     this.set_titlebar(header) 

     var box = new Box(Gtk.Orientation.VERTICAL, 1) 
     box.pack_start(body, true, true, 0) 
     this.add(box) 

class Header:HeaderBar 
    construct(title:string = "") 
     this.show_close_button = true 
     this.set_title(title) 

    def add_item(item:Widget) 
     this.pack_start(item) 

class OpenButton:ToolButton 
    construct(command:Command) 
     this.icon_widget = new Image.from_icon_name(
               "document-open", 
               IconSize.SMALL_TOOLBAR 
               ) 
     this.clicked.connect(command.execute) 

class DocumentView:ScrolledWindow 
    construct(document:TextBuffer) 
     var view = new TextView.with_buffer(document) 
     view.set_wrap_mode(Gtk.WrapMode.WORD) 
     this.add(view) 

interface Command:Object 
    def abstract execute() 

interface DocumentSelector:Object 
    def abstract select():bool 
    def abstract get_document():string 

class Text:TextBuffer 
    construct (initial:string = "") 
     this.text = initial 

class DocumentFileSelector:Object implements DocumentSelector 

    _parent:Window 
    _uri:string = "" 

    construct(parent:Window) 
     _parent = parent 

    def select():bool 
     var dialog = new FileChooserDialog("Open file", 
              _parent, 
              FileChooserAction.OPEN, 
              dgettext("gtk30", "_OK"), 
              ResponseType.ACCEPT, 
              dgettext("gtk30", "_Cancel"), 
              ResponseType.CANCEL 
              ) 

     selected:bool = false 
     var response = dialog.run() 
     case response 
      when ResponseType.ACCEPT 
       _uri = dialog.get_uri() 
       selected = true 

     dialog.destroy() 
     return selected 

    def get_document():string 
     return "Reading the text from a URI is not implemented\n%s".printf(_uri) 

class Load:Object implements Command 

    _receiver:TextBuffer 
    _document_selector:DocumentSelector 

    construct(receiver:TextBuffer, document_selector:DocumentSelector) 
     _receiver = receiver 
     _document_selector = document_selector 

    def execute() 
     if _document_selector.select() 
      _receiver.text = _document_selector.get_document() 

圖形用戶界面的常見高級模式是model-view-controller (MVC)。這是關於解耦您的對象,以便它們可以輕鬆地重新使用和更改。在該示例中,document已成爲表示模型的對象。通過將它作爲一個獨立的對象,它允許給出相同數據的多個視圖。例如,當編寫一個StackOverflow問題時,你有一個編輯器窗口,但也是一個預覽。兩者對同一數據都有不同的看法。

在該示例中,使用command pattern將標題工具​​欄進一步分爲不同的對象。工具欄中的每個按鈕都有一個關聯的命令。通過將命令作爲單獨的對象,命令可以被重新使用。例如,鍵綁定Ctrl-O也可以使用Load命令。這樣,連接到打開的文檔按鈕的命令的代碼不需要被重寫,以將其附加到Ctrl-O。

命令模式使用了一個接口。只要對象實現​​方法,那麼它就可以用作命令。 Load命令還利用該對象的接口來詢問用戶打開哪個URI。 Gtk +還提供FileChooserNative。因此,如果您想切換到使用FileChooserNative對話框而不是FileChooserDialog,則只需編寫一個實現DocumentSelector接口的新對象,並將其傳遞給Load命令。通過以這種方式分離對象,它使您的程序更加靈活,並且專用字段的使用僅限於每個對象。

作爲一個側面說明,編譯您的示例時出現了一些警告:warning: Gtk.Stock has been deprecated since 3.10。在這個答案的例子使用較新的方式:「‘

  • 爲打開的文檔圖標the GNOME developer documentation for Stock Items狀態’使用命名圖標‘文件打開’或標籤」 _open所以我用document-open。這些名稱來自freedesktop.org Icon Naming Specification
  • 對於文件選擇器對話框中的確定按鈕GNOME Developer documentation指出「不要使用圖標,使用標籤」_OK「。之前的下劃線表示它已被國際化並由gettext翻譯。 gettext使用'域'作爲翻譯文件。對於GTK + 3,域名爲gtk30。要在編譯程序時啓用gettext,需要將缺省域的宏傳遞給C編譯器。這就是爲什麼需要-X -DGETTEXT_PACKAGE。還需要在Genie程序Intl.setlocale()中將區域設置設置爲運行時環境。當使用類似LC_ALL="zh_CN" ./text_editor_example這樣的程序來運行你的程序時,如果你已經安裝了該語言環境,將顯示中文確定按鈕
+0

@AIThomas這讓我更加清楚我在Github上閱讀的代碼!你做了這麼好的綜合!謝謝! –