对象的组合

在面向对象的编程中,经常用到的一种方式就是将更简单的对象组合形成复杂的对象,而复杂的对象还可以组合形成更复杂的对象。这也正是现实世界存在的一种方式,如一台电风扇,可以由底座、支架、控制器、扇头等组成,扇头又有网罩、扇叶、电机组成,电机又由轴承、转子、线圈等组成。编程实现的话,就可以把各种元件建模为对象,然后逐步组合即可。
下面以要实现的图形化时钟的组成,来看类的组合关系。时钟的组成部件可以简单看作为表盘(带有刻度和外周圆圈)、时针、分针和秒针组成,前面定义了刻度类,下面就是表盘外周圆圈类和表盘类的代码。

class PlateOuter:
    def __init__(self,canvas,center_point,radius):
        self.canvas = canvas
        self.center_point = center_point
        self.radius = radius
        self.widget_id = None

    def draw(self):
        x0 = self.center_point[0] - self.radius
        y0 = self.center_point[1] - self.radius
        x1 = self.center_point[0] + self.radius
        y1 = self.center_point[1] + self.radius
        self.canvas.create_oval(x0,y0,x1,y1)

    def delete(self):
        if self.widget_id:
            self.canvas.delete(self.widget_id)

class Plate:

    def __init__(self,canvas, center_point, radius, plong):
        self.canvas = canvas
        self.center_point = center_point
        self.radius = radius
        self.plong = plong
        self.markers = []
        self.gen_markers()

    def draw(self):
        for marker in self.markers:
            marker.draw()

    def gen_markers(self):
        for start,end in zip(gen_end_points(self.center_point,self.radius,6),
                gen_end_points(self.center_point,self.radius + self.plong,6)):
            self.markers.append(Marker(start[1],end[1],self.canvas))
        self.markers.append(PlateOuter(self.canvas,self.center_point,self.radius+20))

    def delete(self):
        for w in self.markers:
            self.canvas.delete(w)

PlateOuter类就是用来代表个表盘外圆圈的,其基本结构也很简单,类似之前的Marker等类,包括四个实例变量和三个实例方法,四个实例变量分别用来表示画布、表盘的中心点座标、表盘的半径和表盘圆的部件id;三个实例方法,第一个是构造方法,draw()方法是依据表盘的中心座标和半径计算后在指定的位置绘制一个圆,delete()方法是用于清除自己的。
在Plate类中,组合了PlateOuter类和多个Marker类构成了表盘,在gen_markers()方法中,生成了表示每个刻度的刻度类的实例和一个PlateOuter类的实例;在draw()方法中,只不过是调用每个组成部分的draw()方法来进行绘制就可以了,delete()方法中也是采取同样的方式来实现的。
下面来看看时钟代码:

class MyClocker:

    def __init__(self,root,canvas, center_point, radius, plong):
        self.root = root
        self.plate = Plate(canvas, center_point, radius, plong)
        self.s_pointer = Pointer(6,canvas,center_point,plong=180,width=1,color='red')
        self.m_pointer = Pointer(3,canvas,center_point,plong=150,width=2,color='blue')
        self.h_pointer = Pointer(1,canvas,center_point,plong=120,width=4,color='black')
        self.display()

    def display(self):
        self.plate.draw()
        self.s_pointer.draw()
        self.m_pointer.draw()
        self.h_pointer.draw()
        self.root.update()

    def walk(self):
        self.s_pointer.walk()
        if (self.s_pointer.count + 1) % 30 == 0:
            self.m_pointer.walk()
            if (self.m_pointer.count + 1) % 4 == 0:
                self.h_pointer.walk()

    def start(self):
        while True:
            self.walk()
            self.root.update()
            time.sleep(1)

在时钟代码类MyClocker中,第一个仍然是构造方法,其中的主要部分为实例化一个表示表盘类Plate,实例化三个Pointer类分别代表时针、分针和秒针,之后调用display()方法绘制各个组成部件;walk()方法中,秒针移动一步,之后检测分针是否应该移动一步,若分针移动一步,则之后还会检测时针是否应该移动一步。
start()方法是时钟启动的关键方法,在其中使用了一个while死循环,调用一次walk()方法后,就sleep一秒。这样就达到每秒移动秒针一步,其他指针会按规则移动的,整个时钟就可以正确无误的工作了。
如果你要知道时钟的图形界面是如何生成的,就必须要了解一下tkinter库。其主要调用代码如下:

root = tkinter.Tk()
cvns = tkinter.Canvas(root,width=530,height=530,bg='white')
cvns.pack()
mc = MyClocker(root,cvns,(260,260),200,10)
t = threading.Thread(target=mc.start)
t.setDaemon(True)
t.start()
root.resizable(False, False)
root.mainloop()

注意这里使用了一个线程来启动时钟的指针的转动,可以避免在进行其他图形化操作时,指针停止的问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容