免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2666 | 回复: 0
打印 上一主题 下一主题

system-config-kickstart源码阅读(二)主界面篇 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-04 14:15 |只看该作者 |倒序浏览

                承接上篇内容,在system-config-kickstat的最后我们知道其调用了kickstartGui.py程序中的kickstartGui类,那么此篇就是详细讲解kickstartGui.py这个程序所实现的一切内容。
先看一张实现kickstartGui的UML输出图片:

一目了然,kickstartGui仅实现了一个类,再看其流程,和前面的文章所提到的有些不同,根据其import的模块,我将发现语言的hardwareList.py以及partition从原来的打功能中抽取了出来,这是不读代码所无法顾及的细节问题:

下面我们开始分析kickstartGui.py的主程序:
import gtk
import gtk.glade
import gobject
import signal
import basic
import bootloader
import install
import partition
import network
import auth
import firewall
import savefile
import savedialog
import xconfig
import packages
import scripts
import os
from pykickstart.parser import *
from pykickstart.version import makeVersion
try:
    from gtk import _disable_gdk_threading
    _disable_gdk_threading()
except ImportError:
    pass
##
## I18N
##
import gettext
gtk.glade.bindtextdomain("system-config-kickstart")
_ = lambda x: gettext.ldgettext("system-config-kickstart", x)
上述关于导入模块的就不具体解释了,相信看到流程图就明白。至于gtk编程,这是另外要设计的,仅下面类及其方法的实现作一细品。
##
## Icon for windows
##
iconPixbuf = None
try:
    iconPixbuf = gtk.gdk.pixbuf_new_from_file("/usr/share/system-config-kickstart/pixmaps/system-config-kickstart.png")
//gtk的典型图片载入,可参考pygtktut的9.6节获得更多内容。
except:
    pass
##
## Pull in the Glade file
##
if os.access("system-config-kickstart.glade", os.F_OK):
    xml = gtk.glade.XML ("system-config-kickstart.glade", domain="system-config-kickstart")
else:
    xml = gtk.glade.XML ("/usr/share/system-config-kickstart/system-config-kickstart.glade", domain="system-config-kickstart")
##libglade是个好东西,虽然不是完整的MVC模式,至少让界面的设计从代码中分离出来是个不错的主意,关于
pygtk和glade相结合编程的东西,后续我会有陆续的文章,也许我可以写多一点的实例。
class kickstartGui: //定义kickstartGui类
    def destroy(self, args):    //定义destory方法,此方法从gtk的事件中获得参数。
        gtk.main_quit()    //This function tells GTK+ that it is to exit from gtk.main() when control is returned to it.
//此方法是告诉GTk+,需要从gtk.main()主方法中退出。
        self.packages_class.cleanup()   //见下面的packages_class的定义
    def __init__ (self, file):   //入口函数
        self.ksHandler = makeVersion()   
       ///Return a new instance of the syntax handler for version.  version can be
       either a string or the matching constant.  This function is useful for
       standalone programs which just need to handle a specific version of
       kickstart syntax (as provided by a command line argument, for example)
       and need to instantiate the correct object.//
        if file:   //如果用户输入了xx.ks的参数,那么做如下的判断。
            self.parser = KickstartParser(self.ksHandler)
  //The kickstart file parser class as represented by a basic state       machine.  To create a specialized parser, make a subclass and override       any of the methods you care about.  Methods that don't need to do       anything may just pass.  However, _stateMachine should never be       overridden
            msg = None
            try:
                self.parser.readKickstart(file)  //看来解释pykickstart库成为了必要的了,
            except KickstartError, e:
                msg = _("The following error was found while parsing your "
                        "kickstart configuration:\n\n%s" % e)
            except IOError, e:
                msg = _("The kickstart file %s could not be opened.") % file
            if msg:    //呵呵,将msg人性化,弹出个小对话框。
                dlg = gtk.MessageDialog (None, 0, gtk.MESSAGE_ERROR,
                                         gtk.BUTTONS_OK, msg)
  //典型的gtk widget,有心人可以运行pygtk-demo来看下。顺便看看代码,和此处有和不同?
                dlg.set_title(_("Error Parsing Kickstart Config"))
                dlg.set_position(gtk.WIN_POS_CENTER)
                dlg.set_modal(True)
                dlg.run()
                dlg.destroy()
                sys.exit(0)  //错误就退出,绝不干无用功。其实我觉得此处不是很妥,应该运行,友好的提示用户到哪里去找.ks文件。
        self.xml = xml        //引用上述的glade为Gtk可操作的widget.
        name_tag = (_("Kickstart"))   
        comment_tag = (_("Create a kickstart file"))  //字符串变量定义而已
    self.toplevel = xml.get_widget("main_window")   //主窗口
    self.toplevel.connect ("destroy", self.destroy)   //事件定义为destory,然后传递给上面destory方法。
    self.toplevel.set_icon(iconPixbuf)    //哈哈,给单调的窗口设置个小logo图标。
    #bring in widgets from glade file,这句英文已经解释的很清楚了。
    self.options_notebook = xml.get_widget("options_notebook")
    self.install_radiobutton = xml.get_widget("install_radiobutton")
    self.category_clist = xml.get_widget("category_clist")
    self.open_menu = xml.get_widget("open_menu")
    self.preview_menu = xml.get_widget("preview_menu")
    self.save_menu = xml.get_widget("save_menu")
    self.quit_menu = xml.get_widget("quit_menu")
    self.help_menu = xml.get_widget("help_menu")
    self.about_menu = xml.get_widget("about_menu")
    #populate category list    没错!就是左边那个列表。
    self.category_view = xml.get_widget("list_view")
    self.category_store = gtk.ListStore(gobject.TYPE_STRING)
//参见pygtktut中14.2.2. Creating TreeStore and ListStore Objects 一节。
    self.category_view.set_model(self.category_store) //gtk+中很少用到的方法
   
    col = gtk.TreeViewColumn(_("Subsection"), gtk.CellRendererText(), text=0)
    col.set_sort_column_id(0)
    self.category_view.append_column(col)
    //单独使用gtk中的widget。看来gtk+的教程不读都不可以。
    self.category_list = [ (_("Basic Configuration")),
                               (_("Installation Method")),
                   (_("Boot Loader Options")),
                               (_("Partition Information")),
                   (_("Network Configuration")),
                               (_("Authentication")),
                   (_("Firewall Configuration")),
                               (_("Display Configuration")),
                   (_("Package Selection")),
                               (_("Pre-Installation Script")),
                   (_("Post-Installation Script")) ]
//定义列表
        
    for item in self.category_list:
        iter = self.category_store.append()
        self.category_store.set_value(iter, 0, item)
    #bring in basic functions引用basic的方法,后续文章介绍。
    self.basic_class = basic.basic(self, xml, self.options_notebook, self.ksHandler)
        # Now that we've loaded the UI elements for the first active thing in the notebook,
        # draw it so we can display a progress bar when yum starts doing stuff.
        self.toplevel.show()
        while gtk.events_pending():
            gtk.main_iteration()
//bring in bootloader functions.
    self.bootloader_class = bootloader.bootloader(xml, self.options_notebook, self.ksHandler)
//bring in install functions.
    self.install_class = install.install(self, xml, self.category_store,
                         self.category_view,
                                             self.options_notebook,
                         self.ksHandler)
//bring in partition functions
    self.partition_class = partition.partition(xml, self.ksHandler)
//bring in network functions
    self.network_class = network.network(xml, self.ksHandler)
//认证方面的方法
    self.auth_class = auth.auth(xml, self.ksHandler)
//防火墙
    self.firewall_class = firewall.Firewall(xml, self.ksHandler)
//关于X的方法
    self.X_class = xconfig.xconfig(xml, self.ksHandler)
//软件包的方法
    self.packages_class = packages.Packages(xml, self.ksHandler)
//脚本的方法
    self.scripts_class = scripts.scripts(xml, self.ksHandler)
以上一小段内容,就是引入流程图中的”功能篇“,也是mport的一个实现。
    self.open_menu.connect("activate", self.on_activate_open)
    self.preview_menu.connect("activate", self.on_activate_preview_options)
    self.save_menu.connect("activate", self.on_activate_save_options)
    self.quit_menu.connect("activate", gtk.main_quit)
    self.help_menu.connect("activate", self.on_help_button_clicked)
    self.about_menu.connect("activate", self.on_about_activate)
    self.category_view.connect("cursor_changed", self.on_list_view_row_activated)
    self.options_notebook.connect("switch-page", self.on_notebook_changed)
//此段实现从glade定义的事件,调用相应的方法。
    #show gui
        self.applyKickstart() //见下面方法定义
    self.toplevel.show() //显示主窗口
    gtk.main() //初始化gtk+
    def on_notebook_changed(self, page, data, num)://实现随着左边列表的变动,而右边显示不同的内容。
        count = 0
        iter = self.category_store.get_iter_first()  //初始化为第一个basic的界面。
        if num == 0:
            self.category_view.get_selection().select_iter(iter)
        else:
            while iter:
                if count == num:
                    self.category_view.get_selection().select_iter(iter)
                    self.category_view.show_all()
                iter = self.category_store.iter_next(iter)
                count = count + 1
    def on_list_view_row_activated(self, tree_view):   //显示左边列表项的选择。
        data, iter = tree_view.get_selection().get_selected()
    category = self.category_store.get_value(iter, 0)
    row = self.category_list.index(category)
    self.options_notebook.set_current_page(row)
    #about box
    def on_about_activate(self, args):   //没错,这就是那个点击“关于”后弹出来的框框显示的内容
        dlg = gtk.MessageDialog (None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
                 _("Kickstart Configurator @VERSION@\n Copyright (c) 2000-2002 Red Hat, Inc.\n Copyright (c) 2000-2002 Brent Fox \n Copyright (c) 2000-2002 Tammy Fox \n A graphical interface for creating a kickstart file"))
    dlg.set_title(_("About Kickstart Configurator"))
    dlg.set_default_size(100, 100)
    dlg.set_position (gtk.WIN_POS_CENTER)
    dlg.set_border_width(2)
    dlg.set_modal(True)
    dlg.set_transient_for(self.toplevel)
    dlg.set_icon(iconPixbuf)
    rc = dlg.run()
        dlg.destroy()
    #display help manual
    def on_help_button_clicked (self, args):   //帮助,
        help_pages = map (lambda str: "file:///usr/share/doc/system-config-kickstart-" + "@VERSION@" + "/system-config-kickstart-" + str + ".html",
                          ["basic", "bootloader", "install", "partitions",
                          "network", "auth", "firewall", "xconfig",
                          "pkgs", "prescript", "postinstall"])
    page = (help_pages [self.options_notebook.get_current_page ()])
    path = "/usr/bin/htmlview" //这个指令你的系统有吗?那么就有下面的错误了!
    if path == None:
        dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
                    (_("Help is not available.")))
        dlg.set_position(gtk.WIN_POS_CENTER)
        dlg.set_icon(iconPixbuf)
        dlg.run()
        dlg.destroy()
        return
    pid = os.fork()
    if not pid:
        os.execv(path, [path, page])
    # Copy possible UI changes back to the kickstartData object.
    def getAllData(self, *args):  //获得数据,从现在的UI上。具体内容详见后续。
        if self.install_class.formToKickstart() is None:
            return None
        if self.bootloader_class.formToKickstart() is None:
            return None
        doInstall = self.install_radiobutton.get_active()
        if self.basic_class.formToKickstart(doInstall) is None:
            return None
        if self.auth_class.formToKickstart() is None:
            return None
    self.network_class.formToKickstart()
    self.firewall_class.formToKickstart()
        self.X_class.formToKickstart()
        #only do these things in installs, not upgrades
    if doInstall:
            self.partition_class.formToKickstart()
            self.packages_class.formToKickstart()
    self.scripts_class.formToKickstart()
        return 0
    def on_activate_open(self, *args): //当点击“文件”-》“打开”时。
        fs = gtk.FileChooserDialog(action=gtk.FILE_CHOOSER_ACTION_OPEN,
                 buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
              gtk.STOCK_OPEN,gtk.RESPONSE_OK))//文件选择对话框。
        result = fs.run()
        file = fs.get_filename()  //获得文件名(全路径)
        if result == gtk.RESPONSE_OK:    //点击“确定”按钮后的反应。
            if os.access(file, os.R_OK) == 1:
                self.ksHandler = makeVersion() //确认版本信息。
                self.parser = KickstartParser(self.ksHandler)  //预定义好的异常处理。
                msg = None
                try:
                    self.parser.readKickstart(file)
                except KickstartError, e:  
                    msg = _("The following error was found while parsing your "
                            "kickstart configuration:\n\n%s" % e)
                except IOError, e:
                    msg = _("The kickstart file %s could not be opened.") % file
                if msg:
                    dlg = gtk.MessageDialog (None, 0, gtk.MESSAGE_ERROR,
                                             gtk.BUTTONS_OK, msg)
                    dlg.set_title(_("Error Parsing Kickstart Config"))
                    dlg.set_position(gtk.WIN_POS_CENTER)
                    dlg.set_modal(True)
                    dlg.run()
                    dlg.destroy()
                    fs.destroy()
                    return
                  //打开文件有问题,那么就弹出相应的信息,继续。
                # Refresh ksdata pointers in every subclass for the new
                # data we loaded in from the file.
                for cl in [self.basic_class, self.bootloader_class,
                           self.install_class, self.partition_class,
                           self.network_class, self.auth_class, self.X_class,
                           self.firewall_class, self.packages_class,
                           self.scripts_class]:
                    cl.updateKS(self.ksHandler)
//读取ks文件,获得常规数据。
                self.applyKickstart()
            self.toplevel.show()
            else:
                dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
                                        (_("The file \"%s\" cannot be accessed.")) % file)
                dlg.set_position(gtk.WIN_POS_CENTER)
                dlg.set_icon(iconPixbuf)
                dlg.run()
                dlg.destroy()
//当然,也可能发生错误。
        fs.destroy()//销毁文件选择widget.
    #show chosen options for preview//当点击“文件”-》“预览”时
    def on_activate_preview_options (self, *args):
        if self.getAllData() != None: 当kickstartData不为空时,调用savefile方法将设置显示。
            previewDialog = savefile.saveFile (self.ksHandler.__str__(),
                                               self.xml)
    def on_activate_save_options (self, *args):当点击“文件”-》“保存”时
        if self.getAllData() != None:当kickstartData不为空时,调用保存对话框。
            fileDialog = savedialog.saveDialog(self.ksHandler.__str__(),
                                               self.xml)
    def applyKickstart(self):  总调度所有的设置,分散到各模块独立,然后总体处置。
        self.basic_class.applyKickstart()
        self.install_class.applyKickstart()
        self.bootloader_class.applyKickstart()
        self.partition_class.applyKickstart()
        self.auth_class.applyKickstart()
        self.network_class.applyKickstart()
        self.firewall_class.applyKickstart()
        self.X_class.applyKickstart()
        self.packages_class.applyKickstart()
        self.scripts_class.applyKickstart()
下面这两个属于功能篇,分别为定义平台和安装类型。其实本应该出现在这里的。
    def platformTypeChanged(self, platform):
        self.bootloader_class.platformTypeChanged(platform)
    def installTypeChanged(self, boolean):
        self.partition_class.setSensitive(boolean)
        self.auth_class.setSensitive(boolean)
        self.X_class.setSensitive(boolean)
        self.firewall_class.setSensitive(boolean)
        self.bootloader_class.enableUpgrade(boolean)
好,主界面也分析完了。看看图吧,最直观的解释:

在接下来的文章中,我会逐个的讲解功能篇的,如果有需要的话,还会返回来补充。
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/6303/showart_1359369.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP