Tkinter app using selenium not responding after clicking run - python-3.x

I created an instagram bot that uses the selenium webdriver along with a tkinter interface. However when the browser is running, the app does not respond not allowing to close it midway or anything else. The tkinter app runs in a seperate class to the actual code
import tkinter as tk
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import threading
import random
import sys
import mysql.connector
from mysql.connector import errorcode
from datetime import datetime
from tkinter import *
from tkinter.ttk import *
from time import strftime
from tkinter import messagebox
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
# THIS VERSION IS V 1.1.0
# THIS UPDATE INCLUDES THE NEW LAYOUT FOR THE GUI USING TTKINTER
# NEW MENUS ARE INCLUDED IN THIS UPDATE
# ADDED NUMBER OF PICTURES TO LIKE OPTION
# NEW FORMAT
# FIXED BUGS EXCLUDING RANDOM REDIRECTION ISSUE
# Note: This works with Chromedriver V77 and Chrome V77
class StartPage(tk.Tk):
# Load application
def __init__(self, root):
root.title("Instagram Bot - Created By HudZah")
# Creating menubar
menubar = Menu(root)
print("Application loading, please wait")
print("When you install this application, you agree to the abide by the Terms And Conditions stated in the installtion folder. This software is not intended to be used for the exploitation of Instagram, rather as a means of promoting your product. Any detention or suspension that is resulted due to excessive use of this Bot is in no means the fault of the publisher, but the consequences faced by the user. It should be clear that this Bot should not be misused or overused, this might result in unknown penalties to be faced by the user." + "\n Created by HudZah")
Border = 3
canvas = tk.Canvas(root, height=600, width=700)
canvas.pack()
MainFrame = tk.Frame(root, bg="white")
MainFrame.place(relwidth=1, relheight=1)
# Adding file menu and commands
file = Menu(menubar, tearoff = 0)
menubar.add_cascade(label = "File", menu = file)
file.add_command(label ='New File', command = None)
file.add_command(label ='Run', command = lambda:GetUserInfo(Username.get(), Password.get(), List.get()))
file.add_command(label ='Save', command = None)
file.add_separator()
file.add_command(label ='Exit', command = root.destroy)
# Adding Edit Menu and commands
edit = Menu(menubar, tearoff = 0)
menubar.add_cascade(label ='Edit', menu = edit)
edit.add_command(label ='Cut', command = None)
edit.add_command(label ='Copy', command = None)
edit.add_command(label ='Paste', command = None)
edit.add_command(label ='Select All', command = None)
edit.add_separator()
edit.add_command(label ='Find...', command = None)
edit.add_command(label ='Find again', command = None)
# Adding Help Menu
help_ = Menu(menubar, tearoff = 0)
menubar.add_cascade(label ='Help', menu = help_)
help_.add_command(label ='Help', command = None)
help_.add_command(label ='Tutorials', command = None)
help_.add_separator()
help_.add_command(label ='About BOTIG', command = None)
UsernameFrame = tk.Frame(root, bg="lightgrey", bd=Border)
help_.add_command(label ='Information', command = None)
# Relative width to the canvas
UsernameFrame.place(relx=0.5, rely=0.135, relwidth=0.60,
relheight=0.1, anchor="n")
Username = tk.Entry(UsernameFrame, font=40)
Username.insert(0, "Username")
Username.place(relwidth=1, relheight=1)
Username.focus()
PasswordFrame = tk.Frame(root, bg="lightgrey", bd=Border)
PasswordFrame.place(relx=0.5, rely=0.26, relwidth=0.60,
relheight=0.1, anchor="n")
Password = tk.Entry(PasswordFrame, show="*", font=40)
Password.insert(0, "Password")
Password.place(relwidth=1, relheight=1)
ListFrame = tk.Frame(root, bg="lightgrey", bd=Border)
ListFrame.place(relx=0.5, rely=0.385, relwidth=0.60,
relheight=0.1, anchor="n")
List = tk.Entry(ListFrame, font=40)
List.insert(0, "Explore Hashtags, eg: food, nyc")
List.place(relwidth=1, relheight=1)
# Minutes
PicsInput = Spinbox(root, from_= 1, to = 2000, font = 13)
PicsInput.place(relx=0.5, rely=0.61, relwidth=0.14,
relheight=0.055, anchor = "n")
button = tk.Button(MainFrame, text="Run",
command=lambda:GetUserInfo(Username.get(), Password.get(), List.get(), PicsInput.get()))
button.place(relx=0.5, rely=0.71, relwidth=0.25,
relheight=0.085, anchor="n")
button = tk.Button(MainFrame, text="Quit",
command= root.destroy)
button.place(relx=0.5, rely=0.81, relwidth=0.25,
relheight=0.085, anchor="n")
label = tk.Label(root, text = "Created by HudZah © 2019", font = 13, bg = "white", fg = "black")
label.place(relx = 0.5, rely = 0.93, anchor = "n")
PicsInputLabel = tk.Label(root, text = "Pictures Per Hashtag", font = 7, bg = "white", fg = "black")
PicsInputLabel.place(relx = 0.5, rely = 0.55, relwidth = 0.3, anchor = "n")
# Create label to show errors to users and Info of bot when closed
root.config(menu = menubar)
#pyinstaller --add-binary="Documents/chromedriver.exe;." InstagramBotV1.1.0.py
class InstagramBot(tk.Tk):
# Run page
def __init__(self, username, password):
self.username = username
self.password = password
self.driver = webdriver.Chrome()
def login(self):
driver = self.driver
driver.get("https://www.instagram.com/")
time.sleep(2)
LoginButton = driver.find_element_by_xpath(
"//a[#href='/accounts/login/?source=auth_switcher']")
LoginButton.click()
time.sleep(5)
UsernameElem = driver.find_element_by_xpath(
"//input[#name='username']")
UsernameElem.clear()
UsernameElem.send_keys(self.username)
PasswordElem = driver.find_element_by_xpath(
"//input[#name='password']")
PasswordElem.clear()
PasswordElem.send_keys(self.password)
PasswordElem.send_keys(Keys.RETURN)
time.sleep(6)
def LikePhoto(self, hashtag, NumOfPics, pages):
driver = self.driver
driver.get("https://www.instagram.com/explore/tags/" + hashtag + "/")
time.sleep(3)
# gathering photos
PicLinks = []
print("Check : Number of pages are", pages)
for i in range(0,pages):
try:
driver.execute_script(
"window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2)
# get tags
Links = driver.find_elements_by_tag_name('a')
# finding relevant hrefs
Links = [elem.get_attribute('href') for elem in Links
if ".com/p/" in elem.get_attribute("href")]
for i in range (0,len(Links)):
if Links[i] not in PicLinks:
PicLinks.append(Links[i])
print(
"Check: Total N.of pic hrefs stored after array : " + str(len(PicLinks)) + " , in " + hashtag)
# print(Links)
except Exception:
continue
LikedPhotos = 0
for i in range(0, len(PicLinks)):
print("Num" , i , " : " , PicLinks[i])
# Liking photos
UniquePhotos = int(NumOfPics)
#for PicLinks in Links:
for i in range(0,int(NumOfPics)):
driver.get(PicLinks[i])
time.sleep(4)
driver.execute_script(
"window.scrollTo(0, document.body.scrollHeight);")
try:
time.sleep(random.randint(5, 9))
#driver.find_element_by_xpath("//class[#glyphsSpriteHeart__outline__24__grey_9 u-__7]").click()
driver.find_element_by_xpath('//span[#aria-label="Like"]').click() # Anonymous function
time.sleep(1)
UniquePhotos = UniquePhotos - 1
LikedPhotos = LikedPhotos + 1
print("Picture liked : ",LikedPhotos , " Pictures left : ", UniquePhotos)
except Exception:
time.sleep(2)
def closeBrowser(self):
self.driver.close()
print("Browser quit")
# Pass info to main class
def GetUserInfo(username, password, hashtagReceived, nOfPics):
if password and username and hashtagReceived:
if username != "Username" and password != "Password" and hashtagReceived != "Explore Hashtags, eg: food, nyc":
try:
if int(nOfPics) != 0:
if messagebox.askokcancel('Application','Application is running, press OK to continue') == True:
hashtags = []
username = username
password = password
pages = int(nOfPics)/10
pages = int(pages)
if pages < 1:
pages = 1
else:
pages = pages
#print("username is ", username, "ands password is", password)
# Split hashtags into an array
hashtags = hashtagReceived.split(",")
try:
print(hashtags)
IG = InstagramBot(username, password)
IG.login()
i = 0
except:
messagebox.showerror("Could not execute task. Please try again.")
try:
while i <= len(hashtags):
# Choose a random tag from the list of tags
tag = hashtags[i]
tag = tag.replace(" ", "")
IG.LikePhoto(tag, nOfPics, pages)
i = i + 1
except ValueError:
print("Value could not be converted to an integer.")
except Exception:
if i != len(hashtags):
IG.closeBrowser()
print("Browser crashed")
time.sleep(3)
IG = InstagramBot(username, password)
IG.login()
i = 0
elif i == len(hashtags):
print("Hashtags are out")
finally:
print("Program finished")
IG.closeBrowser()
else:
SystemExit()
else:
messagebox.showwarning("Error", "Please enter a valid number of posts to like")
print("Please enter a valid number of posts to like")
except Exception:
messagebox.showwarning("Error", "Please enter a valid number of posts to like")
print("Please enter a valid number of posts to like")
else:
messagebox.showwarning("Error", "Please enter suitable data")
print("Please enter suitable data")
else:
messagebox.showwarning('Error', 'Please enter a username or password') #shows warning message
print("Please enter a username and password")
if __name__ == "__main__":
print("Run from main")
root = tk.Tk()
Start = StartPage(root)
root.mainloop()
else:
print("Run from import")
The browser runs perfectly fine and the code works. However if you try and click any buttons on the app, it does not respond. It is supposed to run the browser as well as run the app simultaneously

The issue you're facing is because selenium and tkinter are running on the same thread/process, you need either use the threading module or subprocess module to run it, doing this your tkinter application will not be freezing anymore.
Threading documentation can be found here
Subprocess documentation here
You can find some implementations of threading on this link

Related

Python loop does not work accurately and reliably with time.sleep

What would be the best way to run a program once a day.
time.sleep is inaccurate on long runs.
I try to get def send_stock_data (self): run once a day, for example. time.sleep drifts too much for this purpose.
The program reads the excel file and sends the data by e-mail if the column 'Amount' is 0.
class progloop():
def prog_loop (self):
progress.pack_forget()
while run_cycle == True:
get_stock_and_send_mail()
sleep(48000)
continue
def __init__(self):#adds Threading to loop
t = threading.Thread(target=self.prog_loop)
t.start()
The above class currently handles the loop.
Below is the full script.
import threading
import tkinter as tk
import os
import smtplib
import datetime
import time
import pandas as pds
import traceback
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
from tkinter import BOTTOM,HORIZONTAL, Label, Menu, PhotoImage, messagebox, filedialog, ttk
from datetime import datetime
from time import sleep
root = tk.Tk()
sec = 0
action = None
run_cycle = True
stop_cycle = True
today = datetime.now()
clock_time = today.strftime('%H:%M:%S')
date = today.strftime('%d/%m/%Y')
weekday = datetime.today().strftime('%A')
def on_run():#start send_stock_data loop and timer
global run_cycle, action
global action
string_var.set('Application state: Running')
run_cycle = True
if not action:
timer()
def on_stop():#pause send_stock_data loop and timer
global run_cycle, action
string_var.set('Application state: Paused')
run_cycle = False
messagebox.showinfo('Attention','Program is paused, press Run to resume execution')
if action:
root.after_cancel(action)
action = None
def Info():#Info btn data
messagebox.showinfo('Application info','''-The application reads the stock balance from the excel file and sends out e-mail information if the part (s)
of the warehouse balance has run out of stock within the alarm limit.\n
-The default path to the excel file is specified in the Windows environment variables as 'FilePath'\n
-In case of problems contact the author
\n\nDefault alarm limit = 0 parts in stock
\n\nDefault Google smtp server:'smtp.gmail.com'\nDefault port: 587 ''')
def write_err_log():#writes an error log with traceback
traceback_err = traceback.format_exc()
date_time = [clock_time,date]
write_log = open("ErrorLog.txt", "a")
write_log.write(str(date_time)) and write_log.write(str(traceback_err))
write_log.close()
#Gui
string_var = tk.StringVar()
string_var.set('Application state: Waiting for commands')
root.iconbitmap(r'C:\\Users\\Lauri\\Visual_studio\\Bitmap_2.ico')
root.title('Warehouse assistant')
root.geometry('460x360')
root.resizable(False,False)
background_image = PhotoImage(file = r'C:\\Users\\Lauri\\Visual_studio\\BcImage.png')
background_label = tk.Label(image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
Label_header = ttk.Label(root, font=('underline', 12),text='Parts balance monitoring application').pack()
Label_start = ttk.Label(root,font=('underline',8), text='Press Run to start the application\nPress Pause to stop the application').pack()
Label_appState = ttk.Label(root, font=('underline', 10),textvariable=string_var).pack()
btnInfo = ttk.Button(root, text = 'Info', width= 15, command = Info).pack()
Start_btn = ttk.Button(root, text = 'Run', width= 15, command=lambda:[bar(),progloop(),root.after(1000,on_run())]).pack()
Stop_btn = ttk.Button(root, text = 'Pause', width= 15, command = on_stop).pack()
exit_btn = ttk.Button(root, text = 'Exit', width= 25, command =lambda:[root.after(1000,),root.destroy()]).pack(side=BOTTOM)
menu = Menu(root)
root.config(menu=menu)
contact_num = os.environ.get('Contact_number')
contact_email = os.environ.get('Contact_email')
helpmenu = Menu(menu)
menu.add_cascade(label='Contact', menu=helpmenu)
helpmenu.add_command(label=contact_email)
helpmenu.add_command(label=contact_num)
helpmenu.add_command(label= 'Exit', command=root.destroy)
progress = ttk.Progressbar(root, orient = HORIZONTAL,length = 158, mode = 'determinate')
def bar():#progressbar
progress['value'] = 5
root.update_idletasks()
root.after(50)
progress['value'] = 43
root.update_idletasks()
root.after(300)
progress['value'] = 68
root.update_idletasks()
root.after(50)
progress['value'] = 100
root.update_idletasks()
root.after(1)
progress.pack(side=BOTTOM)
class get_stock_and_send_mail: #send inventory balance information by email
def send_stock_data(self):
count = 0
pds.set_option('display.max_colwidth', -1)
pds.set_option('colheader_justify','left')
#reads excel file and searches for desired inventory balance value
while True:
file = os.environ.get('FilePath')#default path to file specified in the Windows environment variables as 'FilePath'
try:
dataFrame = pds.read_excel(file)
break
except PermissionError as error:
write_err_log()
messagebox.showerror(title='error',message= 'Permission Error Permission error has occured during read file?\n\n\n'+str(error)+"'.",
detail=traceback.format_exc())
except FileNotFoundError as error:
messagebox.showwarning(title='Select Excel File',
message='DEFAULT FILE NOT FOUND.\nChoose Excel file from directory.\nCONTINUE --> PRESS OK')
file = filedialog.askopenfilename()#manual path to file if default path doesn't exist
try:
dataFrame = pds.read_excel(file)
break
except FileNotFoundError:
write_err_log()
continue
df_bool = []
for Amount in dataFrame.Amount:
if Amount <= 0:
df_bool.append(True)
else:
df_bool.append(False)
if True in df_bool:
while run_cycle == True:
today = datetime.now()
clock_time = today.strftime('%H:%M:%S')
date = today.strftime('%d/%m/%Y')
weekday = datetime.today().strftime('%A')
Email_adress = os.environ.get('email_user')
Email_password = os.environ.get('email.password')
empty_stock = dataFrame.loc[dataFrame.Amount <= 0]
try:
with smtplib.SMTP('smtp.gmail.com',587) as server:
server.ehlo()
server.starttls()
server.ehlo_or_helo_if_needed()
server.set_debuglevel(1)
server.login(Email_adress,Email_password)
subject = 'no-reply'
body = f"""Row no.:{empty_stock}\nPart (s) is out of stock\n
Date: {weekday} {date}\n
Time: {clock_time}
\n\n\nThis was an automatically generated message.\nDo not reply to this message!
"""
msg = f'subject:{subject}\n\n{body}'
server.sendmail(Email_adress,Email_adress, msg)
server.quit
except smtplib.SMTPException as error:
write_err_log()
count +=1
if count <= 5:
continue
elif count >= 6:
messagebox.showerror(title='error',message= 'ERROR'+str(error)+"'.",
detail=traceback.format_exc())
break
def __init__(self):#adds Threading to loop
t = threading.Thread(target=self.send_stock_data)
t.start()
class progloop():
def prog_loop (self):
progress.pack_forget()
while run_cycle == True:
get_stock_and_send_mail()
sleep(48000)
continue
def __init__(self):#adds Threading to loop
t = threading.Thread(target=self.prog_loop)
t.start()
def timer(): #Runtime timer
global sec
if run_cycle == False:
return
sec += 1
time['text'] = f'Runtime: {sec} sec'
time.after(1000, timer)
time = Label(root, fg='black')
time.pack(side=BOTTOM)
root.mainloop()
If you really want to actually use a sleep, and time shifting/drifting over multiple days is the problem, what you can do, although not perfectly accurate, is to readjust the sleep time based on current time. Something like :
def seconds_since_midnight():
now = datetime.now()
return (now - now.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
def one_day_to(day_time_secs):
return 86400+day_time_secs-seconds_since_midnight()
And using for instance
sleep(one_day_to(3600*18))
to schedule at about 18h00 the next day, without drifting from day to day

Display messages in window tkinter

I am working on the following small tkinter application.
The URL & token is for a public development demo which can be found here if interested - https://snipe-it.readme.io/reference#api-overview
My current issue is displaying logging messages to the current tkinter window.
Currently it just prints to the console. I've tried a few different methods but I'm not very good with tkinter at the moment.
I have tried solutions like so - How do I create a popup window in tkinter? - but this just duplicates the main window each time.
Any help or pointers would be appreciated.
The csv file looks like so:
asset_tag,
1382613192,
1169063140,
785100692,
4565667567345457574535,
import csv
import logging
from datetime import datetime
from multiprocessing import Process, Queue
from tkinter import *
from tkinter import filedialog, ttk
from tkinter.messagebox import showinfo
import requests
#todo set initialdir as current user
# initiate logging module
logging.basicConfig(level=logging.INFO)
# set headers are they are used in several functions
headers = {
"Accept": "application/json",
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImVmMGVhY2Y4MjAyYzgwZWI2M2JkNmIwZDc0OGYwY2FkYzU2Y2ZlMzgyNzY4ODY0N2EwNmU4ZTBlNmYwZDgwODNjZmMyMzI2YWYyYTZlMTFkIn0.eyJhdWQiOiIxIiwianRpIjoiZWYwZWFjZjgyMDJjODBlYjYzYmQ2YjBkNzQ4ZjBjYWRjNTZjZmUzODI3Njg4NjQ3YTA2ZThlMGU2ZjBkODA4M2NmYzIzMjZhZjJhNmUxMWQiLCJpYXQiOjE0OTMzMzI2MjgsIm5iZiI6MTQ5MzMzMjYyOCwiZXhwIjoxODA4ODY1NDI4LCJzdWIiOiIyIiwic2NvcGVzIjpbXX0.NU7ZRIt-d4b0o8uv9ipo1vSWcg1svbmPp47kHErafm9iuK4FjygKd2_4Hp73HKAmjiYcEn3r39pwNh2t9BMFnTXv0KeDGC8zfZ9z7OJN_a59LPoarWBFzCsYETyAm-CeeFnfdj9Cr0ZeGOnnaPuWPYxicwKFeqJI4Hn8nCymcamDGE0u4WOO95ihGOAen4_fqpj-kkBDsvsGhB-cQxeuKdlbvO1yOsKmgQv-kQuxiFMn1zgU7P02mC6XXrbw6jTm7JOaBSbvqSwNtsrSKZkim1jxLsQ4dm36lFmeMkU6hZvNSUnxg8JwbmoxQ_3tZlG3IJh3Sc9ZUi-AEAQ4bbGzi_xNS9fenIdzLDaSiv_esYyNOYXqOuSBk8Yr-720N9OcVjGLnPrV3RtmPisV1aLFgKWLImtlyQgUq3d5LA3QXz8Q_8isvO9Am1u8ri2plbHGJLJ6GRW_mYcBEYMwUozaeXTUe_FUSSO8gpGtO9Hpa5SbERY272_tojyVXpYPaPdUYYmS9CP332jBNESPT8wGwpOM-iddeVo_n82w3dHmDEdp1Brbs3_vKk0AcgvDLsAbd4dZZO-UqddVx6SDb3HLw1Pmw1wGGYHA6w8wWQAiS9kg2xMcz5i75HOULaN3miqYvcPCvHpI2CBfuvdplI8QNm_XzFPmoQRu_5kR8knzla4",
"Content-Type": "application/json"
}
# functions
def check_in(id, asset_tag, headers):
# check in function, takes the asset ID and POST's to the url
# can optionally set a checkin note in the payload
url = "https://develop.snipeitapp.com/api/v1/hardware/"+id+"/checkin"
# modify if you would like a note with the checkin
payload = {"note": "checked in by ME"}
try:
response = requests.request("POST", url, json=payload, headers=headers)
checkin_response = response.json()
'''
This if statement is required here because if the asset is already checked in, it still returns an http successful message.
So this checks if the value in the status key is equal to "error" and if so then it will log this as an error.
'''
if "error" in checkin_response['status']:
logging.error("Unable to checkin asset %s - %s"
% (asset_tag, checkin_response['messages']))
else:
logging.info("Successfully checked in asset %s" % (asset_tag))
# catch any http errors and continue
except requests.exceptions.RequestException as e:
logging.error(e)
pass
def get_asset_id(asset_tag, output_q, headers):
# this function gets the asset id from a given asset tag
url = "https://develop.snipeitapp.com/api/v1/hardware/bytag/"+asset_tag+""
output_dict = {}
try:
response = requests.request("GET", url, headers=headers, verify=False)
response.raise_for_status()
json_response = response.json()
'''
This checks if the status key exists in the response and if it does then it returns an error.
This is because the status key does not exist at all in a successful query.
Again this is required because even if the asset tag doesn't exist, it still returns http 200.
'''
if "status" in json_response:
logging.error("Unable to retrieve asset ID for asset tag %s - %s"
% (asset_tag, json_response['messages']))
else:
logging.info("Successfully retrieved asset ID from %s" %
(asset_tag))
asset_id = str(json_response['id'])
check_in(asset_id, asset_tag, headers)
except requests.exceptions.RequestException as e:
logging.error(e)
pass
output_q.put(output_dict)
# class for tkinter configuration
class App:
def __init__(self, master):
self.label = ttk.Label(master, text="Bulk Checkin")
self.label.grid(row=0, column=0, columnspan=2)
ttk.Button(master, text="Select File",
command=self.select_file).grid(row=2, column=0)
ttk.Button(master, text="Checkin Assets",
command=self.checkin_assets).grid(row=2, column=1)
def select_file(self):
filename = filedialog.askopenfilename(initialdir="C:/Users/fraser/Desktop/", filetypes=(("CSV Files","*.csv"),))
self.infile = open(filename, "r")
print(self.infile.name)
def checkin_assets(self):
# function for Checkin Assets button click
# start a timer so we can see how long it takes
start_time = datetime.now()
output_q = Queue(maxsize=20)
procs = []
with open(self.infile.name, 'r') as myfile:
csv_reader = csv.DictReader(myfile)
line_count = 0
for row in csv_reader:
if line_count == 0:
", ".join(row)
line_count += 1
line_count += 1
asset_tag = row["asset_tag"]
my_proc = Process(target=get_asset_id, args=(
asset_tag, output_q, headers))
my_proc.start()
procs.append(my_proc)
# Make sure all processes have finished
for a_proc in procs:
a_proc.join()
# log how long this took
logging.info("\nElapsed time: " + str(datetime.now() - start_time))
def main():
main = Tk()
app = App(main)
main.mainloop()
if __name__ == "__main__":
main()
Here you go, this is assuming you want the messages as a popup. If not, create your own messaging window, or add a label or something you can update to the main one. I used the two messaging classes from the referenced post, but you can call them from anywhere and pass a message you want displayed.
import csv
import logging
from datetime import datetime
from multiprocessing import Process, Queue
from tkinter import *
from tkinter import filedialog, ttk
from tkinter.messagebox import showinfo
class App(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.label = ttk.Label(master, text="Bulk Checkin")
self.label.grid(row=0, column=0, columnspan=2)
ttk.Button(master, text="Select File",
command=self.select_file).grid(row=2, column=0)
ttk.Button(master, text="Checkin Assets",
command=self.checkin_assets).grid(row=2, column=1)
def select_file(self):
filename = filedialog.askopenfilename(initialdir="C:/Users/fraser/Desktop/", filetypes=(("CSV Files","*.csv"),))
self.infile = open(filename, "r")
print(self.infile.name)
def checkin_assets(self):
# function for Checkin Assets button click
# start a timer so we can see how long it takes
start_time = datetime.now()
output_q = Queue(maxsize=20)
procs = []
with open(self.infile.name, 'r') as myfile:
csv_reader = csv.DictReader(myfile)
line_count = 0
for row in csv_reader:
if line_count == 0:
", ".join(row)
line_count += 1
line_count += 1
asset_tag = row["asset_tag"]
my_proc = Process(target=get_asset_id, args=(
asset_tag, output_q, headers))
my_proc.start()
procs.append(my_proc)
# Make sure all processes have finished
for a_proc in procs:
a_proc.join()
# log how long this took
logging.info("\nElapsed time: " + str(datetime.now() - start_time))
def popup_bonus(self, message):
win = Toplevel()
win.wm_title("Message")
l = Label(win, text=message)
l.grid(row=0, column=0)
b = ttk.Button(win, text="Ok", command=win.destroy)
b.grid(row=1, column=0)
def popup_showinfo(self, message):
showinfo("Message", message)
def main():
main = Tk()
app = App(main)
main.mainloop()
if __name__ == "__main__":
main()
I use easygui because it is the simplest and one-liner. It can work alongside tkinter so there is no compatibility issues. For example, if you want a messagebox/alert, just do this:
import easygui
easygui.msgbox('in cmd, type: "shutdown -a" within 20 seconds', title="Chance to abort shutdown")

How to Use Functions properly with GUI and netmiko

I'm new for Programming and right now doing a small project for my Network Engineer job.
The whole idea of this app: input IP, MAC, username, password: and see prints from functions like:
logging to device!
gathering info
and etc
and show all this info real time in print_out = tkinter.Text box
Currently, I have 2 .py files:
MAC_search_GUI.py
MAC_search.py
MAC_search_GUI.py is a tkinter window where I can put in IP, MAC, username, and password + there is another window where I can see logs:
import tkinter
from tkinter import Button, Text
from tkinter import simpledialog
from MAC_search import MAC_search_func
def show_entry_fields():
IP = e1.get()
MAC = e2.get()
username = e3.get()
password = e4.get()
"""
e1.delete(0, tkinter.END)
e2.delete(0, tkinter.END)
e3.delete(0, tkinter.END)
e4.delete(0, tkinter.END)
"""
SESSION = {'ip': IP,
'device_type':'cisco_ios',
'username': username,
'password': password}
result = MAC_search_func(IP, MAC, **SESSION)
return print(result)
# f4b5.2fa0.8fca
root = tkinter.Tk()
tkinter.Label(root, text="IP:").grid(row=1)
tkinter.Label(root, text="MAC:").grid(row=2)
tkinter.Label(root, text="Username").grid(row=3)
tkinter.Label(root, text="Password").grid(row=4)
e1 = tkinter.Entry(root)
e2 = tkinter.Entry(root)
e3 = tkinter.Entry(root)
e4 = tkinter.Entry(root)
e1.grid(row=1, column=1)
e2.grid(row=2, column=1)
e3.grid(row=3, column=1)
e4.grid(row=4, column=1)
print_out = tkinter.Text(root, height = 20, width = 60, bg = "light cyan").grid(row=7, column=2)
tkinter.Button(root, text='Apply', command=show_entry_fields).grid(row=5, column=1, sticky=tkinter.W,
pady=4)
root.geometry("600x600")
root.mainloop()
The function MAC_search_func is located in MAC_search.py, connecting to input IP and collecting info about that device:
import netmiko
from netmiko import ConnectHandler
from netmiko.ssh_exception import NetMikoTimeoutException
from netmiko.ssh_exception import NetMikoAuthenticationException
from paramiko.ssh_exception import SSHException
def MAC_search_func(arg_ip, arg_mac, **arg_session):
def conv_Po_to_Int(arg_int):
JSON = connection.send_command("show etherchannel summary", use_textfsm=True)
for line in JSON:
if line['po_name'] == arg_int:
int_status = line['interfaces']
return int_status[0]
interface = ''
try:
connection = netmiko.ConnectHandler(**arg_session)
print("Connection is succefull")
except NetMikoTimeoutException:
return print(" #### Device is not reachable! ####")
except NetMikoAuthenticationException:
return print(" #### Wrong Username or Password! ####")
except SSHException:
return print(" #### Make sure SSH is enabled! ####")
# Looking for MAC in Mac address table
JSON = connection.send_command("show mac address-table", use_textfsm=True)
for line in JSON:
if line['destination_address'] == arg_mac:
interface = line['destination_port']
# Checking if interface is Port channel
if interface[0:2] == "Po":
interface = conv_Po_to_Int(interface)
# IF MAC is not found
if interface == '':
return print("This MAC-ADDRESS IS NOT FOUND ON THIS IP: "+ arg_ip)
# If Mac was found on switch checking if Int is Trunk or Access
JSON = connection.send_command("show interfaces status", use_textfsm=True)
for line in JSON:
if line['port'] == interface:
int_status = line['vlan']
# if port is trunk checking which device located on another end
if int_status == "trunk":
JSON = connection.send_command("show cdp neighbors " + interface, use_textfsm=True)
for line in JSON:
next_switch = line['neighbor']
JSON = connection.send_command("show cdp entry " + next_switch)
result = JSON.find('IP address:')
return print("Looks like this mac located on device with " + JSON[result:(result + 21)] + " and hostname: " + next_switch)
else:
return print("MAC was found on " + interface)
connection.disconnect()
So, I have a few issues with that:
I don't know how to send RETURN from my MAC_search function back to MAC_search_GUI, so I can print it with:
print_out = tkinter.Text(root, height = 20, width = 60, bg = "light cyan").grid(row=7, column=2)
When I pressing Apply, the app is NOt-responding until all functions will not be finished(5-10 seconds), because it takes time to connect and get info from switch. How to make that after i will click Apply, app will not wait until the end but just continue working, until Return will not show up in print_out window
Sorry Mast,
Solution is:
for my first problem:
result = MAC_search_func(IP, MAC, **SESSION)
return print_out.insert("end-1c", result)
For my second problem I did not found complete solution, but it working faster now.

How do I pass dynamic parameters as arguments

I have the following code :
from tkinter import *
class GUI:
def __init__(self,master):
self.ip_word = Label(master,text="Input Path")
self.ip_word.grid(row=0,sticky=E)
self.ip_path_field = Entry(master)
self.ip_path_field.grid(row=0,column=1,sticky=W)
self.op_word = Label(master,text="Output Path")
self.op_word.grid(row=2,sticky=E)
self.op_path_field = Entry(master)
self.op_path_field.grid(row=2,column=1,sticky=W)
self.filename_word=Label(master,text="Output Filename ")
self.filename_word.grid(row=4,sticky=E)
self.filename =Entry(master)
self.filename.grid(row=4,column=1,sticky=W)
self.Submit = Button(master,text="Submit",fg="black",bg="white",command=self.Scraper(ip_path_field,op_path_field,filename) )
self.Submit.grid(row=5,columnspan=2)
"""
def printMessage(self):
str1=ip_path_field
str2=op_path_field
str3=filename
Scraper(str1,str2,str3)"""
def Scraper(self,ip_path_field,op_path_field,filename):
import pandas as pd
import os
# "C:/Users/chowdhuryr/Desktop/first automation/MAIN RESEARCH DATA.xlsx"
user_input =ip_path_field#input("Enter the input file path of your file: ")
if os.path.exists(user_input):
df = pd.read_excel(user_input, sheetname='Sheet1')
print("File Found and We are Processing !")
else:
print ("Input Directory does not exists.")
#"C:/Users/chowdhuryr/Desktop/first automation/OUTPUT DATA.xlsx"
user_output =op_path_field#input("Enter the output file path of your file: ")
#if os.path.exists(user_input):
#df = pd.read_excel(user_input, sheetname='Sheet1')
#--------------------------------------------------------------------------------------------------------------------------------------
#setting up the path
import os
os.chdir('C:/Users/chowdhuryr/Desktop/first automation')
df=df[0:5]
#--------------------------------------------------------------------------------------------------------------------------------------
#importing necessary packages
from selenium import webdriver
#from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
#---------------------------------------------------------------------------------------------------------------------------------------
#Setting up Chrome webdriver
#chrome_options = webdriver.ChromeOptions()
options = webdriver.ChromeOptions()
options.add_argument("--headless") #making the window work in the background
options.add_argument('window-size=1200x850')
#declaring a list to store the messages
Message=list()
Tier=list()
Wirecentre=list()
#---------------------------------------------------------------------------------------------------------------------------------------
#iteration to access the url and and retriving the Tier Locations
for i in range(0,df.shape[0]): #(0,df.shape[0]):
driver = webdriver.Chrome(executable_path='C:/Users/chowdhuryr/Desktop/first automation/chromedriver', chrome_options=options) #openning chrome
#driver.maximize_window() #maximizing the window
driver.get('https://clec.att.com/facilitiescheck/facilities_chk.cfm') #openning the url
street_address=driver.find_element_by_xpath('/html/body/form/table/tbody/tr[2]/td[2]/table/tbody/tr[1]/td[2]/input') #accessing the street address field
street_address.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[0]) #passing the values to street_address location
city=driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[2]/td[2]/input') #accessing the city to street address field
city.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[1]) #passing the values to the city location
state=driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[4]/td[2]/select') #accessing the state field
state.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[2]) #passing the values to the state field
checkbox=driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[8]/td[1]/input') #accessing the checkbox
checkbox.click() #clicking on the check box
search_button=driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[8]/td[1]/input') #accessing the submit button
search_button.submit() #clicking the submit button
#try-except block in case if radio button appears
try:
Address=driver.find_element_by_xpath('/html/body/form/table[2]/tbody/tr[1]/td[2]/b') #taking the xpath of the address block
if (Address): #checking if it contains any radio button or not
Radio_button=driver.find_element_by_xpath('/html/body/form/table[2]/tbody/tr[2]/td[1]/input') #getting the xpath of radio button
Radio_button.click() #clicking the radio button
submit_button=driver.find_element_by_xpath('/html/body/form/table[3]/tbody/tr[2]/td/input') #getting the submit button
submit_button.submit()
except NoSuchElementException:
print('no such element found')
message_body= driver.find_element_by_xpath('//*[#id="msg"]/table/tbody/tr/td').text #Extracting the Message from the message box
Message.append(message_body[14:]) #putting the message into a text
str = message_body.split() #splitting the message
if any ("Tier"in s for s in str):
j=str.index('Tier')
Tier.append(str[j+1])
else:
Tier.append("NULL")
if any ("AT&T"in s for s in str):
j=str.index('AT&T')
Wirecentre.append(str[j+1])
else:
Wirecentre.append("NULL")
#saving the screenshot
str=df['STRIP_EC_CIRCUIT_ID'][i]
filename="C:\\Users\\chowdhuryr\\Desktop\\first automation\\"+str+".png" #Taking the circuit id name
driver.get_screenshot_as_file(filename)
driver.close() #closiing the driver
#------------------------------------------------------------------------------------------------------------------------------------------
#putting the back thenew columns into the dataframe and storing it into an excel sheet
df['Tier']=Tier #putting the Tier column back into the dataset
df['Wirecentre']=Wirecentre #putting the Wirecentre column back into the dataset
df['Message']=Message #putting the Message column back into the dataset
if os.path.exists(user_output):
user_output="user_output"+filename+".xlsx"
writer = pd.ExcelWriter(user_output) #writing the dataframe down into a new excel file
df.to_excel(writer,'sheet1',index=False) #to_excel(writer,'Sheet1')
writer.save()
else:
print ("Output Directory does not exists.")
#-------------------------------------------------------------------------------------------------------------------------------------------
#Generating pop up window at the end of the process
popup_driver=webdriver.Chrome()
popup_driver.maximize_window()
popup_driver.execute_script(" window.alert('Process is Completed');") #generating the pop up """
root =Tk()
b=GUI(root)
#b.Scraper(ip_path_field,op_path_field,filename)
root.mainloop()
Now what I want to do is this :
I want to pass the variables ip_path_field,op_path_field,filename as arguments to the function named scraper . Now ip_path_field,op_path_field,filename are all user inputs and not hard coded strings. Now whenever I run the following code, I get the GUI opened and whenever I provide my inputs in the required edit boxes and press the submit button I get the following error name 'ip_path_field' is not defined. My purpose of this code is to pass the user defined file paths to the function called scraper() as defined in the code above.
You need to create a callback that gets the values, and then passes them to the function that does the work.
Example:
class GUI:
def __init__(self,master):
...
self.Submit = Button(..., command=self.handle_submit)
...
def handle_submit(self):
ip_path = self.ip_path_field.get()
op_path = self.op_path_field.get()
filename = self.filename.get()
self.Scrapper(ip_path_field,op_path_field,filename)
The main problem you are having is related to how you built your method. Instead you should have put the get() calls inside that method. This way you can simple run the command to call scraper without needing to pass arguments. I have taken your code and re-written it to more closely follow the PEP8 standard with the method correction included.
You only need to apply the self. prefix to class attributes/methods. Things like labels/buttons that are not going to be modified later in the code should be left as normal widgets that are not assigned as attributes.
Next I moved all your imports to the top of the code. You only need to import a library one time and it should all be listed at the top of your code. On imports keep in mind it is better to import tkinter as tk instead of using * to prevent any accidental overrides occurring.
I have changed your string concatenation to use the format() method as + is deprecated.
Here is your code with some basic clean up as well.
import tkinter as tk
import pandas as pd
import os
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
class GUI:
def __init__(self,master):
tk.Label(master, text="Input Path").grid(row=0, sticky="e")
tk.Label(master, text="Output Path").grid(row=2, sticky="e")
tk.Label(master, text="Output Filename").grid(row=4, sticky="e")
self.ip_path_field = tk.Entry(master)
self.op_path_field = tk.Entry(master)
self.filename = tk.Entry(master)
self.ip_path_field.grid(row=0, column=1, sticky="w")
self.op_path_field.grid(row=2, column=1, sticky="w")
self.filename.grid(row=4, column=1, sticky="w")
tk.Button(master, text="Submit", fg="black", bg="white",
command=self.scrapper).grid(row=5,columnspan=2)
def scrapper(self): # changed function to a method.
user_input = self.ip_path_field.get(0, "end")
user_output = self.op_path_field.get(0, "end")
filename = self.filename.get(0, "end")
if os.path.exists(user_input):
df = pd.read_excel(user_input, sheetname='Sheet1')
print("File Found and We are Processing !")
else:
print ("Input Directory does not exists.")
os.chdir('C:/Users/chowdhuryr/Desktop/first automation')
df = df[0:5]
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument('window-size=1200x850')
message = list()
tier = list()
wirecentre = list()
for i in range(0,df.shape[0]):
driver = webdriver.Chrome(executable_path='C:/Users/chowdhuryr/Desktop/first automation/chromedriver', chrome_options=options)
driver.get('https://clec.att.com/facilitiescheck/facilities_chk.cfm')
street_address = driver.find_element_by_xpath('/html/body/form/table/tbody/tr[2]/td[2]/table/tbody/tr[1]/td[2]/input')
street_address.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[0])
city=driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[2]/td[2]/input')
city.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[1])
state = driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[4]/td[2]/select')
state.send_keys(df['CIRCUIT_LOC_ADDR'][i].split(',')[2])
checkbox = driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[8]/td[1]/input')
checkbox.click()
search_button = driver.find_element_by_xpath('/html/body/form/table[1]/tbody/tr[2]/td[2]/table/tbody/tr[8]/td[1]/input')
search_button.submit()
try:
address = driver.find_element_by_xpath('/html/body/form/table[2]/tbody/tr[1]/td[2]/b')
if (address):
radio_button = driver.find_element_by_xpath('/html/body/form/table[2]/tbody/tr[2]/td[1]/input')
radio_button.click()
submit_button = driver.find_element_by_xpath('/html/body/form/table[3]/tbody/tr[2]/td/input')
submit_button.submit()
except NoSuchElementException:
print('no such element found')
message_body = driver.find_element_by_xpath('//*[#id="msg"]/table/tbody/tr/td').text
message.append(message_body[14:])
strx = message_body.split()
if any ("Tier"in s for s in strx):
j = strx.index('Tier')
tier.append(strx[j+1])
else:
tier.append("NULL")
if any ("AT&T"in s for s in strx):
j = strx.index('AT&T')
wirecentre.append(strx[j+1])
else:
wirecentre.append("NULL")
strx = df['STRIP_EC_CIRCUIT_ID'][i]
filename = "C:\\Users\\chowdhuryr\\Desktop\\first automation\\{}.png".format(strx)
driver.get_screenshot_as_file(filename)
driver.close()
df['Tier'] = tier
df['Wirecentre'] = wirecentre
df['Message'] = message
if os.path.exists(user_output):
user_output="user_output{}.xlsx".format(filename)
writer = pd.ExcelWriter(user_output)
df.to_excel(writer, 'sheet1', index=False)
writer.save()
else:
print ("Output Directory does not exists.")
popup_driver = webdriver.Chrome()
popup_driver.maximize_window()
popup_driver.execute_script(" window.alert('Process is Completed');")
root = tk.Tk()
b = GUI(root)
root.mainloop()
All that said you should try to use a minimal code next time. It is easier to fix issues when you narrow it down to exactly the problem. For example based on your question a Minimal, Complete, and Verifiable example would look something like this.
from tkinter import *
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
class GUI:
def __init__(self,master):
self.ip_path_field = Entry(master)
self.ip_path_field.grid(row=0,column=1,sticky=W)
self.op_path_field = Entry(master)
self.op_path_field.grid(row=2,column=1,sticky=W)
self.filename =Entry(master)
self.filename.grid(row=4,column=1,sticky=W)
self.Submit = Button(master, text="Submit",
command=self.Scrapper(ip_path_field,op_path_field,filename) )
self.Submit.grid(row=5,columnspan=2)
def Scrapper(self,ip_path_field,op_path_field,filename):
user_input = ip_path_field
user_output = op_path_field
filename = filename
root = Tk()
b = GUI(root)
root.mainloop()

How to implement a thread in a wxPython GUI application

I'm having trouble implementing a thread correctly to keep my application from locking up and experiencing weird behavior. The app is designed to log into a ubuntu based server or ubuntu embedded server and search for log files that may be in the clear. The embedded server works, but the app keeps locking up while the search is occurring. The siteserver will not process. I have yet to code the local file search. I would like to add a progress bar once I figure out how to implement threads. I thought this would be straight forward since I've been learning and working with Python for several months now, but working with a GUI has its challenges. I'm still a neophyte and open to all the criticisms; it only helps me to become a better programmer. Any help is greatly appreciated. Here is the code below:
#!c:\python27
import wx
import os
import re
import paramiko
import string
import fileinput
import os.path
import dircache
import sys
import time
import datetime, time
import wx
from wxGui import *
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame("SecureTool v2.0.0", (50, 60), (458, 332))
frame.Show()
self.SetTopWindow(frame)
return True
class MyFrame(wx.Frame):
def __init__(self, title, pos, size):
wx.Frame.__init__(self, None, -1, title, pos, size)
toolbar = self.CreateToolBar()
toolbar.Realize()
menuFile = wx.Menu()
menuFile.Append(1, "&About...")
menuFile.AppendSeparator()
menuFile.Append(2, "E&xit")
menuBar = wx.MenuBar()
menuBar.Append(menuFile, "&File")
menu2 = wx.Menu()
menu2.Append(wx.NewId(), "&Copy", "Copy in status bar")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), "C&ut", "")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), "Paste", "")
menu2.AppendSeparator()
menu2.Append(wx.NewId(), "&Options...", "Display Options")
menuBar.Append(menu2, "&Edit")
self.SetMenuBar(menuBar)
self.CreateStatusBar()
self.SetStatusText("Welcome to SecureTool!")
self.Bind(wx.EVT_MENU, self.OnAbout, id=1)
self.Bind(wx.EVT_MENU, self.OnQuit, id=2)
panel = wx.Panel(self)
panel.SetBackgroundColour('LIGHT GREY')
#Close button
button = wx.Button(panel, label="EXIT", pos=(229, 160), size=(229, 80))
self.Bind(wx.EVT_BUTTON, self.OnQuit, button)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
#Embed Server button
button2 = wx.Button(panel, label="Embed Server", pos=(0, 160), size=(229, 80))
self.Bind(wx.EVT_BUTTON, self.OnIP, button2)
#Site Server
button3 = wx.Button(panel, label="SITESERVER", pos=(0, 80), size=(229, 80))
self.Bind(wx.EVT_BUTTON, self.OnUsrPswd, button3)
#Local Search
button4 = wx.Button(panel, label="LOCAL SEARCH", pos=(229, 80), size=(229, 80))
self.Bind(wx.EVT_BUTTON, self.OnOpen, button4)
EVT_RESULT(self, self.OnResult)
self.worker = None
def OnIP(self, event):
ip_address = 0
result = ''
dlg = wx.TextEntryDialog(None, "Enter the IP Address.",
'Embed Server Connect', 'xxx.xxx.xxx.xxx')
if dlg.ShowModal() == wx.ID_OK:
ip_address = dlg.GetValue()
if ip_address:
cmsg = wx.MessageDialog(None, 'Do you want to connect to: ' + ip_address,
'Connect', wx.YES_NO | wx.ICON_QUESTION)
result = cmsg.ShowModal()
if result == wx.ID_YES:
self.DispConnect(ip_address)
cmsg.Destroy()
dlg.Destroy()
return True
def OnUsrPswd(self, event):
passwrd = 0
result = ''
result = wx.TextEntryDialog(None, 'Enter Weekly Password', 'Site Server login','')
if result.ShowModal() == wx.ID_OK:
passwrd = result.GetValue()
if passwrd:
psmsg = wx.MessageDialog(None, 'Do you want to connect to the Siteserver?', 'Connect',
wx.YES_NO | wx.ICON_QUESTION)
result = psmsg.ShowModal()
if result == wx.ID_YES:
self.SiteserverConnect(passwrd)
psmsg.Destroy()
result.Destroy()
return True
def ErrMsg(self):
ermsg = wx.MessageDialog(None, 'Invalid Entry!', 'ConnectionDialog', wx.ICON_ERROR)
ermsg.ShowModal()
ermsg.Destroy()
def GoodConnect(self):
gdcnt = wx.MessageDialog(None, 'You are connected!', 'ConnectionStatus', wx.ICON_INFORMATION)
gdcnt.ShowModal()
#if gdcnt.ShowModal() == wx.ID_OK:
gdcnt.Destroy()
def OnFinish(self):
finish = wx.MessageDialog(None, 'Job is finished!', 'WorkStatus', wx.ICON_INFORMATION)
finish.ShowModal()
finish.Destroy()
def DispConnect(self, address):
pattern = r"\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
port = 22
user = 'root'
password ='******'
if re.match(pattern, address):
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(address,port,user,password)
Ssh = ssh
self.GoodConnect()
self.OnSearch(Ssh)
else:
self.ErrMsg()
def SiteserverConnect(self, password):
port = 22
user = 'root2'
address = '10.5.48.2'
if password:
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(address,port,user,password)
Ssh = ssh
self.GoodConnect()
self.OnSiteSearch(Ssh)
else:
self.ErrMsg()
def startWorker(self,a, b, c):
self.button2.Disable()
self.thread = Thread(target=self.LongRunningSearch)
self.thread.start()
def OnSearch(self, sssh):
self.startWorker(self.OnFinish, self.LongRunningSearch, wargs=[sssh])
self.OnFinish()
def LongRunningSearch(sssh):
ssh = sssh
apath = '/'
apattern = '"*.txt" -o -name "*.log"'
rawcommand = 'find {path} -name "*.txt" -o -name "*.log"'
command1 = rawcommand.format(path=apath, pattern=apattern)
stdin, stdout, stderr = ssh.exec_command(command1)
filelist = stdout.read().splitlines()
ftp = ssh.open_sftp()
for afile in filelist:
(head, filename) = os.path.split(afile)
paths = '/dispenser_result.log'
temp = ftp.file(paths, 'w')
from time import strftime
temp.write("{0:^75}".format("Company -Security Report" ) + strftime(" %Y-%m-%d %H:%M:%S") + "\n\n")
ustring = wx.TextEntryDialog(None, 'Enter a search string below:', 'Search', 'String Name')
if ustring.ShowModal() == wx.ID_OK:
userstring = ustring.GetValue()
if userstring:
userStrHEX = userstring.encode('hex')
userStrASCII = ''.join(str(ord(char)) for char in userstring)
regex = re.compile(r"(%s|%s|%s)" % ( re.escape( userstring ), re.escape( userStrHEX ), re.escape( userStrASCII )))
else:
sys.exit('You Must Enter A String!!!')
count = 0
for afile in filelist:
(head, filename) = os.path.split(afile)
if afile.endswith(".log") or afile.endswith(".txt"):
f=ftp.open(afile, 'r')
for i, line in enumerate(f.readlines()):
result = regex.search(line)
if result:
count += 1
ln = str(i)
pathname = os.path.join(afile)
template = "\n\nLine: {0}\nFile: {1}\nString Type: {2}\n\n"
output = template.format(ln, pathname, result.group())
ftp.get(afile, 'c:\\Extracted\\' + filename)
temp.write(output)
break
else:
#print "No Match in: " + os.path.join(afile)
temp.write("\nNo Match in: " + os.path.join(afile))
f.close()
for fnum in filelist:
#print "\nFiles Searched: ", len(filelist)
#print "Files Matched: ", count
num = len(filelist)
temp.write("\n\nFiles Searched:" + '%s\n' % (num))
temp.write("Files Matched:"+ '%s\n' % (count))
temp.write("Search String:"+ '%s\n' % (userstring))
break
temp.close()
defaultFolder = "DispenserLogResults"
if not defaultFolder.endswith(':') and not os.path.exists('c:\\Extracted\\DispenserLogResults'):
os.mkdir('c:\\Extracted\\DispenserLogResults')
else:
pass
ftp.get(paths, 'c:\\Extracted\\DispenserLogResults\\dispenser_result.log')
ftp.remove(paths)
re.purge()
ftp.close()
ssh.close()
def OnSiteSearch(self, sssh):
ssh = sssh
apath = '/var/log/apache2 /var/opt/smartmerch/log/'
apattern = '"*.log"'
rawcommand = 'find {path} -type f -name "*.log"'
command1 = rawcommand.format(path=apath, pattern=apattern)
stdin, stdout, stderr = ssh.exec_command(command1)
filelist = stdout.read().splitlines()
ftp = ssh.open_sftp()
for afile in filelist:
(head, filename) = os.path.split(afile)
paths = '/var/tmp/siteserver_result.log'
temp = ftp.file(paths, 'w')
from time import strftime
temp.write("{0:^75}".format("Gilbarco - SQA Security Report" ) + strftime(" %Y-%m-%d %H:%M:%S") + "\n\n")
temp.write("\n{0:^75}".format("SiteServer Logs" ))
ustring = wx.TextEntryDialog(None, 'Enter a search string below:', 'Search', 'String Name')
if ustring.ShowModal() == wx.ID_OK:
userstring = ustring.GetValue()
if userstring:
userStrHEX = userstring.encode('hex')
userStrASCII = ''.join(str(ord(char)) for char in userstring)
regex = re.compile(r"(%s|%s|%s)" % ( re.escape( userstring ), re.escape( userStrHEX ), re.escape( userStrASCII )))
else:
sys.exit('You Must Enter A String!!!')
count = 0
for afile in filelist:
(head, filename) = os.path.split(afile)
if afile.endswith(".log") or afile.endswith(".txt"):
f=ftp.open(afile, 'r')
for i, line in enumerate(f.readlines()):
result = regex.search(line)
if result:
count += 1
ln = str(i)
pathname = os.path.join(afile)
template = "\n\nLine: {0}\nFile: {1}\nString Type: {2}\n\n"
output = template.format(ln, pathname, result.group())
ftp.get(afile, 'c:\\Extracted\\' + filename)
temp.write(output)
break
else:
temp.write("\nNo Match in: " + os.path.join(afile))
f.close()
for fnum in filelist:
num = len(filelist)
temp.write("\n\nFiles Searched:" + '%s\n' % (num))
temp.write("Files Matched:"+ '%s\n' % (count))
temp.write("Search String:"+ '%s\n' % (userstring))
break
temp.close()
defaultFolder = "SiteServerLogResults"
if not defaultFolder.endswith(':') and not os.path.exists('c:\\Extracted\\SiteServerLogResults'):
os.mkdir('c:\\Extracted\\SiteServerLogResults')
else:
pass
ftp.get(paths, 'c:\\Extracted\\SiteServerLogResults\\siteserver_result.log')
ftp.remove(paths)
re.purge()
ftp.close()
ssh.close()
self.OnFinish()
def OnOpen(self,e):
self.dirname = ''
dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname, self.filename), 'r')
self.control.SetValue(f.read())
f.close()
dlg.Destroy()
def OnQuit(self, event):
self.Close(True)
def OnAbout(self, event):
wx.MessageBox("This is sQAST v2.0.0",
"About secureTool", wx.OK | wx.ICON_INFORMATION, self)
def OnCloseWindow(self, event):
self.Destroy()
if __name__ == '__main__':
app = MyApp(False)
app.MainLoop()
Traceback Error after running:
Traceback (most recent call last):
File "C:\SQA_log\wxGui.py", line 87, in OnIP
self.DispConnect(ip_address)
File "C:\SQA_log\wxGui.py", line 143, in DispConnect
self.OnSearch(Ssh)
File "C:\SQA_log\wxGui.py", line 169, in OnSearch
self.startWorker(self.OnFinish, self.LongRunningSearch, wargs=[sssh])
In your particular case I would do something like:
1) Encapsulate the long task and separate it from the GUI reaction, simplify your method:
def OnSearch(self, sssh):
self.LongRunningSearch(sssh) # Move all the blocking code here,
# just NOT the GUI reaction !
# Meaning self.OnFinish()...
self.OnFinish()
2) Verify that it still runs fine. Then modify the method to add the thread:
def OnSearch(self, sssh):
startWorker(self.OnFinish, self.LongRunningSearch, wargs=[sssh])
self.OnSearch will end immediately and self.OnFinish will be called after the thread running self.LongRunningSearch has finished. It may still need some tuning as I am unable to run your code on my computer.
I don't see any threading in your application at all. Whenever you make a call to something that will take a while, that something needs to run in a separate thread or it will block the main loop of your GUI.
You should read the following wiki entry on threading in wxPython: http://wiki.wxpython.org/LongRunningTasks
I have used the information therein to successfully create threaded wxPython applications. There's also a simple threading tutorial here: http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
Hope that helps. If you get stuck, you should post to the official wxPython mailing group: https://groups.google.com/forum/#!forum/wxpython-users Those guys will set you straight.
You can also have a look at convenience module for threading implemented in the wx, wx.lib.delayedresult. It is very easy to use and to add when you find it is needed. I am not sure why it is ignored so often. I have written an example which uses it some time ago here.
It basically needs you to create two functions / methods. First, which will be ran in another thread, and second, which will be ran after another thread finishes. Then you just call startWorker(LongTaskDone, LongTask).
Example 1: Using wx.lib.delayedresult. wx.CallAfter is used to show progress in GUI thread using gauge widget. Official Documentation.
from time import sleep
import wx
from wx.lib.delayedresult import startWorker
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.startButton = wx.Button(self.panel, label="Long Task")
self.abortButton = wx.Button(self.panel, label="Abort")
self.abortButton.Disable()
self.gauge = wx.Gauge(self.panel, size=(-1, 20))
self.shouldAbort = False
self.startButton.Bind(wx.EVT_BUTTON, self.OnStartButton)
self.abortButton.Bind(wx.EVT_BUTTON, self.OnAbortButton)
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.startButton)
self.sizer.Add(self.abortButton)
self.sizer.Add((10, 10))
self.sizer.Add(self.gauge)
self.border = wx.BoxSizer()
self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5)
self.panel.SetSizerAndFit(self.border)
self.SetSizerAndFit(self.windowSizer)
self.Show()
def OnStartButton(self, e):
self.startButton.Disable()
self.abortButton.Enable()
startWorker(self.LongTaskDone, self.LongTask)
def OnAbortButton(self, e):
self.shouldAbort = True
def LongTask(self):
for a in range(101):
sleep(0.05)
wx.CallAfter(self.gauge.SetValue, a)
if self.shouldAbort:
break
return self.shouldAbort
def LongTaskDone(self, result):
r = result.get()
if r:
print("Aborted!")
else:
print("Ended!")
self.startButton.Enable()
self.abortButton.Disable()
self.shouldAbort = False
self.gauge.SetValue(0)
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Example 2: Using standard threading module. In some cases this may be more "ugly". I would recommend using wx.lib.delayedresult. Official Documentation.
from time import sleep
from threading import Thread
import wx
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.startButton = wx.Button(self.panel, label="Long Task")
self.abortButton = wx.Button(self.panel, label="Abort")
self.abortButton.Disable()
self.gauge = wx.Gauge(self.panel, size=(-1, 20))
self.shouldAbort = False
self.thread = None
self.startButton.Bind(wx.EVT_BUTTON, self.OnStartButton)
self.abortButton.Bind(wx.EVT_BUTTON, self.OnAbortButton)
self.windowSizer = wx.BoxSizer()
self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.startButton)
self.sizer.Add(self.abortButton)
self.sizer.Add((10, 10))
self.sizer.Add(self.gauge)
self.border = wx.BoxSizer()
self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5)
self.panel.SetSizerAndFit(self.border)
self.SetSizerAndFit(self.windowSizer)
self.Show()
def OnStartButton(self, e):
self.startButton.Disable()
self.abortButton.Enable()
self.thread = Thread(target=self.LongTask)
self.thread.start()
def OnAbortButton(self, e):
self.shouldAbort = True
def LongTask(self):
for a in range(101):
sleep(0.05)
wx.CallAfter(self.gauge.SetValue, a)
if self.shouldAbort:
break
wx.CallAfter(self.LongTaskDone, self.shouldAbort)
def LongTaskDone(self, r):
if r:
print("Aborted!")
else:
print("Ended!")
self.startButton.Enable()
self.abortButton.Disable()
self.shouldAbort = False
self.gauge.SetValue(0)
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Note: threading.Lock may be needed for thread-safe passing of more complicated variables and data than simple boolean flag.
Edit: Added examples.
Edit: Added ability to abort the thread.
Edit: Simplified the examples. Got rid of timer based on tom10's idea and also deleted threading.Lock as it is not needed here.
In wxPython, all calls to methods of GUI objects and event handlers need to happen in the main thread. Because of this, you need to be careful when using threading with wx, but it's usually easy to make the required modifications.
In your code, for example, you could do the call to wx.TextEntryDialog from the main thread, and then pass this information to the search thread.
If you need to request action of the GUI from the thread, the easiest way is to use wx.CallAfter, though there are other approaches that work as well.

Resources