PySimpleGUI with Selenium - python

I've made a PySimpleGUI app with Selenium, it takes CSV values and puts them into a form and submits it, now however we need to send an input to the form.
So I added this:
[sg.Multiline(size=(30, 5), key='deposit')],
But how do I reference it in the send_keys function that I'm using currently:
...
mail_field.send_keys(line[13])
phone_field.send_keys(line[16])
deposit_field.send_keys('deposit') ?
submit.click()
Thought I'd try values['deposit'] but that doesnt work either
Entire code
import csv
import time
import threading
from selenium import webdriver
import PySimpleGUI as sg
import os
import sys
def resource_path(relative_path):
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def make_window(theme):
sg.theme('LightGrey')
menu_def = [['&Application', ['E&xit']],
['&Help', ['&About']]]
right_click_menu_def = [[], ['Exit']]
# Table Data
input_layout = [[sg.Menu(menu_def, key='-MENU-')],
[sg.Button("Open File", button_color=(
'white', 'dodgerblue'))],
[sg.Text('Chrome Version')],
[sg.OptionMenu(values=(
'96', '97', '98'), k='-OPTION MENU-', background_color='dodgerblue'), ],
[sg.Multiline(size=(30, 5), key='deposit')],
[sg.Button('Submit', button_color=('white', '#Ed1941'))]]
layout = [[sg.Text('Name', size=(35, 1), justification='center', font=(
"Helvetica", 16), relief=sg.RELIEF_RIDGE, k='-TEXT HEADING-', enable_events=True)]]
layout += [[sg.TabGroup([[sg.Tab('Setup CSV and Chrome Version', input_layout),
]], key='-TAB GROUP-', size=(400, 120))]]
return sg.Window('Name', layout, icon='icon.ico', right_click_menu=right_click_menu_def)
def main():
window = make_window(sg.theme('LightGrey'))
# This is an Event Loop
while True:
event, values = window.read(timeout=100)
# keep an animation running so show things are happening
if event not in (sg.TIMEOUT_EVENT, sg.WIN_CLOSED):
print('============ Event = ', event, ' ==============')
print('-------- Values Dictionary (key=value) --------')
for key in values:
print(key, ' = ', values[key])
if event in (None, 'Exit'):
print("[LOG] Clicked Exit!")
break
elif event == 'About':
print("[LOG] Clicked About!")
sg.popup('',
'Select CSV file',
'Select Chrome Version',
'Submit',
'')
elif event == 'Popup':
print("[LOG] Clicked Popup Button!")
sg.popup("You pressed a button!")
print("[LOG] Dismissing Popup!")
elif event == "Open File":
print("[LOG] Clicked Open File!")
csv_file_selected = sg.popup_get_file('Choose your file')
# sg.popup("You chose: " + str(folder_or_file))
# print("[LOG] User chose file: " + str(folder_or_file))
def run_selenium(window, file, driver):
with open(file, 'rt') as csv_file:
csv_reader = csv.reader(csv_file)
# -------------------------------------------------------------------------------
# Web Automation
driver = webdriver.Chrome(executable_path=driver)
driver.get('')
fname_field = driver.find_element_by_xpath('//*[#id="FIRSTNAME"]')
lname_field = driver.find_element_by_xpath('//*[#id="LASTNAME"]')
phone_field = driver.find_element_by_xpath('//*[#id="PHONE"]')
mail_field = driver.find_element_by_xpath('//*[#id="EMAIL"]')
deposit_field = driver.find_element_by_xpath('//*[#id="DEPOSIT"]')
submit = driver.find_element_by_xpath(
'//*[#id="sib-form"]/div[8]/div/button')
with open(file, 'rt', encoding='utf-8-sig') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=';')
next(csv_reader)
for line in csv_reader:
time.sleep(3.0)
fname_field.send_keys(line[10])
lname_field.send_keys(line[11])
mail_field.send_keys(line[13])
phone_field.send_keys(line[16])
deposit_field.send_keys(text) //Here
submit.click()
# Not to update GUI in thread, but generate an event which will be processed in event loop.
window.write_event_value('Done', None)
def main():
# My GUI
window = make_window(sg.theme())
folder_or_file = None
cd97 = resource_path('./chromedriver97.exe')
cd98 = resource_path('./chromedriver98.exe')
# Using your path for all the drivers of all versions
paths = {
'97': cd97,
'98': cd98,
}
while True:
event, values = window.read(timeout=100)
# keep an animation running so show things are happening
if event not in (sg.TIMEOUT_EVENT, sg.WIN_CLOSED):
# print('============ Event = ', event, ' ==============')
# print('-------- Values Dictionary (key=value) --------')
for key in values:
print(key, ' = ', values[key])
if event in (None, 'Exit'):
# print("[LOG] Clicked Exit!")
break
elif event == 'About':
# print("[LOG] Clicked About!")
sg.popup('',
'Select CSV file',
'Select Chrome Version',
'Submit',
'')
elif event == 'Popup':
# print("[LOG] Clicked Popup Button!")
sg.popup("You pressed a button!")
# print("[LOG] Dismissing Popup!")
elif event == "Open File":
#print("[LOG] Clicked Open File!")
folder_or_file = sg.popup_get_file('Choose your file')
# sg.popup("You chose: " + str(folder_or_file))
#print("[LOG] User chose file: " + str(folder_or_file))
elif event == 'Submit':
version = values['-OPTION MENU-']
if folder_or_file is None or version not in paths:
# print("No CSV file selected or wrong Chrome version selected")
continue
# Using thread to avoid long time job will block and cause GUI no response
threading.Thread(target=run_selenium, args=(
window, folder_or_file, paths[version])).start()
# Disable Submit button to prevent submit again when threading
window['Submit'].update(disabled=True)
# print('[LOG] Run Selenium ...')
elif event == 'Done':
# Enable Submit button when thread done
window['Submit'].update(disabled=False)
# print('[LOG] End Selenium')
window.close()
main()

elif event == 'Submit': text=values['deposit'];...;threading.Thread(target=run_selenium, args=(window, folder_or_file, paths[version], text)).start();... and one more arguemnt text in def run_selenium(window, file, driver, text):
Answer by: Jason Yang. https://stackoverflow.com/users/11936135/jason-yang

Related

monitor chrome tabs - python selenium

I'm writing a script where I need you to monitor the number of open tabs. The script reads a table and each information in the table opens a new tab in the browser. It turns out that in order not to have thousands of tabs open, I need the script to monitor the tabs to have only 3 open, when I close a tab I need to automatically open the next tab
def base_dados_processos(self):
try:
df = pd.read_excel('TABLE.xlsx')
self.num_proc = df['COLUM']
except Exception:
pg.alert(text='error', title='ERROR', button='OK')
self.chrome.quit()
def loop_pesquisa(self):
for PROCESSOS in zip(self.num_proc):
num_current_tabs = len(self.chrome.window_handles)
if num_current_tabs < 3:
pg.hotkey('ctrl', '1')
time.sleep(1)
self.chrome.get('https://stackoverflow.com/')
pg.write(str(test))
pg.press('enter')
time.sleep(3)
pg.press('tab', presses=26)
pg.press('enter')
time.sleep(1)
pg.press('enter')
To do this, just add a loop that monitors the guides, it can be as follows:
while len(self.chrome.window_handles) > 3:
time.sleep(0.5)
without your code:
def base_dados_processos(self):
try:
df = pd.read_excel('TABLE.xlsx')
self.num_proc = df['COLUM']
except Exception:
pg.alert(text='error', title='ERROR', button='OK')
self.chrome.quit()
def loop_pesquisa(self):
for PROCESSOS in zip(self.num_proc):
while len(self.chrome.window_handles) > 3:
time.sleep(0.5)
pg.hotkey('ctrl', '1')
time.sleep(1)
self.chrome.get('https://stackoverflow.com/')
pg.write(str(test))
pg.press('enter')
time.sleep(3)
pg.press('tab', presses=26)
pg.press('enter')
time.sleep(1)
pg.press('enter')

Selenium Web-driver automation not working properly

So I found this script online, which is meant to brute-force web-forms online etc. using Selenium, and I thought it would be a good idea to take it, modify it a bit and experiment with it. This time, I tried creating a bot that:
Signs up to Twitter
Goes to twitter.com
Posts something.
Logs out.
Loop 1, 2, 3, 4 again
However, when I run the script, it just pops up a browser window and does nothing. Then the Terminal ends the Python script like it did its work correctly and finished with no problems...
Code (note that the script might look weird for what I want, but that's because I found the script as a web-form brute-forcer online, and decided to modify it to my needs):
#!/bin/python
from mainLib import *
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import simplejson as json
import sys
import optparse
profile = webdriver.FirefoxProfile()
profile.set_preference("general.useragent.override", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36")
driver = "reserved"
def userExists(username):
try:
driver.get("https://twitter.com/"+username)
assert (("??" or "?Twitter / ?") not in driver.title)
except AssertionError:
print '\033[1;31mUser\033[1;m',
print '\033[1;35m#%s\033[1;m' %username,
print '\033[1;31mdoes not exist.\033[1;m',
print '\033[1;33mTrying with the next Username...\033[1;m'
print ' '
return 1
except:
'uknown error'
def login(user, password, delay):
try:
print '\033[1;33mCreating account with mail: \033[1;m' + '\033[1;35m' + password + '\033[1;m' '\033[1;33m ...\033[1;m'
sleep(2)
if driver.current_url == 'https://twitter.com':
print '\033[1;33mPlease retry using a different IP Address (Proxy/VPN).\033[1;m'
driver.get("https://twitter.com/signup")
if driver.title == "Login on Twitter":
driver.get("https://twitter.com/signup")
sleep(3)
elem = driver.find_element_by_id('full-name')
elem.clear()
elem.send_keys('john')
elem = driver.find_element_by_id('email')
elem.clear()
elem.send_keys(password)
elem = driver.find_element_by_id('password')
elem.clear()
elem.send_keys("twitter1")
sleep(3)
elem.send_keys(Keys.RETURN)
sleep(delay + 3)
if driver.title == "Twitter / Error":
print ' \033[1;31mFailed!\033[1;m'
driver.get("https://twitter.com/signup")
sleep(3)
if driver.title == "Login to Twitter":
print ' \033[1;31mFailed!\033[1;m'
driver.get("https://twitter.com/signup")
sleep(3)
# if "This email is already registered." in driver.page_source:
# print ' \033[1;31mFailed!\033[1;m'
if driver.current_url == 'https://twitter.com/account/access':
print ' \033[1;31mFailed!\033[1;m'
print ("")
print '\033[1;33mPlease retry using a different IP Address (Proxy/VPN).\033[1;m'
driver.close()
sys.exit("")
assert (("Enter your phone") not in driver.title)
except AssertionError:
print ' \033[1;32mSuccess!\033[1;m'
# print '\033[1;35mEmail: \033[1;m' + password
# print '\033[1;35mPassword: \033[1;m' + "twitter1"
# print("")
try:
f = open('CreatedAccounts.txt','a')
except:
f = open('CreatedAccounts.txt','w')
f.write(password+'\n')
f.close()
driver.get("https://twitter.com")
elem = driver.find_element_by_id('tweet-box-home-timeline')
elem.clear()
elem.send_keys('It worked!')
elem = driver.find_element_by_xpath('//*[#id="timeline"]/div[2]/div/form/div[2]/div[2]/button')
elem.send_keys(Keys.RETURN)
time.sleep(5)
driver.get("https://twitter.com/logout")
sleep(5)
elem = driver.find_element_by_css_selector("button.js-submit").click()
sleep(5)
driver.get("https://twitter.com/signup")
# driver.delete_all_cookies()
# return 1
# else:
# print '\033[1;33mPlease check your Internet Connection.\033[1;m'
def dictionaryAttack(usernames,passwords,delay):
if str(type(usernames)) == "<type 'list'>":
for username in usernames:
#if (userExists(username) == 1):
# continue
driver.get("https://twitter.com/signup")
sleep(delay)
print("Creating Accounts...")
print("")
for password in passwords:
if (login(username,password,delay) == 1):
cj.clear()
break
def main():
parser = optparse.OptionParser()
parser.add_option('-f', '--file', action="store", dest="userfile", help="File containing valid usernames (one per line)", default=False)
parser.add_option('-d', '--dictionary', action="store", dest="dictionary", help="Text file containing passwords", default=False)
parser.add_option('-u', '--username', action="store", dest="username", help="A valid username", default=False)
parser.add_option('-t', '--time', action="store", dest="delay", help="Delay (in seconds) - use this option based on your Network Connection speed.", default=True)
options, args = parser.parse_args()
global driver
if (options.delay is None):
delay = 4
else:
delay = int(options.delay)
print '\033[1;33mUsing\033[1;m',
print '\033[1;35m%d second(s)\033[1;m' %delay,
print '\033[1;33mof delay between login attempts.\033[1;m'
print ' '
if ( (options.userfile == False) and (options.username == False) ) :
print 'You have to set an username or a userfile'
exit()
if ( (options.userfile != False) and (options.username != False) ) :
print 'You can\'t set both options at once.. choose between username or userfile'
exit()
if (options.dictionary == False):
print 'You have to set a valid path for the passwords dictionary.'
exit()
try:
f = open(options.dictionary,'r')
passwords = []
while True:
line = f.readline()
if not line:
break
passwords.append(line.strip('\n'))
f.close()
except:
print 'Check the path to the dictionary and try again.'
exit()
if (options.userfile != False):
try:
f = open(options.userfile,'r')
usernames = []
while True:
line = f.readline()
if not line:
break
usernames.append(line.strip('\n'))
f.close()
except:
print 'Check the path to the users file and try again.'
exit()
driver = webdriver.Firefox(profile)
driver.implicitly_wait(30)
dictionaryAttack(usernames,passwords,delay)
else:
driver = webdriver.Firefox(profile)
driver.implicitly_wait(30)
dictionaryAttack(options.username,passwords,delay)
driver.close()
if __name__ == '__main__':
main()
See the link below. That could be the problem.
https://github.com/SeleniumHQ/selenium/issues/2257

How do I use multiple threads with PySide and Twython?

I'm trying to write a small python app, using PySide for the GUI and Twython as a Twitter API library, to catch a stream from Twitter.
The problem that I am having is that when I click "Start Monitoring Twitter" button, the UI freezes until the stream is complete, at which point the code continues to execute and disables the Start button and enables the Stop button. Here's the UI:
Everything else seems to work -- if I leave it, then the CSV file is created as I suspect -- the Twython components seem to be working as expected.
Line 151 is where the streaming from Twitter is engaged when I click start:
self.stream.statuses.filter(track=self.search_term)
How can I move the streaming to a separate thread and then use the Stop button on the UI to tell Twython to complete capturing the stream and exit?
I need to be able to send the MyStreamer instance to another thread and then send it the .disconnect() signal to have it terminate capturing the stream.
Here's the full code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import platform
import PySide
from PySide.QtGui import QApplication, QMainWindow, QPushButton, QCheckBox, QTextEdit
from time import sleep
from ui_tweetstream import Ui_MainWindow
from twython import Twython
from twython import TwythonStreamer
import csv
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
# Set up Variables
self.tweet_fav_count = True
self.tweet_geocoordinates = True
self.tweet_id = True
self.tweet_language = True
self.tweet_orig_tweet_id = True
self.tweet_orig_username = True
self.tweet_retweeted = True
self.tweet_sensitive = True
self.tweet_source_app = True
self.tweet_timestamp = True
self.tweet_user_name = True
self.search_term = "#bigdata"
self.tweets_to_get = 1000
# Bind the interface
self.check_tweet_fav_count.clicked.connect(self.setTweetFavCount)
self.check_tweet_geocoordinates.clicked.connect(self.setTweetGeocoordinates)
self.check_tweet_id.clicked.connect(self.setTweetID)
self.check_tweet_language.clicked.connect(self.setTweetLanguage)
self.check_tweet_orig_tweet_id.clicked.connect(self.setTweetOrigTweetID)
self.check_tweet_orig_username.clicked.connect(self.setTweetOrigUsername)
self.check_tweet_retweeted.clicked.connect(self.setTweetRetweeted)
self.check_tweet_sensitive.clicked.connect(self.setTweetSensitive)
self.check_tweet_source_app.clicked.connect(self.setTweetSourceApp)
self.check_tweet_timestamp.clicked.connect(self.setTweetTimestamp)
self.check_tweet_user_name.clicked.connect(self.setTweetUsername)
self.button_start.clicked.connect(self.streamStart)
self.button_stop.clicked.connect(self.streamStop)
# Set the initial states
self.button_stop.setEnabled(False)
APP_KEY = ''
APP_SECRET = ''
OAUTH_TOKEN = ''
OAUTH_TOKEN_SECRET = ''
self.t = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
self.stream = MyStreamer(APP_KEY,APP_SECRET,OAUTH_TOKEN,OAUTH_TOKEN_SECRET)
self.stream.init_mainWindow(self)
def streamStop(self):
print "Stopping stream"
# Enable other controls here
self.button_stop.setEnabled(False)
self.button_start.setEnabled(True)
self.setControlStates(True)
self.stream.stopStream()
def setControlStates(self, state):
self.check_tweet_fav_count.setEnabled(state)
self.check_tweet_geocoordinates.setEnabled(state)
self.check_tweet_id.setEnabled(state)
self.check_tweet_language.setEnabled(state)
self.check_tweet_orig_tweet_id.setEnabled(state)
self.check_tweet_orig_username.setEnabled(state)
self.check_tweet_retweeted.setEnabled(state)
self.check_tweet_sensitive.setEnabled(state)
self.check_tweet_source_app.setEnabled(state)
self.check_tweet_timestamp.setEnabled(state)
self.check_tweet_user_name.setEnabled(state)
self.search_box.setEnabled(state)
self.num_tweets_box.setEnabled(state)
# Functions for determining what to track
def setTweetFavCount(self):
self.tweet_fav_count = not self.tweet_fav_count
print "tweet_fav_count:", self.tweet_fav_count
def setTweetGeocoordinates(self):
self.tweet_geocoordinates = not self.tweet_geocoordinates
print "tweet_geocoordinates:", self.tweet_geocoordinates
def setTweetID(self):
self.tweet_id = not self.tweet_id
print "tweet_id:", self.tweet_id
def setTweetLanguage(self):
self.tweet_language = not self.tweet_language
print "tweet_language:", self.tweet_language
def setTweetOrigTweetID(self):
self.tweet_orig_tweet_id = not self.tweet_orig_tweet_id
print "tweet_orig_tweet_id:", self.tweet_orig_tweet_id
def setTweetOrigUsername(self):
self.tweet_orig_username = not self.tweet_orig_tweet_id
print "tweet_orig_username:", self. tweet_orig_username
def setTweetRetweeted(self):
self.tweet_retweeted = not self.tweet_retweeted
print "tweet_retweeted:", self.tweet_retweeted
def setTweetSensitive(self):
self.tweet_sensitive = not self.tweet_sensitive
print "tweet_sensitive:", self.tweet_sensitive
def setTweetSourceApp(self):
self.tweet_source_app = not self.tweet_source_app
print "tweet_source_app:", self.tweet_source_app
def setTweetTimestamp(self):
self.tweet_timestamp = not self.tweet_timestamp
print "tweet_timestamp:", self.tweet_timestamp
def setTweetUsername(self):
self.tweet_user_name = not self.tweet_user_name
print "tweet_user_name:", self.tweet_user_name
# Functions for starting and stopping the stream
def streamStart(self):
print "Starting stream"
self.setControlStates(False)
# Disable other controls here
self.button_start.setEnabled(False)
self.button_stop.setEnabled(True)
# Hack to try to disable the UI
# sleep(0.25)
# Get the active search term
self.search_term = self.search_box.text()
# Get the number of tweets
self.tweets_to_get = int(self.num_tweets_box.text())
# Set the streamer
self.stream.set_start_criteria(self.tweets_to_get)
self.stream.statuses.filter(track=self.search_term)
class MyStreamer(TwythonStreamer):
def init_mainWindow(self, the_main_window):
self.main_window = the_main_window
self.stop = False
self.header_done = False
def set_start_criteria(self, numTweets):
self.maxTweets = numTweets
self.tweetCount = 0
print "Number of tweets to get:", self.maxTweets
def stopStream(self):
self.stop = True
def on_success(self, data):
if 'text' in data:
self.tweetCount += 1
print "tweetCount:", self.tweetCount
#tweet = data['text'].encode('utf-8')
theTweet = data
writer = TweetMonkey()
writer.assignMainWindow(self.main_window, self.header_done)
self.header_done = True
writer.process(theTweet)
# Want to disconnect after the first result?
if self.stop is True or self.tweetCount >= self.maxTweets:
self.disconnect()
def on_error(self, status_code, data):
print status_code, data
class TweetMonkey:
def assignMainWindow(self,the_main_window, is_header_done):
self.main_window = the_main_window
self.header_done = is_header_done
def clean(self,text):
text = text.replace("\n","; ")
text = text.replace('"', "'")
text = text.replace(','," ")
return text
def create_header(self):
header = []
tweets = open("tweets.csv", 'ab+')
wr = csv.writer(tweets, dialect='excel')
if self.main_window.tweet_id is True:
header.append("id")
if self.main_window.tweet_language is True:
header.append("lang")
if self.main_window.tweet_user_name is True:
header.append("user_name")
header.append("tweet")
if self.main_window.tweet_retweeted is True:
header.append("retweeted")
if self.main_window.tweet_fav_count is True:
header.append("favorite_count")
if self.main_window.tweet_source_app is True:
header.append("source")
if self.main_window.tweet_orig_tweet_id is True:
header.append("in_reply_to_status_id")
if self.main_window.tweet_orig_username is True:
header.append("in_reply_to_screen_name")
# header.append("in_reply_to_user_id")
if self.main_window.tweet_sensitive is True:
header.append("possibly_sensitive")
if self.main_window.tweet_geocoordinates is True:
header.append("geo")
if self.main_window.tweet_timestamp is True:
header.append("created_at")
wr.writerow(header)
tweets.close()
def process(self, tweet):
if not self.header_done:
self.create_header()
self.header_done = True
# Create the file or append to the existing
theOutput = []
tweets = open("tweets.csv", 'ab+')
wr = csv.writer(tweets, dialect='excel')
if self.main_window.tweet_id is True:
theOutput.append(tweet['id'])
if self.main_window.tweet_language is True:
theOutput.append(tweet['lang'].encode('utf-8'))
if self.main_window.tweet_user_name is True:
theOutput.append(tweet['user']['name'].encode('utf-8', 'replace'))
theOutput.append(self.clean(tweet['text']).encode('utf-8', 'replace'))
if self.main_window.tweet_retweeted is True:
theOutput.append(tweet['retweeted'])
if self.main_window.tweet_fav_count is True:
theOutput.append(tweet['favorite_count'])
if self.main_window.tweet_source_app is True:
theOutput.append(self.clean(tweet['source']).encode('utf-8', 'replace'))
if self.main_window.tweet_orig_tweet_id is True:
theOutput.append(tweet['in_reply_to_status_id'])
if self.main_window.tweet_orig_username is True:
theOutput.append(tweet['in_reply_to_screen_name'])
#theOutput.append(tweet['in_reply_to_user_id'])
if self.main_window.tweet_sensitive is True:
if tweet.get('possibly_sensitive'):
theOutput.append(tweet['possibly_sensitive'])
else:
theOutput.append("False")
if self.main_window.tweet_geocoordinates is True:
if tweet['geo'] is not None:
if tweet['geo']['type'] == 'Point':
lat = str(tweet['geo']['coordinates'][0]) + " "
lon = str(tweet['geo']['coordinates'][1])
theOutput.append(lat + lon)
else:
theOutput.append(tweet['geo'])
else:
theOutput.append(tweet['geo'])
if self.main_window.tweet_timestamp is True:
theOutput.append(tweet['created_at'])
wr.writerow(theOutput)
tweets.close()
if __name__ == '__main__':
app = QApplication(sys.argv)
frame = MainWindow()
frame.show()
app.exec_()
I know this is an old post but I ran into a similar problem in a simple app I recently wrote, my solution was to use threading.
I used the worker from:
https://pymotw.com/2/threading/
and the method described in:
http://aadrake.com/using-twitter-as-a-stream-processing-source.html
Basically running the Twython stream as a separate thread feeding text to a queue then I run the rest of the program in a separate loop reading from the queue.

Python action freezes the program

I have this little program I wrote, In it there is a class of methods, and a class that build the window (only one).
from Tkinter import *
from tkMessageBox import *
import socket
import platform ,sys
import subprocess
from multiprocessing.pool import ThreadPool
import Queue
import threading
class Methods(object):
def __init__(self):
#TODO : implement
pass
def getHostName(self):
try:
return socket.gethostname()
except:
return "ERROR :Could'nt get Hostname"
def getOperatingSystem(self):
try:
return platform.system() + " " + platform.release() + " " + platform.version() + " " + sys.getwindowsversion()[4]
except:
return "ERROR :Could'nt get Operating System"
def getHotFixes(self,queue):
try:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
myProcess = subprocess.Popen(
"wmic qfe get HotFixID, InstalledOn",
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
startupinfo = startupinfo)
out, error = myProcess.communicate()
full_list = out.splitlines()
result = ""
for item in full_list:
if item != "" and item != " ":
result += "%s \n" % item
out_number = len(result.splitlines()) - 1
a = "There Are %s Microsoft HotFixes Updates \n\n%s" % (out_number , result)
queue.put(a)
except:
return "ERROR :Could'nt get HotFixes"
#VISUAL
#This class will have an instance of Methods and call every action by itself.
class MainWindow(object):
def __init__(self):
self.root = Tk()
self.root.title('SAAP')
self.root.geometry('610x440+100+100')
#self.root.resizable(0,0)
self.methods = Methods()
def openHostName():
disableAllButtons(self)
result = self.methods.getHostName()
print result
self.textLabelString.set("Host Name")
self.textBox.config(state=NORMAL)
self.textBox.delete("1.0",END)
self.textBox.insert(INSERT,result)
self.textBox.config(state=DISABLED)
enableAllButtons(self)
def openOperatingSystem():
disableAllButtons(self)
result = self.methods.getOperatingSystem()
print result
self.textLabelString.set("Operating System")
self.textBox.config(state=NORMAL)
self.textBox.delete("1.0",END)
self.textBox.insert(INSERT,result)
self.textBox.config(state=DISABLED)
enableAllButtons(self)
def openHotFixes():
queue = Queue.Queue()
thread_ = threading.Thread(
target = self.methods.getHotFixes,
name='Thread1',
args=[queue],
)
thread_.start()
thread_.join()
result = queue.get()
disableAllButtons(self)
self.textLabelString.set("Microsoft Hotfixes")
self.textBox.config(state=NORMAL)
self.textBox.delete("1.0",END)
self.textBox.insert(INSERT,result)
self.textBox.config(state=DISABLED)
enableAllButtons(self)
#items decleration
self.actionLabel = Label(self.root, text = 'Actions',bg='blue',fg='white')
self.button1 = Button(self.root, text = 'Host Name' , command=openHostName)
self.button2 = Button(self.root, text = 'Operating System' , command = openOperatingSystem)
self.button3 = Button(self.root, text = 'Microsoft HotFixes' , command = openHotFixes)
self.button4 = Button(self.root, text = 'N4')
self.button5 = Button(self.root, text = 'Fi5o')
self.button6 = Button(self.root, text = 'C6y')
self.button7 = Button(self.root, text = '7')
self.button8 = Button(self.root, text = '8y')
self.button9 = Button(self.root, text = 'R9s')
self.button10 = Button(self.root, text = '10t')
self.button11 = Button(self.root, text = 'I11s')
self.textLabelString = StringVar()
self.textLabel = Label(self.root,bg='black',fg='white',width=60,textvariable=self.textLabelString)
self.textLabelString.set("Output")
self.textBox = Text(self.root,width=52)
self.textBox.insert(INSERT,"Here's the output")
self.textBox.config(state=DISABLED)
self.scrollBar = Scrollbar(self.root)
self.scrollBar.config(command=self.textBox.yview)
self.textBox.config(yscrollcommand=self.scrollBar.set)
#items placing
self.actionLabel.grid(row=0,column=0,sticky=W+E+N+S,pady=5)
self.button1.grid(row=1,column=0,padx=5,pady=5,sticky=W+E)
self.button2.grid(row=2,column=0,padx=5,pady=5,sticky=W+E)
self.button3.grid(row=3,column=0,padx=5,pady=5,sticky=W+E)
self.button4.grid(row=4,column=0,padx=5,pady=5,sticky=W+E)
self.button5.grid(row=5,column=0,padx=5,pady=5,sticky=W+E)
self.button6.grid(row=6,column=0,padx=5,pady=5,sticky=W+E)
self.button7.grid(row=7,column=0,padx=5,pady=5,sticky=W+E)
self.button8.grid(row=8,column=0,padx=5,pady=5,sticky=W+E)
self.button9.grid(row=9,column=0,padx=5,pady=5,sticky=W+E)
self.button10.grid(row=10,column=0,padx=5,pady=5,sticky=W+E)
self.button11.grid(row=11,column=0,padx=5,pady=5,sticky=W+E)
self.textLabel.grid(row=0,column=1,padx=10,pady=5)
self.textBox.grid(row=1,column=1,rowspan=11,pady=5)
self.scrollBar.grid(row=1,column=2,rowspan=11,sticky=N+S)
def disableAllButtons(self):
self.button1['state'] = DISABLED
self.button2['state'] = DISABLED
self.button3['state'] = DISABLED
self.button4['state'] = DISABLED
self.button5['state'] = DISABLED
self.button6['state'] = DISABLED
self.button7['state'] = DISABLED
self.button8['state'] = DISABLED
self.button9['state'] = DISABLED
self.button10['state'] = DISABLED
self.button11['state'] = DISABLED
def enableAllButtons(self):
self.button1['state'] = NORMAL
self.button2['state'] = NORMAL
self.button3['state'] = NORMAL
self.button4['state'] = NORMAL
self.button5['state'] = NORMAL
self.button6['state'] = NORMAL
self.button7['state'] = NORMAL
self.button8['state'] = NORMAL
self.button9['state'] = NORMAL
self.button10['state'] = NORMAL
self.button11['state'] = NORMAL
def main():
mainw = MainWindow()
mainw.root.mainloop()
if __name__ == "__main__":
main()
Now , My problem is when I press a button it needs to do something and then the output should appear on screen.
BUT, and here comes the but --
when the action takes a bit, it freezes the program until the action is done.
I want to make the program treat maybe the action as a different thread so it won't freeze.
I tried some stuff but it did not worked for me unfortunately ...
Any Help ?
Appreciated!
It is ok to execute your actions in separate threads, however you need to implement a
mechanism for signaling to your main thread (where Tk's loop is running) when actions
are finished, and to get the result(s).
One approach is to have a proper Action class, creating thread objects ; you pass
the method to execute and its arguments, then you start the thread - beforehand,
you register a callback that will be called in your Tk loop when action is finished.
In order to pass results from the thread to the callback, a Queue can be used:
import functools
class Action(threading.Thread):
def __init__(self, method, *args):
threading.Thread.__init__(self)
self.daemon = True
self.method=method
self.args=args
self.queue=Queue.Queue()
def run(self):
self.queue.put(self.method(*self.args))
def register_callback(self, tkroot, callback):
# to be called by Tk's main thread,
# will execute the callback in the Tk main loop
try:
result = self.queue.get_nowait()
except:
# set a timer, to check again for results within 100 milliseconds
tkroot.after(100, functools.partial(self.register_callback,
tkroot, callback))
else:
return callback(result)
EDIT: modification of the original example to show how to apply this to the getHotFixes
method
As an example, here is how to change getHotFixes accordingly:
class Methods(object):
...
def getHotFixes(self):
try:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
myProcess = subprocess.Popen("wmic qfe get HotFixID, InstalledOn",
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
startupinfo = startupinfo)
out, error = myProcess.communicate()
full_list = out.splitlines()
result = ""
for item in full_list:
if item != "" and item != " ":
result += "%s \n" % item
out_number = len(result.splitlines()) - 1
return "There Are %s Microsoft HotFixes Updates \n\n%s" % (out_number , result)
except:
return "ERROR :Could'nt get HotFixes"
Finally, in MainWindow you just need to call the getHotFixes method, register
a callback to do something useful with the result when it's finished using
register_callback and call start() to start the Action thread:
class MainWindow(object):
def __init__(self):
self.root = Tk()
...
def openHotFixes():
disableAllButtons(self)
action = Action(self.methods.getHotFixes)
action.register_callback(self.root, openHotFixesDone)
action.start()
def openHotFixesDone(result):
self.textLabelString.set("Microsoft Hotfixes")
self.textBox.config(state=NORMAL)
self.textBox.delete("1.0",END)
self.textBox.insert(INSERT,result)
self.textBox.config(state=DISABLED)
enableAllButtons(self)
Hope this helps.

Suspected thread issue with pyinotify

I have been working with pyinotify and I am having issues with it where after multiple changes to a folder it simply stops receiving notifications. I have a feeling it is something to do with the the fact that two threads are running; namely the notifier thread and the wxpython thread.
The purpose of the app is to essentially load a picture to the screen when it detects an ip connection, monitor a folder for the file 'Checklist' and based on that file do some processing i.e move files around.
It works intermittently but being a python newbie Im not exactly sure what the issue might be as I have basically taken the threaded example and worked around it. Sometimes, it only gets one notification and stops receiving file change notifications.
Additionally, if I restart the linux box and try again, it works for a good number of file changes and then stops receiving notifications again which makes me think that perhaps its not releasing the watches properly?
Any help would be greatly appreciated as well as optimizations and improvements are very welcome. I'm sure I could learn a lot from the feedback. The code is below
import pyinotify
import os.path
import shutil
import errno
import subprocess
import logging
import wx
import time
import signal
import sys
#update CHECKLIST name
CHECKLIST = 'Checklist' #this must exist in the update archive
#static values
DIR_UPDATE = 'd'
FILE_UPDATE = 'f'
PING_IP = ' localhost' # change in production
#paths
WATCH_PATH = '/home/test'
LOG_PATH = '/home/test/update.log'
CONNECTED_IMG = 'update.jpg'
UPDATING_IMG = 'updating.png'
#msgs
UPDATEFOUND_MSG = ' Update Found '
UPDATEUNZIPPEDSTART_MSG = ' Update unzipping '
UPDATEUNZIPPED_MSG = ' Update unzipped '
UPDATEFILE_MSG = ' Update file '
UPDATEFILEMSG_CONT = ' moved into path '
REMOVEFILEMSG_CONT = ' removed from update folder '
UPDATECOMPLETE_CONT = ' Update complete'
ROADANGELRESTART_MSG = ' Update restarting app '
DIRCREATED_MSG = ' Directory created at path '
#errors
UPDATEFAILED_MSG = ' Update process failed on '
BADLYFORMED_MSG = ' Badly formed src/dest combination '
UPDATESRCUNAVAILABLE = ' Invalid update file specified '
UPDATEDESTUNAVAILABLE = ' Invalid update destination specified '
INVALIDUPDATEFORMAT = ' Invalid format string '
#on startup create the watchfolder if it doesnt exist
WM = pyinotify.WatchManager() # Watch Manager
WM_MASK = pyinotify.IN_CLOSE_WRITE # watched events
#setup logger
LOGGER = logging.getLogger('Updater')
LOG_HANDLE = logging.FileHandler(LOG_PATH)
FORMATTER = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
LOG_HANDLE.setFormatter(FORMATTER)
LOGGER.addHandler(LOG_HANDLE)
LOGGER.setLevel(logging.INFO)
#Global values used primarily in the main function loop
HANDLER = None
NOTIFIER = None
WDD = None
UPDATE_UI = None
WINDOW = None
CURR_IMG = None
LAST_CURRIMG = None
class EventHandler(pyinotify.ProcessEvent):
VERBOSE = False
""" Main class to monitor file events and process accordingly"""
def process_IN_CLOSE_WRITE(self, event):
""" Only executes when a Checklist has finished being written to"""
path = event.pathname
print 'evt'
#look for the update_ready file before processing
if (os.path.basename(path) == 'Checklist'):
EventHandler.parse_updates(WATCH_PATH)
global CURR_IMG
CURR_IMG = os.path.join(WATCH_PATH, UPDATING_IMG)
show_window()
print 'update completed'
time.sleep(1000)
#classmethod
def parse_updates(cls, path):
""" parses update files """
#handle errors for opening the file
file_path = os.path.join(path, CHECKLIST)
print file_path
files = open(file_path)
#handle errors for malformed tuples-done
#handle errors for unavailable files-done
#handle errors for unavailable dests-done #created automatically
#handle permission errors
for line in files:
#remove linebreaks etc and ensure its not empty
if line.strip():
array = line.split('=')
length = len(array)
if length == 3:
EventHandler.process_line(path, array)
else:
if length > 0:
EventHandler.print_bad_msg(array[0])
else:
EventHandler.print_bad_msg()
print 'removing ', file_path
os.remove(file_path) #remove the checklist file
#classmethod
def mkdir(cls, path):
""" makes a directory from a path"""
try:
os.mkdir(path)
print DIRCREATED_MSG, path
except OSError, err:
print err
if err.errno != errno.EEXIST: #thrown when the dir already exists
return False
#classmethod
def move_file(cls, src, dest):
""" moves a file from src to dest and remove after
expects that the dest already exists at this point
otherwise ignores the move"""
#print 'moving from', src, 'to ', dest
if os.path.isfile(dest):
shutil.copy2(src, dest)
else:
print UPDATEDESTUNAVAILABLE
#remove the src file when done
os.remove(src)
#classmethod
def process_line(cls, path, array):
""" process a line from the checklist"""
#remove newlines etc
update_file = array[0].strip()
update_src = os.path.join(path, update_file)
update_dest = array[1].strip()
update_type = array[2].strip()
#ensure we have valid values in all three fields
if update_file and update_dest and update_type:
#ensure the src file exists
if os.path.isfile(update_src):
#check if destination is directory and
#copy the file into the directory
if update_type == DIR_UPDATE:
EventHandler.mkdir(update_dest)
dest = os.path.join(update_dest, update_file)
EventHandler.move_file(update_src, dest)
else:
EventHandler.move_file(update_src, update_dest)
else:
print UPDATESRCUNAVAILABLE
else:
print INVALIDUPDATEFORMAT
#classmethod
def print_bad_msg(cls, msg = ''):
""" print a badly formed message with optional value"""
if msg:
print BADLYFORMED_MSG, msg
else:
print BADLYFORMED_MSG
class UpdateFrame(wx.Frame):
""" Displays update images to screen"""
def __init__(self, path):
wx.Frame.__init__(self, None, wx.ID_ANY)
image_file = path
image = wx.Bitmap(image_file)
image_size = image.GetSize()
# set the frame size to fit the screen size
self.SetClientSize(wx.DisplaySize())
# bitmap's upper left corner is in frame position (x, y)
# by default pos=(0, 0)
wx.StaticBitmap(self, wx.ID_ANY, image, size = image_size)
# the parent is the frame
self.SetTitle('Update Mode')
def ping_ip():
""" ping once to establish connection """
ret = subprocess.call("ping -c 1 %s" % PING_IP,
shell=True,
stdout=open('/dev/null', 'w'),
stderr=subprocess.STDOUT)
if ret == 0:
return True
else:
return False
def show_window():
""" update screen window when currimage changes is set """
global UPDATE_UI
global WINDOW
global CURR_IMG
global LAST_CURRIMG
if LAST_CURRIMG != CURR_IMG:
if not UPDATE_UI:
UPDATE_UI = wx.App()
if not WINDOW:
WINDOW = UpdateFrame(CURR_IMG)
UPDATE_UI.ExitMainLoop()
while(UPDATE_UI.IsMainLoopRunning()):
pass
WINDOW.Destroy()
WINDOW = UpdateFrame(CURR_IMG)
WINDOW.Show(True)
UPDATE_UI.MainLoop()
LAST_CURRIMG = CURR_IMG
print 'changed'
def in_updatemode():
return ping_ip()
while True:
try:
if not in_updatemode():
print 'waiting for connect'
time.sleep(3)
if NOTIFIER:
NOTIFIER.stop()
else:
if not HANDLER:
HANDLER = EventHandler()
if not NOTIFIER:
NOTIFIER = pyinotify.ThreadedNotifier(WM, HANDLER)
NOTIFIER.start()
if not WDD:
WDD = WM.add_watch(WATCH_PATH, WM_MASK, rec=True,quiet=False)
# ip is active so show the image and start the notifier
# state = ip active
CURR_IMG = os.path.join(WATCH_PATH, CONNECTED_IMG)
show_window()
print 'here'
except KeyboardInterrupt:
print 'out'
NOTIFIER.stop()
break
I basically found out that that the issue was indeed the fact that pyinotify's threaded notifier and wxPython's main loop dont play nice together.
The solution was to create a custom main loop (something I didn't know you could do in the first place) and place the pyinotify's threaded notifier within that loop. That way it ran as part of the wxPython main loop.
I got the idea from
http://www.java2s.com/Open-Source/Python/GUI/wxPython/wxPython-src-2.8.11.0/wxPython/samples/mainloop/mainloop.py.htm
Code below explains the concept
class CustomApp(wx.App):
def MainLoop(self):
global HANDLER
global WM
global NOTIFIER
global WDD
global UPDATE_UI
global PING_TIMER
# Create an event loop and make it active. If you are
# only going to temporarily have a nested event loop then
# you should get a reference to the old one and set it as
# the active event loop when you are done with this one...
evtloop = wx.EventLoop()
old = wx.EventLoop.GetActive()
wx.EventLoop.SetActive(evtloop)
# This outer loop determines when to exit the application,
# for this example we let the main frame reset this flag
# when it closes.
while self.keepGoing:
# At this point in the outer loop you could do
# whatever you implemented your own MainLoop for. It
# should be quick and non-blocking, otherwise your GUI
# will freeze.
# call_your_code_here()
if not HANDLER:
HANDLER = EventHandler()
if not WM:
WM = pyinotify.WatchManager() # Watch Manager
if not NOTIFIER:
NOTIFIER = pyinotify.ThreadedNotifier(WM, HANDLER)
NOTIFIER.start()
print 'notifier started'
if not WDD:
WDD = WM.add_watch(WATCH_PATH, WM_MASK, rec=True,quiet=False)
# This inner loop will process any GUI events
# until there are no more waiting.
while evtloop.Pending():
evtloop.Dispatch()
# Send idle events to idle handlers. You may want to
# throttle this back a bit somehow so there is not too
# much CPU time spent in the idle handlers. For this
# example, I'll just snooze a little...
time.sleep(0.10)
self.ProcessIdle()
wx.EventLoop.SetActive(old)
def OnInit(self):
global UPDATE_UI
if not UPDATE_UI:
UPDATE_UI = Updater()
UPDATE_UI.Show()
self.SetTopWindow(UPDATE_UI)
self.keepGoing = True
return True
"--------------------------------------------------------------------------------------"
Watcher()
app = CustomApp(False)
Additionally, I wanted to catch SIGINT in the program and I solved it using the recipe from this url
http://code.activestate.com/recipes/496735-workaround-for-missed-sigint-in-multithreaded-prog/
Hope this helps another python newbie or oldie :)

Categories

Resources