'''
Data Mining Project :
'''
#======================
# imports
#======================
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
from tkinter import Menu
from tkinter import messagebox as msg
from tkinter import Spinbox
from time import sleep # careful - this can freeze the GUI
from tkcalendar import Calendar, DateEntry
from tkinter import filedialog as fd
import os
import sys
from datetime import date
import time
import requests
import dill
import pandas as pd
from io import StringIO
import sqlite3
import math
from collections import deque
# should use auto calculation to get the last stock date
dblastdate='20160901'
# Create instance
win = tk.Tk()
# Add a title
win.title("Data Mining Project")
tabControl = ttk.Notebook(win) # Create Tab Control
tab1 = ttk.Frame(tabControl) # Create a tab
tabControl.add(tab1, text='database maintenance') # Add the tab
tab2 = ttk.Frame(tabControl) # Add a second tab
tabControl.add(tab2, text='mining target') # Make second tab visible
tabControl.pack(expand=1, fill="both") # Pack to make visible
#
# configuration area
#
area_config = ttk.LabelFrame(tab1, text=' configure ')
area_config.grid(column=0, row=0, padx=8, pady=4)
ttk.Label(area_config, text="Start Date:").grid(column=0, row=0, sticky=tk.W)
start_date = tk.StringVar()
start_Entry = DateEntry(area_config, font='Arial 12', width=12, background='darkblue',
foreground='white', borderwidth=2, date_pattern='y-mm-dd', textvariable=start_date)
start_Entry.grid(column=1, row=0, sticky=tk.W)
ttk.Label(area_config, text="End Date:").grid(column=0, row=1, sticky=tk.W)
end_date = tk.StringVar()
end_Entry = DateEntry(area_config, font='Arial 12', width=12, background='darkblue',
foreground='white', borderwidth=2, date_pattern='y-mm-dd', textvariable=end_date)
end_Entry.grid(column=1, row=1, sticky=tk.W)
def getDirName():
fDir = os.path.dirname('__file__')
fName = fd.askdirectory(parent=win, title='choose stock data dir', initialdir=fDir)
filedir.set(fName)
def getDatabaseFileName():
fDir = os.path.dirname('__file__')
fName = fd.askopenfilename(parent=win, title='select database file', initialdir=fDir)
dbfilename.set(fName)
def checkDataDir():
if not os.path.exists(filedir.get()+'/twse'):
check_datadir_l.configure(text='directory is not created')
return
if not os.path.exists(filedir.get()+'/tpex'):
check_datadir_l.configure(text='directory is not created')
return
filelist=os.listdir(filedir.get()+'/twse')
total_twse_item=len(filelist)
if (total_twse_item==0):
check_datadir_l.configure(text='directory is empty')
return
filelist.sort()
firstord=date.fromisoformat(filelist[0][:4]+'-'+filelist[0][4:6]+'-'+filelist[0][6:8]).toordinal()
lastord=date.fromisoformat(filelist[-1][:4]+'-'+filelist[-1][4:6]+'-'+filelist[-1][6:8]).toordinal()
if (firstord==0):
check_datadir_l.configure(text='illegal file name in twse directory')
return
if ((lastord-firstord+1)==total_twse_item):
check_datadir_l.configure(text='file complete '+date.fromordinal(firstord).isoformat()+ \
' to '+date.fromordinal(lastord).isoformat())
return
check_datadir_l.configure(text=date.fromordinal(firstord).isoformat()+' to '+ \
date.fromordinal(lastord).isoformat()+' short of '+ \
str(total_twse_item-lastord+firstord-1)+' files')
return
def checkDatabase():
if not os.path.exists(dbfilename.get()):
check_db_l.configure(text='database file has not created yet')
return
# check data on 2330
conn=sqlite3.connect('d:/archive/stock/twstock.db')
cc=conn.execute('select orddate from datalog')
ordlist=cc.fetchall()
ordlist.sort()
check_db_l.configure(text='database from '+date.fromordinal(ordlist[0][0]).isoformat()+ \
' to '+date.fromordinal(ordlist[-1][0]).isoformat())
conn.close()
def checkAVGstatus():
if not os.path.exists(dbfilename.get()):
check_avg_l.configure(text='database file has not created yet')
return
# check data on 2330
conn=sqlite3.connect('d:/archive/stock/twstock.db')
cc=conn.execute('select orddate from stock where stock_id is 2330 and avg_5 is not NULL')
ordlist=cc.fetchall()
ordlist.sort()
check_avg_l.configure(text='Averages valid from '+date.fromordinal(ordlist[0][0]).isoformat()+ \
' to '+date.fromordinal(ordlist[-1][0]).isoformat())
conn.close()
ttk.Button(area_config, text="data dir", command=getDirName).grid(column=0, row=2, sticky=tk.W)
filedir = tk.StringVar()
filedirLen = 40
filedirEntry = ttk.Entry(area_config, width=filedirLen,textvariable=filedir)
filedirEntry.grid(column=1, row=2, sticky=tk.W)
ttk.Button(area_config, text="database file", command=getDatabaseFileName).grid(column=0, row=3, stick=tk.W)
dbfilename = tk.StringVar()
dbfilenameLen=40
dbfilenameEntry = ttk.Entry(area_config, width=dbfilenameLen, textvariable=dbfilename)
dbfilenameEntry.grid(column=1, row=3, sticky=tk.W)
ttk.Button(area_config, text="check data dir", command=checkDataDir).grid(column=0, row=4, sticky=tk.W)
check_datadir_l=ttk.Label(area_config, text='--')
check_datadir_l.grid(column=1, row=4, padx=8, sticky=tk.W)
ttk.Button(area_config, text="check database", command=checkDatabase).grid(column=0, row=5, sticky=tk.W)
check_db_l=ttk.Label(area_config, text='--')
check_db_l.grid(column=1, row=5, padx=8, sticky=tk.W)
ttk.Button(area_config, text="check averages", command=checkAVGstatus).grid(column=0, row=6, sticky=tk.W)
check_avg_l=ttk.Label(area_config, text='--')
check_avg_l.grid(column=1, row=6, padx=8, sticky=tk.W)
#
# database maintenance routines
#
def crawl_date(orddate, save_dir):
datestr = date.fromordinal(orddate).strftime("%Y%m%d")
fetchstr = 'http://www.twse.com.tw/exchangeReport/MI_INDEX?response=csv&date=' + datestr + '&type=ALLBUT0999'
r = requests.post(fetchstr)
f = open(save_dir+"/twse/"+datestr, "wt", encoding="utf8", newline='\n')
f.write(r.text)
f.close()
datestr2 = date.fromordinal(orddate).strftime("%Y/%m/%d")
datestr2 = datestr2.replace(datestr2[0:4], str(int(datestr2[0:4])-1911))
fetchstr = 'http://www.tpex.org.tw/web/stock/aftertrading/daily_close_quotes/stk_quote_download.php?l=zh-tw&d=' + datestr2 + '&s=0,asc,0'
r = requests.post(fetchstr)
f = open(save_dir+"/tpex/"+datestr, "wt", encoding="utf8", newline='\n')
f.write(r.text)
f.close()
return 0
def create_db_file(dbfname):
conn = sqlite3.connect(dbfname)
c = conn.cursor()
c.execute("CREATE TABLE stock ( \
stock_id text, \
orddate int, \
name text, \
shares int, \
transactions int, \
amount int, \
open real, \
high real, \
low real, \
close real, \
diff real, \
lastbuy real, \
lastbuys int, \
lastsell real, \
lastsells int, \
eps real, \
allshares int, \
deviation real, \
avg_5 real, \
avg_10 real, \
avg_20 real, \
avg_60 real, \
PRIMARY KEY (stock_id, orddate) \
)")
conn.commit()
c.execute("CREATE TABLE datalog (\
orddate int PRIMARY KEY,\
strdate text, \
twse_update int,\
tpex_update int\
)")
conn.commit()
conn.close()
def update_twse_db(orddate, datapath):
datestr=date.fromordinal(orddate).strftime("%Y%m%d")
f=open(datapath+"/twse/"+datestr, "rt", encoding="utf8")
content=f.read()
f.close()
# if content is empty, the stock market is not open
if content=='':
return None
# clear all '=' in some lines
content=content.replace('=','')
# filter all lines without 10 fields of data
lines=content.split('\n')
lines=list(filter(lambda l:len(l.split('",')) > 10, lines))
# join all lines with carriage return
content = "\n".join(lines)
# use pd to read content as csv file
df=pd.read_csv(StringIO(content))
# remove all element str and remove ','
df = df.astype(str)
df = df.apply(lambda s: s.str.replace(',', ''))
# add 'date' field to the sheet
df['orddate']=pd.to_numeric(orddate)
df = df.rename(columns={'證券代號':'stock_id'})
df = df.rename(columns={'證券名稱':'name'})
df = df.rename(columns={'成交股數':'shares'})
df = df.rename(columns={'成交筆數':'transactions'})
df = df.rename(columns={'成交金額':'amount'})
df = df.rename(columns={'開盤價':'open'})
df = df.rename(columns={'最高價':'high'})
df = df.rename(columns={'最低價':'low'})
df = df.rename(columns={'收盤價':'close'})
df = df.rename(columns={'漲跌(+/-)':'sign'})
df = df.rename(columns={'漲跌價差':'diff'})
df = df.rename(columns={'最後揭示買價':'lastbuy'})
df = df.rename(columns={'最後揭示買量':'lastbuys'})
df = df.rename(columns={'最後揭示賣價':'lastsell'})
df = df.rename(columns={'最後揭示賣量':'lastsells'})
df = df.rename(columns={'本益比':'eps'})
# drop stock id not equal to 4 chars
df = df[df.stock_id.str.len()==4]
# set index as stock_id and date
df = df.set_index(['stock_id', 'orddate'])
df = df.apply(lambda ss:pd.to_numeric(ss, errors='coerce') if ((ss.name!='name') and (ss.name!='sign')) else ss)
# remove the last empty field
# df = df[df.columns[df.isnull().all() == False]]
df = df.drop(columns=['Unnamed: 16','sign'])
return df
def update_tpex_db(orddate, datapath):
datestr=date.fromordinal(orddate).strftime("%Y%m%d")
f=open(datapath+"/tpex/"+datestr, "rt", encoding="utf8")
# print("fetching ", datestr)
content=f.read()
f.close()
# clear all '=' in some lines
content=content.replace('=','')
# filter all lines without 10 fields of data
lines=content.split('\n')
lines=list(filter(lambda l:len(l.split(',')) > 10, lines))
# join all lines with carriage return
content = "\n".join(lines)
# if content is less than 100 lines, the stock is not open
if content.count('\n')<100:
return None
# use pd to read content as csv file
df=pd.read_csv(StringIO(content))
# remove all element str and remove ','
df = df.astype(str)
df = df.apply(lambda s: s.str.replace(',', ''))
# add 'date' field to the sheet
df['orddate']=pd.to_numeric(orddate)
# remove all space char in column name
for i in df.columns:
df = df.rename(columns={i : i.replace(' ', '')})
df = df.rename(columns={'代號':'stock_id'})
df = df.rename(columns={'名稱':'name'})
df = df.rename(columns={'收盤':'close'})
df = df.rename(columns={'漲跌':'skip_1'})
df = df.rename(columns={'開盤':'open'})
df = df.rename(columns={'最高':'high'})
df = df.rename(columns={'最低':'low'})
df = df.rename(columns={'均價':'skip_2'})
df = df.rename(columns={'成交股數':'shares'})
df = df.rename(columns={'成交金額(元)':'amount'})
df = df.rename(columns={'成交筆數':'transactions'})
df = df.rename(columns={'最後買價':'lastbuy'})
df = df.rename(columns={'最後賣價':'lastsell'})
df = df.rename(columns={'發行股數':'allshares'})
df = df.rename(columns={'次日參考價':'skip_3'})
df = df.rename(columns={'次日漲停價':'skip_4'})
df = df.rename(columns={'次日跌停價':'skip_5'})
# drop stock id not equal to 4 chars (including '代號')
df = df[df.stock_id.str.len()==4]
# set index as stock_id and date
df = df.set_index(['stock_id', 'orddate'])
# convert all field to numbers except name
df = df.apply(lambda s:pd.to_numeric(s, errors='coerce') if (s.name!='name') else s)
# drop all columns to be skipped
df = df.drop(columns=['skip_1','skip_2','skip_3','skip_4','skip_5'])
return df
def check_workingday(conn, orddate):
cc=conn.execute('select strdate from datalog where orddate='+str(orddate))
res=cc.fetchone()
if res!=None:
return True
return False
def get_price(conn, orddate, st_id): # return price, if no value, return -1.0, if pass database limit return 0.0
if (orddate<=dblastorddate):
return 0.0
cc=conn.execute('select close from stock where orddate is '+str(orddate)+' and stock_id is '+st_id)
res=cc.fetchone()
if res==None:
return -1.0
if res[0]==None:
#print('get a NaN in orddate', orddate,'stock id',st_id)
return -1.0
return res[0]
def cal_averages(dque):
sum=0.0
for i in range(0,61):
if i==5: # 5 days average
avg5=sum/5.0
if i==10: # 10 days average
avg10=sum/10.0
if i==20: # 20 days average
avg20=sum/20.0
if i==60: # 60 days average
avg60=sum/60.0
continue
sum=sum+dque[i][1]
return (avg5, avg10, avg20, avg60)
def cal_deviation(dque, avg20):
sum=0.0
for i in range(0, 20):
diff = dque[i][1]-avg20
sum=sum+abs(diff*diff)
return math.sqrt(sum/20.0)
#
# database maintenance area
#
def fetch_data():
print("fetch stock data")
# ensure dir for twse and tpex exist
if not os.path.exists(filedir.get()+'/twse'):
os.mkdir(filedir.get()+'/twse')
if not os.path.exists(filedir.get()+'/tpex'):
os.mkdir(filedir.get()+'/tpex')
d1=date.fromisoformat(start_date.get())
d2=date.fromisoformat(end_date.get())
d_start=d1.toordinal()
d_end=d2.toordinal()
print("fetch date ",d_start, d_end)
total_d = d_end - d_start + 1;
fetch_data_l.configure(text='fetching data')
for ordd in range(d_start, d_end+1):
status_line_l.configure(text='fetching '+date.fromordinal(ordd).isoformat())
progress_b["value"]=int((ordd-d_start+1)*100/total_d)
progress_b.update()
crawl_date(ordd,filedir.get())
time.sleep(10)
fetch_data_l.configure(text='done '+start_date.get()+' to '+end_date.get())
status_line_l.configure(text='idle')
progress_b["value"]=0
progress_b.update()
def update_database():
print("update database")
# make sure db file exists
if not os.path.exists(dbfilename.get()):
create_db_file(dbfilename.get())
d1=date.fromisoformat(start_date.get())
d2=date.fromisoformat(end_date.get())
d_start=d1.toordinal()
d_end=d2.toordinal()
print("fetch date ",d_start, d_end)
total_d = d_end - d_start + 1;
update_db_l.configure(text='updating data')
conn=sqlite3.connect(dbfilename.get())
for ordd in range(d_start, d_end+1):
status_line_l.configure(text='updating '+date.fromordinal(ordd).isoformat())
progress_b["value"]=int((ordd-d_start+1)*100/total_d)
progress_b.update()
# test if datalog contains orddate data
cc = conn.execute("select twse_update from datalog where orddate="+str(ordd))
res = cc.fetchone()
if res!=None :
continue
# this orddate data is not updated yet, go on update the database
df=update_twse_db(ordd, filedir.get())
# if the content is empty, the day is off, skip
if (type(df)!=pd.core.frame.DataFrame):
continue
df.to_sql('stock', conn, if_exists='append')
# updata datalog for orddate
dd=date.fromordinal(ordd)
cc = conn.execute("insert into datalog (orddate, strdate, twse_update) values ("+str(ordd)+","+dd.strftime('%Y%m%d')+",1)")
conn.commit()
df=update_tpex_db(ordd, filedir.get())
if (type(df)!=pd.core.frame.DataFrame):
continue
df.to_sql('stock', conn, if_exists='append')
# update datalog for orddate
cc = conn.execute("update datalog set tpex_update = 1 where orddate="+str(ordd))
conn.commit()
conn.close()
update_db_l.configure(text='done '+start_date.get()+' to '+end_date.get())
status_line_l.configure(text='idle')
progress_b["value"]=0
progress_b.update()
def cal_averages():
print("cal averages")
d1=date.fromisoformat(start_date.get())
d2=date.fromisoformat(end_date.get())
d_start=d1.toordinal()
d_end=d2.toordinal()
lastdate=date.fromisoformat(dblastdate[:4]+'-'+dblastdate[4:6]+'-'+dblastdate[6:8])
dblastorddate=lastdate.toordinal()
total_d = d_end - d_start + 1;
cal_avgs_l.configure(text='calculating averages')
dbconn=sqlite3.connect(dbfilename.get())
start_day=d_end
# find the first working day
while (check_workingday(dbconn, start_day)==False):
start_day=start_day-1
if (start_day<d_start):
print('no working days within the range')
cal_avgs_l.configure(text='no working days found')
return -1
print("start day=",start_day)
cc=dbconn.execute('select stock_id from stock where orddate is '+str(start_day))
st_idlist=cc.fetchall()
for st_id in st_idlist:
#for st_id in (('2377',),):
print(st_id)
cur_day=start_day
dque=deque()
# fill up 60 working days' data
for j in range(0,60):
price=-1.0
while (price<0.0):
price=get_price(dbconn, cur_day, st_id[0])
if (price<0.0):
cur_day=cur_day-1
dque.append((cur_day, price))
cur_day=cur_day-1
avgs=cal_averages(dque) # return avg5, avg10, avg20, avg60
deviation=cal_deviation(dque, avgs[2])
if (dque[0][0]>=d_start):
#update database
updatestr='update stock set avg_5='+str(avgs[0])+', avg_10='+str(avgs[1])+\
', avg_20='+str(avgs[2])+', avg_60='+str(avgs[3])+\
', deviation='+str(deviation)+' where orddate is '+str(dque[0][0])+\
' and stock_id is "'+st_id[0]+'"'
dbconn.execute(updatestr)
dbconn.commit()
while (dque[0][0]> d_start): # if the queue top day is still larger than loop end
status_line_l.configure(text='updating '+date.fromordinal(dque[0][0]).isoformat())
progress_b["value"]=100-int((dque[0][0]-d_start+1)*100/total_d)
progress_b.update()
# shift one day
dque.popleft()
price=-1.0
while (price<0.0):
price=get_price(dbconn, cur_day, st_id[0])
if (price<0.0):
cur_day=cur_day-1
dque.append((cur_day, price))
cur_day=cur_day-1
avgs=cal_averages(dque)
deviation=cal_deviation(dque, avgs[2])
if (dque[0][0]>=d_start):
#update database
updatestr='update stock set avg_5='+str(avgs[0])+', avg_10='+str(avgs[1])+\
', avg_20='+str(avgs[2])+', avg_60='+str(avgs[3])+\
', deviation='+str(deviation)+' where orddate is '+str(dque[0][0])+\
' and stock_id is "'+st_id[0]+'"'
dbconn.execute(updatestr)
dbconn.commit()
dbconn.close()
cal_avgs_l.configure(text='done '+start_date.get()+' to '+end_date.get())
status_line_l.configure(text='idle')
progress_b["value"]=0
progress_b.update()
return 0
# update area UI part
area_update = ttk.LabelFrame(tab1, text=' maintenance ')
area_update.grid(column=0, row=4, padx=8, pady=4, sticky=tk.W)
ttk.Button(area_update, text='fetch data', command=fetch_data).grid(column=0, row=0, sticky=tk.W)
fetch_data_l=ttk.Label(area_update, text='--')
fetch_data_l.grid(column=1, row=0, padx=8, sticky=tk.W)
ttk.Button(area_update, text='update database', command=update_database).grid(column=0, row=1, sticky=tk.W)
update_db_l=ttk.Label(area_update, text='--')
update_db_l.grid(column=1, row=1, padx=8, sticky=tk.W)
ttk.Button(area_update, text='calculate AVGs', command=cal_averages).grid(column=0, row=2, sticky=tk.W)
cal_avgs_l=ttk.Label(area_update, text='--')
cal_avgs_l.grid(column=1, row=2, padx=8, sticky=tk.W)
#
# status area
#
area_status = ttk.LabelFrame(tab1, text=' status ')
area_status.grid(column=0, row=8, padx=8, pady=4, sticky=tk.W)
ttk.Label(area_status, text="Status :").grid(column=0, row=0, sticky=tk.W)
status_line_l=ttk.Label(area_status, text='idle')
status_line_l.grid(column=1, row=0, sticky=tk.W)
ttk.Label(area_status, text="Progress :").grid(column=0, row=1, sticky=tk.W)
progress_b = ttk.Progressbar(area_status, orient='horizontal', length=300, mode='determinate')
progress_b.grid(column=1, row=1)
# Tab Control 2 refactoring ---------------------------------------------------------
# We are creating a container frame to hold all other widgets -- Tab2
mighty2 = ttk.LabelFrame(tab2, text=' The Snake ')
mighty2.grid(column=0, row=0, padx=8, pady=4)
# Creating three checkbuttons
chVarDis = tk.IntVar()
check1 = tk.Checkbutton(mighty2, text="Disabled", variable=chVarDis, state='disabled')
check1.select()
check1.grid(column=0, row=0, sticky=tk.W)
chVarUn = tk.IntVar()
check2 = tk.Checkbutton(mighty2, text="UnChecked", variable=chVarUn)
check2.deselect()
check2.grid(column=1, row=0, sticky=tk.W)
chVarEn = tk.IntVar()
check3 = tk.Checkbutton(mighty2, text="Enabled", variable=chVarEn)
check3.deselect()
check3.grid(column=2, row=0, sticky=tk.W)
# GUI Callback function
def checkCallback(*ignoredArgs):
# only enable one checkbutton
if chVarUn.get(): check3.configure(state='disabled')
else: check3.configure(state='normal')
if chVarEn.get(): check2.configure(state='disabled')
else: check2.configure(state='normal')
# trace the state of the two checkbuttons
chVarUn.trace('w', lambda unused0, unused1, unused2 : checkCallback())
chVarEn.trace('w', lambda unused0, unused1, unused2 : checkCallback())
# First, we change our Radiobutton global variables into a list
colors = ["Blue", "Gold", "Red"]
# We have also changed the callback function to be zero-based, using the list
# instead of module-level global variables
# Radiobutton Callback
def radCall():
radSel=radVar.get()
if radSel == 0: mighty2.configure(text='Blue')
elif radSel == 1: mighty2.configure(text='Gold')
elif radSel == 2: mighty2.configure(text='Red')
# create three Radiobuttons using one variable
radVar = tk.IntVar()
# Next we are selecting a non-existing index value for radVar
radVar.set(99)
# Now we are creating all three Radiobutton widgets within one loop
for col in range(3):
curRad = tk.Radiobutton(mighty2, text=colors[col], variable=radVar,
value=col, command=radCall)
curRad.grid(column=col, row=1, sticky=tk.W) # row=6
# Add a Progressbar to Tab 2
progress_bar = ttk.Progressbar(tab2, orient='horizontal', length=286, mode='determinate')
progress_bar.grid(column=0, row=3, pady=2)
# update progressbar in callback loop
def run_progressbar():
progress_bar["maximum"] = 100
for i in range(101):
sleep(0.05)
progress_bar["value"] = i # increment progressbar
progress_bar.update() # have to call update() in loop
progress_bar["value"] = 0 # reset/clear progressbar
def start_progressbar():
progress_bar.start()
def stop_progressbar():
progress_bar.stop()
def progressbar_stop_after(wait_ms=1000):
win.after(wait_ms, progress_bar.stop)
# Create a container to hold buttons
buttons_frame = ttk.LabelFrame(mighty2, text=' ProgressBar ')
buttons_frame.grid(column=0, row=2, sticky='W', columnspan=2)
# Add Buttons for Progressbar commands
ttk.Button(buttons_frame, text=" Run Progressbar ", command=run_progressbar).grid(column=0, row=0, sticky='W')
ttk.Button(buttons_frame, text=" Start Progressbar ", command=start_progressbar).grid(column=0, row=1, sticky='W')
ttk.Button(buttons_frame, text=" Stop immediately ", command=stop_progressbar).grid(column=0, row=2, sticky='W')
ttk.Button(buttons_frame, text=" Stop after second ", command=progressbar_stop_after).grid(column=0, row=3, sticky='W')
for child in buttons_frame.winfo_children():
child.grid_configure(padx=2, pady=2)
for child in mighty2.winfo_children():
child.grid_configure(padx=8, pady=2)
# Exit GUI cleanly
def _quit():
win.quit()
win.destroy()
exit()
# Creating a Menu Bar
menu_bar = Menu(win)
win.config(menu=menu_bar)
# Add menu items
file_menu = Menu(menu_bar, tearoff=0)
file_menu.add_command(label="New")
file_menu.add_separator()
file_menu.add_command(label="Exit", command=_quit)
menu_bar.add_cascade(label="File", menu=file_menu)
# Display a Message Box
def _msgBox():
msg.showinfo('Python Message Info Box', 'A Python GUI created using tkinter:\nThe year is 2017.')
# Add another Menu to the Menu Bar and an item
help_menu = Menu(menu_bar, tearoff=0)
help_menu.add_command(label="About", command=_msgBox) # display messagebox when clicked
menu_bar.add_cascade(label="Help", menu=help_menu)
#======================
# Start GUI
#======================
print('argv0=',sys.argv[0],sys.argv[1])
win.mainloop()
沒有留言:
張貼留言