Python Tkinter 哲學家問題可視化

Python Tkinter 哲學家問題可視化

學校的課設總算是告一段落,現在有時間分享一下代碼和心得了。這次的文章是迴應之前的雛形版代碼的,這次的可是完全版哦。
我們先來看一下效果吧!
在這裏插入圖片描述
效果總歸還行吧,代碼也比較簡短,只有170行。再此推薦你看我之前關於這個題目的文章,接下來我直接代碼講解了。

# -*- coding:utf-8 -*-
#print('資源加載中,稍等片刻……')
from tkinter import *
from PIL import Image,ImageTk  
from threading import Thread,Lock
from time import sleep
from random import randint

"""哲學家類"""
class Philosopher(Thread):
    def __init__(self, index,forks,numForks,labels):
        Thread.__init__(self)
        self.index = index
        self.forks=forks
        self.numForks=numForks
        self.rightFork = forks[self.index]
        self.leftFork = forks[(self.index + 1) % numForks]
        self.bm_thinking=ImageTk.PhotoImage(Image.open('img/thinking.png').resize((150,150),Image.ANTIALIAS))
        self.bm_eating=ImageTk.PhotoImage(Image.open('img/eating.png').resize((150,150),Image.ANTIALIAS))
        self.bm_waiting=ImageTk.PhotoImage(Image.open('img/waiting.png').resize((150,150),Image.ANTIALIAS))
        self.bm_another=ImageTk.PhotoImage(Image.open('img/another.png').resize((150,150),Image.ANTIALIAS))
    def run(self):
        while True:
            self.thinking()
            self.hungry()
            while True:
                sleep(0.5)
                if hobby.get()==True:#獲取按鈕的狀態
                    self.leftFork.pickup()
                    labels[self.index].configure(image=self.bm_another)
                    labels[self.index].image=self.bm_another
                    text.insert(END,"Philosopher %s pick up left Fork.\n"%(self.index))
                    sleep(0.5)
                    self.rightFork.pickup()
                    text.insert(END,"Philosopher %s pick up right Fork.\n"%(self.index))
                    self.dining()
                    self.leftFork.putdown()
                    text.insert(END,"Philosopher %s put down left Fork.\n"%(self.index))
                    self.rightFork.putdown()
                    text.insert(END,"Philosopher %s put down right Fork.\n"%(self.index))
                    break
                else:
                    if self.leftFork.use==False:
                        if self.rightFork.use==False:
                            self.leftFork.pickup()
                            text.insert(END,"Philosopher %s pick up left Fork.\n"%(self.index))
                            self.rightFork.pickup()
                            text.insert(END,"Philosopher %s pick up right Fork.\n"%(self.index))
                            self.dining()
                            self.leftFork.putdown()
                            text.insert(END,"Philosopher %s put down left Fork.\n"%(self.index))
                            self.rightFork.putdown()
                            text.insert(END,"Philosopher %s put down right Fork.\n"%(self.index))
                            break
              
    def dining(self):
        text.insert(END,"Philosopher %s starts to eat.\n"%(self.index))
        labels[self.index].configure(image=self.bm_eating)
        labels[self.index].image=self.bm_eating
        sleep(randint(2,4))
        text.insert(END,"Philosopher %s finishes eating.\n"%(self.index))
    def thinking(self):
        text.insert(END,"Philosopher %s is thinking.\n"%(self.index))
        labels[self.index].configure(image=self.bm_thinking)
        labels[self.index].image=self.bm_thinking
        if not hobby2.get():#獲取按鈕的狀態
            sleep(randint(2,5))
        
    def hungry(self):
        text.insert(END,"Philosopher %s is hungry.\n"%(self.index))
        labels[self.index].configure(image=self.bm_waiting)
        labels[self.index].image=self.bm_waiting
        sleep(1)

python圖形化的特點,寫代碼的時候要和Tkinter裏的組件結合,所以有時候會不知道類裏的使用到的組件是哪來的,可以回到主程序裏尋找。

"""叉子類"""
class Fork:
    def __init__(self, index,label_forks):
        self.index = index
        self.forks=label_forks
        self._lock = Lock()
        self.bm_fork=ImageTk.PhotoImage(Image.open('img/fork.png').resize((70,130),Image.ANTIALIAS))
        self.bm_nothing=ImageTk.PhotoImage(Image.open('img/nothing.png').resize((70,130),Image.ANTIALIAS))
        self.use=False
        for i in self.forks:
            i.configure(image=self.bm_fork)
    def pickup(self):
    """叉子的消失其實就是拿一張和背景色相同的圖片來遮擋叉子的圖片"""
        self._lock.acquire()
        self.use=True
        self.forks[self.index].configure(image=self.bm_nothing)
        self.forks[self.index].image=self.bm_nothing
    def putdown(self):
        self._lock.release()
        self.use=False
        self.forks[self.index].configure(image=self.bm_fork)
        self.forks[self.index].image=self.bm_fork
def main():
    text.delete('0.0','end')#清除文本框中的所有內容
    numPhilosophers = numForks = 5
    # 創建叉子與哲學家實例
    forks = [Fork(idx,label_forks) for idx in range(numForks)]
    philosophers = [Philosopher(idx,forks,numForks,labels)for idx in range(numPhilosophers)]
    
    # 開啓所有的哲學家線程
    for philosopher in philosophers:
        philosopher.start()
def author():#關於作者的說明,我沒有用文本形式而是圖片呈現
"""必要一提的就是Tkinter不支持二級窗口呈現圖片,所以要用Toplevel"""
    newroot=Toplevel()
    bm_about = ImageTk.PhotoImage(Image.open('img/about.png'))
    aboutme=Label(newroot,image=bm_about)
    aboutme.pack()
    newroot.mainloop()

def detail_show():#“說明”的內容
    newroot2=Toplevel()
    bm_details = ImageTk.PhotoImage(Image.open('img/details.png'))
    details_label=Label(newroot2,image=bm_details)
    details_label.pack()
    newroot2.mainloop()
if __name__ == '__main__':
    root = Tk()
    root.title('哲學家問題')
    root.geometry('1000x750')
    start=Button(root,text='開始',font=('Arial',12),command=main,relief='raised')
    start.place(x=100,y=0)
    tip=Button(root,text='關於',font=('Arial',12),command=author,relief='raised')
    tip.place(x=180,y=0)
    hobby=BooleanVar()
    isDeathLock=Checkbutton(root,text='允許死鎖',font=('Arial',12),variable=hobby,relief='raised')
    isDeathLock.place(x=260,y=0)
    hobby2=BooleanVar()
    death=Checkbutton(root,text='飢餓模式',font=('Arial',12),variable=hobby2,relief='raised')
    death.place(x=400,y=0)
    details=Button(root,text='說明',font=('Arial',12),command=detail_show,relief='raised')
    details.place(x=550,y=0)
    scroll = Scrollbar()
    text=Text(root,width=35,height=50)
    scroll.pack(side=RIGHT,fill=Y)
    text.pack(side=RIGHT)
    scroll.config(command=text.yview)
    text.config(yscrollcommand=scroll.set)
    img_init = Image.open('img/init.png').resize((150,150),Image.BILINEAR)
    bm_init=ImageTk.PhotoImage(img_init)
    labels=[]
    for i in range(5):
        temp=Label(root,image=bm_init)
        labels.append(temp)
    labels[0].place(x=400,y=100)
    labels[1].place(x=520,y=350)
    labels[2].place(x=250,y=570)
    labels[3].place(x=50,y=350)
    labels[4].place(x=150,y=100)
    #加載餐桌圖像
    img_table = Image.open('img/table.png').resize((250,200),Image.BILINEAR)
    bm_table = ImageTk.PhotoImage(img_table)
    label_table=Label(root,image=bm_table)
    label_table.image=bm_table
    label_table.place(x=240,y=300)
    #加載叉子類
    img_fork = Image.open('img/fork.png').resize((70,130),Image.BILINEAR)
    bm_fork = ImageTk.PhotoImage(img_fork)
    label_forks=[]
    for i in range(5):
        temp=Label(root,image=bm_fork)
        label_forks.append(temp)
    label_forks[0].place(x=320,y=100)
    label_forks[1].place(x=600,y=150)
    label_forks[2].place(x=500,y=550)
    label_forks[3].place(x=150,y=550)
    label_forks[4].place(x=75,y=175)
    print('加載完畢')
    root.mainloop()

大功告成,代碼自取,圖片自己挑選設計,死鎖的解決換種算法,我使用的是簡單的”左右手都有叉子才進餐“的算法。關於使用pyinstaller對py文件打包可以參考我的另外一篇文章。喜歡就點個贊再走吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章