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


  小爬最近給同事制作一個小爬蟲:具體要求:

1、每天自動定時觸發;

2、模擬用戶自動登陸;

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

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

5、列示的行項目支持超鏈接,如果用用戶已經通過瀏覽器登陸過,該超鏈接需要能支持單擊后在瀏覽器內新建選項卡並直接進入對應的表單,無需再次登陸。

 

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

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

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

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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM