python tkinter動態追加按鈕等控件可能遇到的問題

  小爬最近給同事製做一個小爬蟲:具體要求:python

一、天天自動定時觸發;web

二、模擬用戶自動登錄;json

三、自動爬取對應API接口數據;windows

4、對爬取結果進行邏輯判斷,對符合條件的數據進行規則化列示;瀏覽器

5、列示的行項目支持超連接,若是用用戶已經經過瀏覽器登錄過,該超連接須要能支持單擊後在瀏覽器內新建選項卡並直接進入對應的表單,無需再次登錄。安全

 

  小爬思考了下:整個程序的功能實現中,2、3、4步驟涉及的功能在先前小爬編寫的一些自動化工具中已經有實現過,因此與小爬而言,核心問題就是步驟1和5;通過翻閱相關資料,「天天自動定時觸發」能夠經過Windows標準的「任務計劃程序"來實現:cookie

  該功能能夠實現 按特定頻次自動觸發特定程序或腳本,且支持傳參,配置過程很是簡單易用;session

惟一的問題,若是經由此功能啓動特定腳本,則經過"os.getcwd()"獲得的路徑是"C:\windows\system32",而不是腳本自身所在的路徑,可能會與您的預期不一致;因此若是您的腳本中涉及調用一個同目錄下的excel文件,那麼如何自動得到該"excel"的路徑呢,能夠經過下面的代碼實現:app

1 filepath=os.path.abspath(sys.argv[0])
2 filepath=os.path.dirname(filepath)

 

  至於tkinter怎麼動態追加按鈕並附上超連接事件,中間涉及到兩個問題:工具

1.按鈕數量不固定,如何動態追加,按鈕名怎麼動態生成;

2.按鈕如何綁定事件,進行超連接到系統默認瀏覽器.

問題1的python其中一種解決方法是:

1 names=locals()
2 for i in range(len(afFormNumberList)): 
3   names["link%s"%i]=tk.Button(window,text="%s\t%s\t%s\t%s\n\n"%(afFormNumberList[i],afPersonNameList[i],failDateList[i],afFromUrlList[i]),font=('微軟雅黑', 10))
 4   names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[i]))
5   names["link%s"%i].pack()    

上面的代碼中,當咱們對這些button進行事件綁定且動態傳參時,若是咱們綁定的url中動態引用了變量i,則事件不會立刻觸發event,而是在用戶點擊該button時纔會進行事件響應,此時,i的值永遠是循環的最後一個數,那麼咱們動態生成的全部的button連接的地址或者說事件永遠是同樣的,這顯然不是咱們想要的結果,咱們可能但願的結果是:

 1 if i==0:
 2     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[0]))
 3 elif i==1:
 4     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[1]))
 5 elif i==2:
 6     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[2]))
 7 elif i==3:
 8     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[3]))
 9 elif i==4:
10     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[4]))
11 elif i==5:
12     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[5]))
13 elif i==6:
14     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[6]))
15 elif i==7:
16     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[7]))
17 elif i==8:
18     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[8]))
19 elif i==9:
20     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[9]))
21 elif i==10:
22     names["link%s"%i].bind("<Button-1>",lambda event:open_url(event,afFromUrlList[10]))

上段代碼經過變量指定,能有效解決超連接永遠爲最後一個的問題,可是這段代碼過於冗長,實現方式太笨,且這種寫法是假定按鈕動態追加的數量小於預設值.缺少靈活性!

小爬互聯網上找了一圈,最終在stackoverflow中找到了想要的答案:https://stackoverflow.com/questions/20596892/disabling-buttons-after-click-in-tkinter

  這段代碼中的核心在於"n=letters[index]",隨着每次的index遞增,咱們的n值是不同的.也就是按鈕動態增長的過程當中,每次command中綁定的n是不同的,而不是永遠的最後一個"i",另外的巧妙之處在於,代碼將button對象追加到一個buttons的空列表中,這樣,隨着index的遞增,咱們的buttons[index]就能夠對應不一樣的button.那就是不一樣index跟不一樣button之間造成了一一映射關係.這纔是這個問題解決的關鍵.

最後生成的gui界面以下:

 

 經過點擊該button,能夠自動打開對應表單(若是瀏覽器已完成該門戶的登錄,則無需再次驗證登錄,而經過office系列軟件集成的超連接在面對此問題時,不管以前是否已登陸,都會生成新的cookie和session,用戶須要再次登錄,這是出於安全考慮,可是操做步驟變得繁瑣).

上文中提到自動化工具的示例代碼以下,供諸位參考,一塊兒學習進步:

  1 #!/usr/bin/env python
  2 # -*- coding: utf-8 -*-
  3 # @Time    : 2019/7/09 17:15
  4 # @Author  : New June
  5 # @Desc    :
  6 # @Software: PyCharm
  7 # @修改日期:2019-07-09.內容:支持水泥臨時授信業務到期單據的提醒、存檔、單進程
  8 # @修改日期:2019-07-11.內容:支持對信息展現,單擊對應行能超連接至對應表單頁
  9 # @修改日期:2019-07-17.內容:改進代碼,使代碼可以對未知數量的button進行事件響應,不須要窮舉,且支持button點擊後置灰,不可用,與未點擊的button區分
 10 
 11 
 12 import time,re,datetime,csv,requests,json,os,easygui,sys
 13 from requests.exceptions import ConnectionError
 14 import webbrowser
 15 import tkinter as tk
 16 
 17 if __name__ == '__main__':
 18     #記得登錄後時間起點,進入爬取過程
 19     #print(os.getcwd())
 20     def open_url(index,url):
 21         #print(event.x,event.y)
 22         #url=buttons[index]["text"].split(" ")[1]
 23         buttons[index].config(state="disabled") #禁用事件
 24         webbrowser.open("%s"%url, new=0) #打開瀏覽器
 25 
 26     filepath=os.path.abspath(sys.argv[0])
 27     filepath=os.path.dirname(filepath)
 28     filepath=filepath+"\\水泥臨時授信存檔.txt"
 29     #print(filepath)
 30     with open(filepath,"r",encoding='UTF-8-sig') as t:
 31         history=t.read().strip()
 32     txt=open(filepath,"a",encoding='UTF-8')
 33     username="yourusername"
 34     psw="password"
 35     
 36     #today=datetime.date.today()
 37     afFormNumberList=[]
 38     afPersonNameList=[]
 39     afFromUrlList=[]
 40     failDateList=[]
 41     #business="水泥(臨時授信)"
 42 
 43     """登錄,拿到登錄後的session"""
 44     loginData={'redirect':'','username':username,'password':psw.lower()}
 45     session=requests.sessions.Session()
 46     response=session.post('http://someurl.com/portal/u/a/login.do',loginData)
 47     text=response.text
 48     if "密碼不匹配" in text:
 49         easygui.msgbox("OA登錄密碼不正確!")
 50         sys.exit(0)
 51     '''過後抽查-事中'''
 52     data_search={
 53         'page':1,
 54         'rows':100,
 55         'condition':
 56         """[{"column":"DELETE_STATUS","exp":"=","value":0},{"column":"CREDIT_TYPE","exp":"like","value":"cement-0"},{"column":"AF_STATUS","exp":"=","value":1},{"column":"CREDIT_TYPE","orderType":"default","orderKey":"","direction":"ASC"}]""",
 57         'additionalParams':'{}'
 58     }      
 59     try:
 60         response=session.post(url="http://someurl.com/mdm/af/credit/list.do",data=data_search)
 61         if response.status_code==200:
 62             pageContent = response.json()
 63             for element in pageContent['rows']:
 64                 temporaryStatus=element['temporaryStatus'] #SAP狀態,1表明成功,0表明未處理
 65                 failDate=element['failDate'] #授信失效日期  臨時授信期限
 66                 afFormNumber=element['afFormNumber']
 67                 #if failDate == str(datetime.date.today()-datetime.timedelta(days=1)) and temporaryStatus=="1": #失效日期在昨天,且SAP狀態爲成功
 68                 if temporaryStatus=="1" and failDate < str(datetime.date.today()) and afFormNumber not in history:
 69                     txt.writelines("%s"%afFormNumber)
 70                     txt.write("\n")
 71                     afFormNumberList.append(afFormNumber) #表單號 
 72                     afFormId=element['id']
 73                     afPersonNameList.append(element['afPersonName'])
 74                     failDateList.append(failDate)
 75                     afFromUrlList.append("http://someurl.com/mdm/af/credit/temporaryForm.do?_hf_data_id=%s"%afFormId) #表單網址
 76             if afFormNumberList:
 77                 # 創建窗口window
 78                 window = tk.Tk()               
 79                 # 給窗口的可視化起名字
 80                 window.title('臨時授信到期信息')                
 81                 # 設置窗口的居中顯示
 82                 screenwidth = window.winfo_screenwidth()
 83                 screenheight = window.winfo_screenheight()
 84                 width = 1080
 85                 height = 700
 86                 size = "%dx%d+%d+%d" % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
 87                 # 設定窗口的大小(長 * 寬)
 88                 window.geometry(size)
 89                 names=locals()
 90                 navigation=tk.Label(window,text="表單編號\t申請人\t臨時授信期限\t表單url地址\n", justify="left",font=('微軟雅黑', 14,"bold"),fg="#8B008B")
 91                 navigation.pack()
 92                 #letters=["A", "T", "D", "M", "E", "A", "S", "R", "M"]  #https://stackoverflow.com/questions/20596892/disabling-buttons-after-click-in-tkinter
 93                 buttons=[]
 94                 for i in range(len(afFormNumberList)): 
 95                     url=afFromUrlList[i]
 96                     index=i
 97                     button =tk.Button(window,text="%s\t%s\t%s\t %s\n\n"%(afFormNumberList[i],afPersonNameList[i],failDateList[i],afFromUrlList[i]),font=('微軟雅黑', 10),command=lambda index=index,url=url:open_url(index,url))
 98                     
 99                     #names["link%s"%i].place(x=20,y=100*(i+1)) 
100                     button.pack()
101                     #window.update()  
102                     buttons.append(button)            
103                 txt.close()
104                 window.mainloop()
105             else:
106                 txt.close()
107                 easygui.msgbox("今天沒有授信到期單據!")
108     except requests.ConnectionError as e:
109         print('Error',e.args)
相關文章
相關標籤/搜索