python: Parent-child form operations

 

# encoding: utf-8
# 版權所有 2024 ©塗聚文有限公司
# 許可資訊查看:言語成了邀功的功臣,還需要行爲每日來值班嗎?
# 描述: 主、子表單 窗體傳值  Parent-child form operations
# Author    : geovindu,Geovin Du 塗聚文.
# IDE       : PyCharm 2023.1 python 3.11
# OS        : windows 10
# Datetime  : 2024/10/24 20:09
# User      : geovindu
# Product   : PyCharm
# Project   : IctGame
# File      : ui/main.py
# explain   : 學習

from tkinter import *
import tkinter as tk
from tkinter import Tk, Menu
from tkinter.ttk import *
from tkinter.messagebox import showinfo
from tkinter.messagebox import askyesno
from tkinter.messagebox import askyesnocancel  # https://docs.python.org/3/library/tkinter.messagebox.html
import datetime
import re
import time
import ctypes

u32 = ctypes.windll.user32


class MainWindow(tk.Tk):
    """
    主表單
    """

    # ts = time.time()

    reverseFlag = False

    def __init__(self):
        """
        
        """
        
        super().__init__()
        self.title("Main Window")
        self.geometry('{}x{}'.format(1350, 900))
        self.button = tk.Button(self, text=" Child New Window", command=self.openChildNewWindow)
        self.button.pack()
        self.btnopen = tk.Button(self, text=" Child Open Window", command=self.openChildOpenWindow)
        self.btnopen.pack()
        # show a label
        self.ts = time.time()  # 放在這裡更新
        self.st = datetime.datetime.fromtimestamp(self.ts).strftime('%Y-%m-%d %H:%M:%S')
        self.label = tk.Label(self, text=self.st)
        self.label.pack(ipadx=10, ipady=10)
        self.protocol("WM_DELETE_WINDOW", self.closeWindows)  # 關閉視窗時調用closeWindows方法
        # create a menubar
        self.menubar = Menu()
        self.config(menu=self.menubar)

        # create the file_menu
        self.fileMenu = Menu(
            self.menubar,
            tearoff=0
        )

        # add menu items to the File menu
        self.fileMenu.add_command(label='New', command=self.openChildNewWindow)
        self.fileMenu.add_command(label='Open...', command=self.openChildOpenWindow)
        self.fileMenu.add_command(label='Close')
        self.fileMenu.add_separator()

        # add a submenu
        self.subMenu = Menu(self.fileMenu, tearoff=0)
        self.subMenu.add_command(label='Keyboard Shortcuts')
        self.subMenu.add_command(label='Color Themes')

        # add the File menu to the menubar
        self.fileMenu.add_cascade(
            label="Preferences",
            menu=self.subMenu
        )

        # add Exit menu item
        self.fileMenu.add_separator()
        self.fileMenu.add_command(
            label='Exit',
            command=self.destroy
        )

        self.menubar.add_cascade(
            label="File",
            menu=self.fileMenu,
            underline=0
        )
        # create the Help menu
        self.helpMenu = Menu(
            self.menubar,
            tearoff=0
        )

        self.helpMenu.add_command(label='Welcome')
        self.helpMenu.add_command(label='About...')

        # add the Help menu to the menubar
        self.menubar.add_cascade(
            label="Help",
            menu=self.helpMenu,
            underline=0
        )
        # self.after(1000,goWindows)
        # self.stateCity = {"江西":"南昌","西藏":"拉薩",
        # "新疆":"烏魯木齊","寧夏":"銀川",
        # "江蘇":"南京","山東":"青島",
        # "廣東":"廣州","福建":"福州","山東":"濟南","安徽":"合肥",
        # "湖北":"武漢","湖南":"長沙",
        # "陝西":"西安","貴州":"貴陽",
        # "四川":"成都","青海":"銀川","甘肅":"蘭州",
        # "內蒙古":"呼爾浩特","遼寧":"瀋陽",
        # "吉林":"長春","黑龍江":"哈爾濱","廣西":"南寧",
        # "雲南":"昆明","山西":"太原","浙江":"杭州",
        # "台灣":"臺北","河北":"石傢莊","河南":"鄭州","北京":"朝陽",
        # "上海":"閔行","天津":"濱海","重慶":"解放碑"}

        self.stuData = [[1, 'geovindu', '0002', '2008-01-03', 16], [2, 'jason', '0001', '2007-02-13', 13],
                        [3, 'ada', '0003', '2008-02-14', 11]
            , [4, 'link', '0004', '2007-03-14', 10], [5, 'dus', '0005', '2007-02-12', 10],
                        [6, 'bike', '0006', '2007-02-14', 17]
            , [7, 'eson', '0007', '2007-04-14', 17], [8, 'fok', '0008', '2007-02-11', 17],
                        [9, 'hosrse', '0009', '2006-02-14', 10]
            , [10, 'ken', '0011', '2007-05-14', 12], [11, 'red', '0021', '2007-02-10', 12],
                        [12, 'mike', '0031', '2005-02-14', 13]
            , [13, 'nike', '0012', '2007-06-14', 27], [14, 'queen', '0022', '2007-02-24', 15],
                        [15, 'pen', '0023', '2004-02-14', 12]
            , [16, 'yelu', '0033', '2007-07-14', 22], [17, 'zoon', '0043', '2007-02-23', 14],
                        [18, 'work', '0051', '2003-02-14', 16]
            , [19, 'ven', '0061', '2007-08-14', 13], [20, 'tesk', '0071', '2007-02-22', 11],
                        [21, 'ilove', '0081', '2002-02-14', 15]
            , [22, 'open', '0091', '2007-09-14', 14], [23, 'seeek', '0092', '2007-02-21', 18],
                        [24, 'uluko', '0028', '2009-02-14', 19]
            , [25, 'xero', '0101', '2007-10-14', 11], [26, 'xoo', '0201', '2007-02-20', 16],
                        [27, 'yyeluey', '0301', '2017-02-14', 21]
            , [28, 'shenzhen', '0401', '2007-12-14', 10], [29, 'guanzhou', '0501', '2007-02-19', 19],
                        [30, 'beiking', '0601', '2014-02-14', 20]
            , [31, 'qinhua', '0701', '2007-11-14', 16], [32, 'nanchang', '0001', '2007-02-18', 20],
                        [33, 'jian', '0801', '2015-02-14', 14]
                        ]
        # 建立Treeview
        self.tree = Treeview(self,style='success.Treeview',height=50,show='headings')  #, columns=("StudentId", "StudentName", "StudentNO", "StudentBirthday", "Age")
        """
        定義捲軸控制項
        orient為捲軸的方向,vertical--縱向,horizontal--橫向
        command=self.tree.yview 將捲軸綁定到treeview控制項的Y軸
        """
        '''消除第一行空列'''
        #self.tree['show'] = 'headings'
        self.tree.pack()
        # 1
        # self.yscrollbar = Scrollbar(self, orient='vertical', command=self.tree.yview)
        # self.yscrollbar.place(relx=0.971, rely=0.028, relwidth=0.024, relheight=0.958)

        # 2
        self.yscrollbar = Scrollbar(self)
        self.yscrollbar.pack(side=RIGHT, fill=Y)
        self.yscrollbar.config(command=self.tree.yview)
        self.tree.configure(yscrollcommand=self.yscrollbar.set)
        
        self.headtext=['序號','姓名','學號','出生日期','年齡']
        
        # 定義列
        self.tree['columns']=("StudentId","StudentName","StudentNO","StudentBirthday","Age")
        
        # 設置列屬性,列不顯示
        """
        self.tree.column("StudentId", width=150, minwidth=100, anchor=S)
        self.tree.column("StudentName", width=150, minwidth=100, anchor=S)
        self.tree.column("StudentNO", width=150, minwidth=100, anchor=S)
        self.tree.column("StudentBirthday", width=150, minwidth=100, anchor=S)
        self.tree.column("Age", width=150, minwidth=100, anchor=S)
        # 設置表頭
        self.tree.heading("StudentId", text="序號", command=lambda c="StudentId": self.treeviewSortColumn(c))
        self.tree.heading("StudentName", text="姓名", command=lambda c="StudentName": self.treeviewSortColumn(c))
        self.tree.heading("StudentNO", text="學號", command=lambda c="StudentNO": self.treeviewSortColumn(c))
        self.tree.heading("StudentBirthday", text="出生日期",
                          command=lambda c="StudentBirthday": self.treeviewSortColumn(c))
        self.tree.heading("Age", text="年齡", command=lambda c="Age": self.treeviewSortColumn(c))
        """
        k=0
        for column in self.tree['column']:
            self.tree.column(column, width=150, minwidth=100, anchor=S)  
            self.tree.heading(column, text=self.headtext[k], command=lambda c=column: self.treeviewSortColumn(c))
            k=k+1               
        
        
        # 建立欄標題
        # self.tree.heading("#0",text="StudentId",command=lambda c="StudentId": self.treeviewSortColumn(c))     # 圖示欄位元icon column
        # self.tree.heading("#1",text="StudentName",command=lambda c="StudentName": self.treeviewSortColumn(c))
        # self.tree.heading("#2",text="StudentNO",command=lambda c="StudentNO": self.treeviewSortColumn(c))
        # self.tree.heading("#3",text="StudentBirthday",command=lambda c="StudentBirthday": self.treeviewSortColumn(c))
        # self.tree.heading("#4",text="Age",command=lambda c="Age": self.treeviewSortColumn(c))

        i = 0
        # 建立內容 多一個空列
        for StudentId, StudentName, StudentNO, StudentBirthday, Age in self.stuData:
            # self.tree.insert("",index=END,text=state,values=self.stateCity[state])
            self.tree.insert("", index=END,text=StudentId,values=(StudentId, StudentName, StudentNO, StudentBirthday, Age))
            i = i + 1
        self.tree.bind("<Double-1>", self.doubleClick)  # 連按2下綁定doubleClick方法        

 
        style = Style()
        style.map("Treeview", foreground=self.fixedMap("foreground"), 
        background=self.fixedMap("background"))  
         
        #定義背景色風格
        self.tree.tag_configure('even', background='lightblue') # even標籤設定為淺藍色背景顏色
        
        self.treeColor() 
        
    def fixedMap(self,option):
        """
        
        :return
        """
        style = Style()
        return [elm for elm in style.map("Treeview", query_opt=option)
           if elm[:2] != ("!disabled", "!selected")] 

    
    def treeColor(self):
        """
        表格欄隔行顯示不同顏色函數
        :return
        """
        self.items=self.tree.get_children() # 得到根目錄所有行的iid
        i=0 # 初值
        for hiid in self.items:
            if i/2!=int(i/2): # 判斷奇偶
                tag1='' # 奇數行
            else:
                tag1='even' # 偶數行
            self.tree.item(hiid,tag=tag1) # 偶數行設為淺藍色的tag='even'
            i+=1 # 累加1       
 

    def openChildNewWindow(self):
        """
        
        :return: 
        """
        subwindow = ChildNewWindow(self)
        self.update()  # 刷新主窗口

    def openChildOpenWindow(self):
        #data = [35, '塗聚文', '0801', '2015-02-14', 14]
        try:
            strtreeitem = self.tree.selection()[0]    # iid
            if strtreeitem is not None:
                values = self.tree.item(strtreeitem, option='values')  # 取值
                print(values)
                data = [values[0], values[1], values[2], values[3], values[4]]
                subwindow = ChildOpenWindow(self, data)
                self.update()  # 刷新主窗口
            else:
                pass
        except Exception as ex:
            showinfo("提示", "請選定一行記錄!")
        

    def closeWindows(self):
        """
        close
        :return
        """
        msgbye()
        self.destroy()  # 關閉

    def goWindows(self):
        """
        
        :return: 
        """

        # ChildBack=u32.GetParent(ChildNewWindow(self).winfo_id())
        MainBack = u32.GetParent(self.winfo_id())
        u32.SetParent(MainBack, self.winfo_id())
        # u32.SetParent(ChildBack,self.winfo_id())

    def doubleClick(self, event):
        """
        雙擊事件 StudentId, StudentName, StudentNO, StudentBirthday, Age
        :param event
        :return: 
        """
        e = event.widget  # 取得事件控制項
        iid = e.identify("item", event.x, event.y)  # 取得連按2下專案id
        StudentId = e.item(iid, "text")  # 取得StudentId
        StudentName = e.item(iid, "values")[1]  # 取得StudentName
        StudentNO = e.item(iid, "values")[2]
        StudentBirthday= e.item(iid, "values")[3]
        Age= e.item(iid, "values")[4]
        str = "{0} : {1}".format(StudentId, StudentName)  # 格式化
        data = [StudentId, StudentName,StudentNO,StudentBirthday,Age]
        # messagebox.showinfo("Double Clicked",str)   # 輸出
        child = ChildOpenWindow(self, data)
        self.update()

    def isNumber(self, tel: str) -> bool:
        """
        驗證是數字的
        :param tel
        :return
        """
        try:
            pattern = r'^\d+$'  # 匹配8位元數字 {8}
            if re.match(pattern, tel):
                # print("電話號碼有效!")
                return True
            else:
                # print("電話號碼無效!")
                return False
        except (ValueError, IndexError, EnvironmentError):
            return False
        except Exception as ex:
            print(ex)
            return False

    def treeviewSortColumn(self, col):
        """
        字母可以排序,數字排序,其它類型冇考慮,自己再寫
        :param col:  列名
        :return: 
        """           
        
        global reverseFlag  # 定義排序旗標全域變數 可以判斷字元類型進行排序
        lst = [(self.tree.set(st, col), st)
               for st in self.tree.get_children("")]
        # print(lst)        # 列印列表
        if type(lst[0]) == int:
            lst.sort(key=lambda lst: int(lst[0]), reverse=self.reverseFlag)  # 排序列表
        else:
            lst.sort(key=lambda lst: lst[0], reverse=self.reverseFlag)
        print(lst)  # 列印列表
        newdata = []
        for num, ids in lst:
            if self.isNumber(num):  # 考慮字串類型
                print(int(num), ids)
                newdata.append((int(num), ids))
            else:
                print(num, ids)
                newdata.append((num, ids))
        newdata.sort(key=lambda newdata: newdata[0], reverse=self.reverseFlag)

        for index, item in enumerate(newdata):  # 重新移動專案內容 以字元排序
            self.tree.move(item[1], "", index)
            print(index, item[0])
        self.reverseFlag = not self.reverseFlag  # 更改排序旗標   

    def __delattr__(self):
        """
        
        :return: 
        """
        print('del1')

    def __del__(self):
        """
        
        :return: 
        """
        print('del2')  # 需要消


def msg():
    """
    
    :return: 
    """
    showinfo("Notebook", "welcome GeovinDu's Notebook")


def msgbye():
    """
    
    :return: 
    """
    
    showinfo("ByeBye", "welcome GeovinDu's Notebook,bye!")


class ChildNewWindow(tk.Toplevel):
    """
    子表單
    """

    def __init__(self, master):
        """
        
        :param master: 
        """
        super().__init__(master)
        # self._data=data
        self.title("Child Window")
        self.geometry('{}x{}'.format(850, 900))
        self.label = tk.Label(self, text="Child Window")
        self.label.pack()
        self.attributes('-topmost', 'true')  # 表單最上層
        self.protocol("WM_DELETE_WINDOW", self.closeWindows)  # 關閉視窗時調用closeWindows方法

    def closeWindows(self):
        """
        close
        :return
        """
        self.destroy()  # 關閉
        self.master.destroy()  # 關閉主視窗,不關閉,還是原有畫布的資料,不更新資料,如何想一個更好的方法封裝?

        msg()
        main = MainWindow()  # 重新載入表單 更新表單資料
        main.attributes('-topmost', 'true')  # 表單最上層
        main.mainloop()
        self.master.update()


class ChildOpenWindow(tk.Toplevel):
    """
    子表單
    """

    def __init__(self, master, data):
        """
        
        :param master: 
        :param data: 
        
        """
        super().__init__(master)
        self.master = master
        self._data = data
        self.title("Open Window")
        self.geometry('{}x{}'.format(850, 900))
        self.label = tk.Label(self, text="Open Window, Geovin Du like you.")
        # self.Toplevel(self.master)
        self.label.pack()
        self.attributes('-topmost', 'true')  # 表單最上層        
        self.protocol("WM_DELETE_WINDOW", self.closeWindows)  # 關閉視窗時調用closeWindows方法
        # StudentId, StudentName, StudentNO, StudentBirthday, Age
        self.StudentId = tk.Label(self, text=f'id:{self._data[0]}')
        self.StudentId.pack()
        self.StudentName = tk.Label(self, text=f'Name:{self._data[1]}')
        self.StudentName.pack()
        self.StudentNO = tk.Label(self, text=f'NO:{self._data[2]}')
        self.StudentNO.pack()
        self.StudentBirthday = tk.Label(self, text=f'Birthday:{self._data[3]}')
        self.StudentBirthday.pack()
        self.Age = tk.Label(self, text=f'Age:{self._data[4]}')
        self.Age.pack()
        

    def closeWindows(self):
        """
        close
        :return
        """
        self.destroy()  # 關閉現窗口
        self.master.destroy()  # 關閉主視窗 不關閉,還是原有畫布的資料,不更新資料,如何想一個更好的方法封裝?        
        msg()
        main = MainWindow()  # 重新載入表單 更新表單資料
        main.attributes('-topmost', 'true')  # 表單最上層
        main.mainloop()
        self.master.update()

        

if __name__=="__main__":
    """
    main output
    
    """
    mainwindow = MainWindow()
   
    #mainwindow.after(1000,mainwindow.goWindows())
    mainwindow.mainloop()

 

 

 

 

 

 

posted @ 2024-10-24 19:47  ®Geovin Du Dream Park™  阅读(5)  评论(0编辑  收藏  举报