Why does it say that curSongJson is not defined if I'm defining it in the displaySongs function when I run the application? I had it working before by removing the displaySongs function and just using a while loop but I need the function for tkinter and updating the label.
import requests
import time
import tkinter
token = ''
endpoint = "https://api.spotify.com/v1/me/player/currently-playing"
spotifyHeaders = {'Authorization':'Bearer ' + token}
requestAmount = 1
#window = tkinter.Tk()
# imageLabel = tkinter.Label(window)
# imageLabel.pack()
def GrabSpotifyCurSong():
return curSongJson['item']['name']
def GrabSpotifyCurArtist():
return curSongJson['item']['artists'][0]['name']
def GrabCurrentSongImage():
return curSongJson['item']['album']['images'][0]['url']
def displaySongs():
try:
curSong = requests.get(endpoint, headers=spotifyHeaders)
curSongJson = curSong.json()
except:
print("Please start listening to a song")
time.sleep(2)
# with open('CurrentSong.jpg','wb+') as SongImage:
# response = requests.get(GrabCurrentSongImage())
# SongImage.write(response.content)
currentSong = GrabSpotifyCurSong()
currentArtist = GrabSpotifyCurArtist()
# imageLabel['text'] = f'{currentArtist} - {currentSong}'
print(f'{currentArtist} - {currentSong}')
# window.after(4000,displaySongs)
displaySongs()
# window.mainloop()
The curSongJson is a local variable so it's scope is limited to the function where it is defined. So, it will not be accessible outside the displaySongs() method.
You can pass the currSongJson to other functions as parameters where needed like -
import requests
import time
import tkinter
token = ''
endpoint = "https://api.spotify.com/v1/me/player/currently-playing"
spotifyHeaders = {'Authorization':'Bearer ' + token}
requestAmount = 1
#window = tkinter.Tk()
# imageLabel = tkinter.Label(window)
# imageLabel.pack()
def GrabSpotifyCurSong(curSongJson):
return curSongJson['item']['name']
def GrabSpotifyCurArtist(curSongJson):
return curSongJson['item']['artists'][0]['name']
def GrabCurrentSongImage(curSongJson):
return curSongJson['item']['album']['images'][0]['url']
def displaySongs():
try:
curSong = requests.get(endpoint, headers=spotifyHeaders)
curSongJson = curSong.json()
except:
print("Please start listening to a song")
time.sleep(2)
# with open('CurrentSong.jpg','wb+') as SongImage:
# response = requests.get(GrabCurrentSongImage(curSongJson))
# SongImage.write(response.content)
currentSong = GrabSpotifyCurSong(curSongJson)
currentArtist = GrabSpotifyCurArtist(curSongJson)
# imageLabel['text'] = f'{currentArtist} - {currentSong}'
print(f'{currentArtist} - {currentSong}')
# window.after(4000,displaySongs)
displaySongs()
# window.mainloop()
Related
Problem:
Telegram bot doesn't recognise seen messages and keeps responding to the latest message until I send "quit" or crtl-c in command line.
Completely new to python. There may be a flaw in my programming logic.
In 'for last_update_id in updates["result"]' I tried to add 1 to the last_update_id variable after each loop. But the variable doesn't seem to update.
# chatbot.py not included. It trains NN model.
import json
import requests
import time
import urllib
import telegram
TOKEN = "xxx"
URL = "https://api.telegram.org/bot{}/".format(TOKEN)
def get_url(url):
response = requests.get(url)
content = response.content.decode("utf8")
return content
def get_json_from_url(url):
content = get_url(url)
js = json.loads(content)
return js
def get_updates(offset): #gets json file from URL
url = URL + "getUpdates"
if offset:
url += "?offset={}".format(offset)
js = get_json_from_url(url)
return js
def get_last_update_id(updates):
update_ids = []
for update in updates["result"]:
update_ids.append(int(update["update_id"]))
return max(update_ids)
def get_last_chat_text(updates):
num_updates = len(updates["result"])
last_update = num_updates - 1
text = updates["result"][last_update]["message"]["text"] #text input
return text
def get_last_chat_id(updates):
chat_id = updates["result"][-1]["message"]["chat"]["id"]
return chat_id
def send_message(output,chat_id):
bot = telegram.Bot(token=TOKEN)
bot.sendMessage(chat_id=chat_id, text = output)
def main():
input_text = get_last_chat_text(updates)
return input_text
print("Let's chat! (type 'quit' to exit)")
last_update_id = None
while True:
updates = get_updates(last_update_id) #returns json file
last_update_id = get_last_update_id(updates) #returns max_update_id
for last_update_id in updates["result"]:
main()
input_text = main()
if input_text == "quit":
break
input_text = tokenize(input_text)
X = bag_of_words(input_text, all_words)
X = X.reshape(1, X.shape[0])
X = torch.from_numpy(X).to(device)
output = model(X)
_, predicted = torch.max(output, dim=1)
tag = tags[predicted.item()]
probs = torch.softmax(output, dim=1)
prob = probs[0][predicted.item()]
if prob.item() > 0.75:
for intent in intents['intents']:
if tag == intent["tag"]:
output = f"{random.choice(intent['responses'])}"
else:
output = f"{bot_name}: I do not understand..."
print(output)
chat_id = get_last_chat_id(updates)
print(chat_id)
send_message(output, chat_id)
time.sleep(0.1)
last_update_id =+ 1 #returns max_id in the json file and adds 1
continue
I managed to fix this problem by adding a break in the loop so it loops back to the outside 'while' loop. Below is the edited code:
# chatbot.py module imported above this line not included. It trains NN model.
import json
import requests
import time
import urllib
import telegram
TOKEN = "XXX"
URL = "https://api.telegram.org/bot{}/".format(TOKEN)
def get_url(url):
response = requests.get(url)
content = response.content.decode("utf8")
return content
def get_json_from_url(url):
content = get_url(url)
js = json.loads(content)
return js
def get_updates(offset): #gets json file from URL
url = URL + "getUpdates"
if offset:
url += "?offset={}".format(offset)
js = get_json_from_url(url)
return js
def get_last_update_id(updates):
update_ids = []
for update in updates["result"]:
update_ids.append(update["update_id"])
return max(update_ids, default = last_update_id)
def get_last_chat_text(updates):
# num_updates = len(updates["result"])
# last_update = num_updates - 1
text = updates["result"][-1]["message"]["text"] #text input
return text
def get_last_chat_id(updates):
chat_id = updates["result"][-1]["message"]["chat"]["id"]
return chat_id
def send_message(output,chat_id):
bot = telegram.Bot(token=TOKEN)
bot.sendMessage(chat_id=chat_id, text = output)
def main():
input_text = get_last_chat_text(updates)
return input_text
bot_name = "XXX"
print("Let's chat! (type 'quit' to exit)")
last_update_id = 0
while True:
updates = get_updates(last_update_id) #returns json file
for last_update_id in updates["result"]:
main()
input_text = main()
if input_text == "quit":
break
input_text = tokenize(input_text)
X = bag_of_words(input_text, all_words)
X = X.reshape(1, X.shape[0])
X = torch.from_numpy(X).to(device)
output = model(X)
_, predicted = torch.max(output, dim=1)
tag = tags[predicted.item()]
probs = torch.softmax(output, dim=1)
prob = probs[0][predicted.item()]
if prob.item() > 0.75:
for intent in intents['intents']:
if tag == intent["tag"]:
output = f"{random.choice(intent['responses'])}"
else:
output = f"{bot_name}: I do not understand..."
print(output)
chat_id = get_last_chat_id(updates)
print(chat_id)
send_message(output, chat_id)
time.sleep(0.1)
break
last_update_id = get_last_update_id(updates) + 1 #returns max_id in the json file and adds 1
1.This is my code here you can see the send() method. I want to use UserQus and BotAns out of send method in on_closing() method for sore in firebase database, I can't do it so, please give me solution for it.
# Creating GUI with tkinter
import tkinter
from tkinter import *
base = Tk()
# from firebase import firebase
UserQus = []
BotAns = []
# This method is for tkinter button
def send():
msg = EntryBox.get("1.0", 'end-1c').strip()
EntryBox.delete("0.0", END)
if msg != '':
ChatLog.config(state=NORMAL)
ChatLog.insert(END, "You: " + msg + '\n\n')
ChatLog.config(foreground="#442265", font=("Verdana", 14))
res = chatbot_response(msg)
ChatLog.insert(END, "Bot: " + res + '\n\n')
ChatLog.config(state=DISABLED)
ChatLog.yview(END)
# here msg an res are append in UserQus and BotAns
UserQus.append(msg)
BotAns.append(res)
def on_closing():
from firebase import firebase
firebase = firebase.FirebaseApplication('https://fir-demopython.firebaseio.com/', None)
# here UserQus and BotAns are sore in data variable for define in firebase.post() # that store in database
data = {'You': UserQus, 'Bot': BotAns}
res = firebase.post('fir-demopython/DemoTbl', data)
if messagebox.askokcancel("Quit", "Do you want to quit?"):
base.destroy()
Quite simply: you are defining the on_closing() function within the send() function. This means that 1/ it's created anew each time send is called, and 2/ it's only visible within the send function (it's a local variable).
Just move your on_closing() definition outside the send() function (and move your imports at the top level too), and the problem is solved:
# imports should NOT be in functions
from firebase import firebase
def send():
msg = EntryBox.get("1.0", 'end-1c').strip()
EntryBox.delete("0.0", END)
if msg != '':
ChatLog.config(state=NORMAL)
ChatLog.insert(END, "You: " + msg + '\n\n')
ChatLog.config(foreground="#442265", font=("Verdana", 12))
res = chatbot_response(msg)
ChatLog.insert(END, "Bot: " + res + '\n\n')
ChatLog.config(state=DISABLED)
ChatLog.yview(END)
UserQus.append(msg)
BotAns.append(res)
# and this should not be defined within the `send()` function, of course
def on_closing():
firebase = firebase.FirebaseApplication('https://fir-demopython.firebaseio.com/', None)
data = {'You': UserQus, 'Bot': BotAns}
res = firebase.post('fir-demopython/DemoTbl', data)
if messagebox.askokcancel("Quit", "Do you want to quit?"):
base.destroy()
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.
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.
I am new to python and I am implementing a simple serial adquisition in a thread.
I can adquire the data using a class by
class CaptureAngles(threading.Thread, port)
def __init__(self):
threading.Thread.__init__(self)
self.port_name = port
...
def run():
self.connect(self.port_name)
...
However, to better integrate with a graphical interface using the traits library I wrote the code as the following, which is no longer working. I am not able to define the attribute of a thread that is started from traits, what am I doing wrong?
This is the error reported
AttributeError: 'CaptureAngles' object has no attribute 'port_name'
And this the full code:
from threading import Thread
from time import sleep
from enthought.traits.api import *
from enthought.traits.ui.api import View, Item, ButtonEditor
from Queue import Queue
class TextDisplay(HasTraits):
string = String()
view= View( Item('string',show_label=False, springy=True, style='custom' ))
class CaptureAngles(Thread):
self.port_name = String('COM5')
def connect(self, port_name):
self.port = serial.Serial(
port = port_name,
baudrate = 9600,
)
self.display.string='Opening Serial Port...' + self.display.string
self.port.close()
self.port.open()
def run(self):
self.connect(self.port_name)
self.display.string = 'Arduino started\n' + self.display.string
self.port.flushInput()
self.port.flushOutput()
self.port.readline() # Discard first package (can be corrupt)
while not self.wants_abort:
rcv = self.port.readline() # Read the data and split into words
angle = int(rcv)
self.display.string = '%d angle captured\n' % n_img \
+ self.display.string
self.close()
def close(self):
self.port.close()
self.display.string='...Serial Port Closed!' + self.display.string
class Arduino(HasTraits):
start_stop_capture = Button()
display = Instance(TextDisplay)
capture_angles = Instance(CaptureAngles)
capture_angles.port_name = 'COM5'
view = View(Item('start_stop_capture', show_label=False ))
def _start_stop_capture_fired(self):
if self.capture_angles and self.capture_angles.isAlive():
self.capture_angles.wants_abort = True
else:
self.capture_angles = CaptureAngles()
self.capture_angles.wants_abort = False
self.capture_angles.display = self.display
self.capture_angles.start()
class MainWindow(HasTraits):
display = Instance(TextDisplay, ())
arduino = Instance(Arduino)
def _arduino_default(self):
return Arduino(display=self.display)
view = View('arduino','display', style="custom", resizable=True)
if __name__ == '__main__':
MainWindow().configure_traits()
Ok, I got it: I was adding the attribute port_name before creating the instance.
class Arduino(HasTraits):
start_stop_capture = Button()
display = Instance(TextDisplay)
capture_angles = Instance(CaptureAngles)
capture_angles.port_name = 'COM5' # <-- wrong: the object is not created yet
...
instead of:
def _start_stop_capture_fired(self):
if self.capture_angles and self.capture_angles.isAlive():
self.capture_angles.wants_abort = True
else:
self.capture_angles = CaptureAngles()
self.capture_angles.port_name = 'COM5' # <-- correct
...