system-config-kickstart源码阅读(三)基本设置篇

这篇我开始基本设置之旅---basic.py解析:

如图所示,basic也仅实现了一个类,7个方法。
还是像前两篇一样,我们来将代码贴出来,然后逐行分析,遇到新的模块或者陌生的语法和调用,我会引导外部分析:
import gtk
import gtk.glade
import gobject
import string
import os
import random    //python的随机数生成器模块,比如需要一从1到100的随机数,则random.randint(1,100).
import crypt    //验证unix密码的函数,只有一种可能那就是crypt.crypt(word, salt)
import getopt    //命令行构造
from rhpl import keyboard_models     
import rhpl.keyboard as keyboard   //rhpl的关于识别系统键盘的库函数
from hardwareLists import langDict   //识别系统语言的类,将会详细分析其代码。
import kickstartGui         //上篇分析过的,毕竟有些东西是要显示在主要的GUI上的。
import sys
from pykickstart.constants import *
##
## I18N
##
import gettext
gtk.glade.bindtextdomain("system-config-kickstart")
_ = lambda x: gettext.ldgettext("system-config-kickstart", x)
sys.path.append("/usr/share/system-config-date")  //追加另外一个设置日期的工具的PYTHONPATH.
class basic: //定义basic类
    def __init__(self, parent_class, xml, notebook, ksHandler):
        self.parent_class = parent_class
        self.notebook = notebook
        self.ks = ksHandler
        self.xml = xml
//以下一段为从glade文件获得gtk相应的widget.
        self.lang_combo = xml.get_widget("lang_combo")
        self.keyboard_combo = xml.get_widget("keyboard_combo")
        self.timezone_combo = xml.get_widget("timezone_combo")
        self.utc_check_button = xml.get_widget("utc_check_button")
        self.root_passwd_entry = xml.get_widget("root_passwd_entry")
        self.root_passwd_confirm_entry = xml.get_widget("root_passwd_confirm_entry")
        self.reboot_checkbutton = xml.get_widget("reboot_checkbutton")
        self.text_install_checkbutton = xml.get_widget("text_install_checkbutton")
        self.ks.bootloader(md5pass="", password="")
        self.interactive_checkbutton = xml.get_widget("interactive_checkbutton")
        self.encrypt_root_pw_checkbutton = xml.get_widget("encrypt_root_pw_checkbutton")
        self.lang_support_list = xml.get_widget("lang_support_list")
        self.platform_combo = xml.get_widget("platform_combo")
        self.platform_list =  [_("x86, AMD64, or Intel EM64T"), _("Intel Itanium"), _("IBM iSeries"),
                               _("IBM pSeries"), _("IBM zSeries/s390")]
        self.platform_combo.set_popdown_strings(self.platform_list)
        self.platform_combo.entry.connect("changed", self.platformChanged)//自定义,不是从glade获得,系统所支持的架构。
        self.key_checkbutton = xml.get_widget("key_checkbutton")
        self.key_entry = xml.get_widget("key_entry")
        self.key_checkbutton.connect("toggled", self.keyChanged)//去找keychanged这个方法吧!
        self.langDict = langDict //获得系统支持的语言,以python字典形式存储。详见后面。
        # set a default platform  
        if not self.ks.platform:
            self.ks.platform = "x86, AMD64, or Intel EM64T"
        #populate language combo  
        self.lang_list = self.langDict.keys()
        self.lang_list.sort()
        self.lang_combo.set_popdown_strings(self.lang_list)
        #set default to English
        self.lang_combo.list.select_item(self.lang_list.index('English (USA)'))
        #populate keyboard combo, add keyboards here
        self.keyboard_dict = keyboard_models.KeyboardModels().get_models()
        keys = self.keyboard_dict.keys()
        keyboard_list = []
        for item in keys:
            keyboard_list.append(self.keyboard_dict[item][0])
        keyboard_list.sort()
        self.keyboard_combo.set_popdown_strings(keyboard_list)
        #set default to English
        kbd = keyboard.Keyboard()
        kbd.read()
        currentKeymap = kbd.get()
    #set keyboard to current keymap
        try:
            self.keyboard_combo.entry.set_text(self.keyboard_dict[currentKeymap][0])
        except:
            self.keyboard_combo.entry.set_text(self.keyboard_dict["us"][0])
        #populate time zone combo
        import zonetab    //来自system-config-date的类,获取/usr/share/zoneinfo/zone.tab文件内容,此将会在以后对文本处理方面来分析。
        zt = zonetab.ZoneTab()
        self.timezone_list = [ x.tz for x in zt.getEntries() ]  //将大洲/国家提取出来。
        self.timezone_list.sort()  //排序
        try:
            select = self.timezone_list.index("America/New_York")  //index,呵呵,狗日的america.
        except:
            select = 0
        self.timezone_combo.set_popdown_strings(self.timezone_list) //下拉列表框
        self.timezone_combo.list.select_item(select)
    def updateKS(self, ksHandler):   //更新ks临时
        self.ks = ksHandler
    def formToKickstart(self, doInstall):    //获得信息
        self.ks.lang(lang=self.languageLookup(self.lang_combo.entry.get_text())) //语言
        keys = self.keyboard_dict.keys()  //键盘
        keys.sort()
        for item in keys:
            if self.keyboard_dict[item][0] == self.keyboard_combo.entry.get_text():
                self.ks.keyboard(keyboard=item)
                break
        self.ks.timezone(timezone=self.timezone_combo.entry.get_text(), isUtc=self.utc_check_button.get_active())                     
//以下的密码设置应该注意一下,很多地方都会用到。
    //首先判断两次输入的明文是否匹配。不匹配则弹出对话框,并再次输入。(再保存文件时会提示!也就是说在applykickstart时才会调用此方法。)
        if self.root_passwd_entry.get_text() != self.root_passwd_confirm_entry.get_text():
            dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("Root passwords do not match."))
            dlg.set_title(_("Error"))
            dlg.set_default_size(100, 100)
            dlg.set_position (gtk.WIN_POS_CENTER)
            dlg.set_icon(kickstartGui.iconPixbuf)
            dlg.set_border_width(2)
            dlg.set_modal(True)
            toplevel = self.xml.get_widget("main_window")
            dlg.set_transient_for(toplevel)
            dlg.run()
            dlg.hide()
            self.notebook.set_current_page(0)//切还到当前
            self.root_passwd_entry.set_text("")  //清空
            self.root_passwd_confirm_entry.set_text("") //清空
            self.root_passwd_entry.grab_focus()  //获得光标
            return None
        pure = self.root_passwd_entry.get_text()
//注意了,加密出没!
        if pure != "":
            if self.encrypt_root_pw_checkbutton.get_active() == True:
                salt = "$1$"
                saltLen = 8
                if not pure.startswith(salt):
                    for i in range(saltLen):
                        salt = salt + random.choice (string.letters + string.digits + './')
                    self.passwd = crypt.crypt (pure, salt)  //类似unix的shadow加密。
                    temp = unicode (self.passwd, 'iso-8859-1')  //编码!!
                    self.ks.rootpw(isCrypted=True, password=temp)
                else:
                    self.ks.rootpw(isCrypted=True, password=pure)
            else:
                self.passwd = self.root_passwd_entry.get_text()
                self.ks.rootpw(isCrypted=False, password=self.passwd)   //大不了不加密就是了。
        self.ks.platform = self.platform_combo.entry.get_text()  获取平台列表
        if self.reboot_checkbutton.get_active():
            self.ks.reboot(action=KS_REBOOT)
        else:
            self.ks.reboot(action=KS_WAIT)
        if self.text_install_checkbutton.get_active():
            self.ks.displaymode(displayMode=DISPLAY_MODE_TEXT)
        else:
            self.ks.displaymode(displayMode=DISPLAY_MODE_GRAPHICAL)
        self.ks.interactive(interactive=self.interactive_checkbutton.get_active())
        if self.key_checkbutton.get_active():
            if self.key_entry.get_text() == "":
                self.ks.key(key=KS_INSTKEY_SKIP)
            else:
                self.ks.key(key=self.key_entry.get_text())
        else:
            self.ks.key(key="")
        return 0
    def languageLookup(self, args):    //语言获取
        return self.langDict [args]
    def platformChanged(self, entry):   //平台
        platform = entry.get_text()
        if platform:
            self.parent_class.platformTypeChanged(entry.get_text())
    def keyChanged(self, args):
        self.key_entry.set_sensitive(self.key_checkbutton.get_active())
    def applyKickstart(self):     //一切都OK?我要保存了阿!!!!!!!
        if self.ks.platform in self.platform_list:
            self.platform_combo.entry.set_text(self.ks.platform)
        if self.ks.lang.lang.find (".") != -1:
            ksLang = self.ks.lang.lang.split(".")[0]
        else:
            ksLang = self.ks.lang.lang
        for lang in self.langDict.keys():
            if self.langDict[lang] == ksLang:
                self.lang_combo.list.select_item(self.lang_list.index(lang))
        if self.ks.keyboard.keyboard != "":
            self.keyboard_combo.entry.set_text(self.keyboard_dict[self.ks.keyboard.keyboard][0])
        if self.ks.timezone.timezone != "":
            self.timezone_combo.list.select_item(self.timezone_list.index(self.ks.timezone.timezone))
        self.reboot_checkbutton.set_active(self.ks.reboot.action == KS_REBOOT)
        if self.ks.displaymode.displayMode == DISPLAY_MODE_TEXT:
            self.text_install_checkbutton.set_active(True)
        if self.ks.interactive.interactive == True:
            self.interactive_checkbutton.set_active(True)
        if self.ks.rootpw.password != "":
            self.root_passwd_entry.set_text(self.ks.rootpw.password)
            self.root_passwd_confirm_entry.set_text(self.ks.rootpw.password)
            self.encrypt_root_pw_checkbutton.set_active(self.ks.rootpw.isCrypted)
        if self.ks.key.key == "":
            self.key_checkbutton.set_active(False)
            self.key_entry.set_text("")
        elif self.ks.key.key == KS_INSTKEY_SKIP:
            self.key_checkbutton.set_active(True)
            self.key_entry.set_text("")
        else:
            self.key_checkbutton.set_active(True)
            self.key_entry.set_text(self.ks.key.key)
文章到此,就开始有了疑惑,不就是这些吗?有什么大不了的?但是,喜欢往里专的人都清楚和明白,键盘是怎么被读取到的?时区了呢?加密的过程是什么?语言列表是如何出现的?以上均是一个import就解决了的问题,那么我们该怎么往下走了呢?
一个小小软件,错综复杂到如此地步,真是小看了它!解决问题是程序员的使命,而代码就是解决问题的实现,解决问题是负载的,代码应该是清晰的,但逻辑上应该是复杂的!
还好,就快完了,其实脚本语言的最大的使命之一就是文本处理!!文本能够熟练的处理好了,也就可以成为一名合格的系统管理员了!至于高深的算法、优秀的数据结构那是很遥远的东西,与现在的无关,将来也许会有!
来看代码吧!废话真多!
//删除了注释。
import string   //仅仅是一个string就够了?string已经很强大了!
#pull list of language from system-config-languages
langDict = {}     //定义一字典
lines = open("/usr/share/system-config-language/locale-list", "r").readlines()
//逐行读取指定文件
for line in lines:  
    tokens = string.split(line) //将行中的单词按空格、tab、换行符等分割,存储到list中。
    if '.' in tokens[0]:  //如果行中的第一个单词里有“点”时
        #Chop encoding off so we can compare to self.installedLangs
        langBase = string.split(tokens[0], '.')  //用.将此单词分割,存储到列表。
        langBase = langBase[0]  //相当于赋值
    elif '@' in tokens[0]:   //如果行中的第一个单词里有@时
        langBase = string.split(tokens[0], '@')//用@将此单词分割,存储到列表。
        langBase = langBase[0]  
    else:
        langBase = tokens[0]  //什么都没有,就是没有了!
    name = ""
    for token in tokens[3:]:   //将行中的第四个词抽取
        name = name + " " + token   //组成新的名称
    name = string.strip(name)     //将刚组成的新的名称再按空格、tab、换行符等分割。
    langDict[name] = langBase  //放到langDict中存储。
我以文件/usr/share/system-config-language/locale-list中的中国那一行为例说明:
zh_CN.UTF-8 utf8 lat0-sun16 Chinese (P.R. of China)  -  中文(简体)
这是原文,经过此程序的过滤后,输出为:
langBase 是zh_CN,name是C h i n e s e.
顺便说一句,关于时区的抽取和语言这个差不了多少,此处我分析了一个例子,贴在此处:
import re,string
fn = '/usr/share/zoneinfo/zone.tab'
def readZoneTab():
    f = open(fn, 'r')
    comment = re.compile("^#")
    coordre = re.compile("[\+-]")
    while 1:
        line = f.readline()
        if not line:
            break
        if comment.search(line):
            continue
        fields = string.split(line, '\t')
        if len(fields)  3:
            comments = string.strip(fields[3])
        else:
            comments = None
    f.close()
if __name__ == "__main__":
    readZoneTab()
还用到了正则表达式模块了,呵呵,这是没有几年功夫搞不定的东西。。。。。
好,此篇点到为止,功能篇将继续,自己期待以下自己的时间!