2016-04-10 26 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上阅读的代码!你做了这么好的综合!谢谢! –