Python How do i fix Tkinter window crashing due to sleep function - python

I have a problem with my Tkinter window crashing due to the usage of the 'sleep' function in the code below.
The programm in the background works just fine, even if the window crashed a long time ago.
How can I use functions like time.time to ensure that I don't make queries to the API too frequently rather than making a blocking call to time.sleep.
In other words, once I follow a list of new users, how do I prevent the program from making another request in the next 30 seconds?
def follow_users(self,users_list):
api = self.api
api.login()
api.getSelfUsersFollowing()
result = api.LastJson
for user in result['users']:
following_users.append(user['pk'])
for user in users_list:
if not user['pk'] in following_users:
print('Following #' + user['username'])
api.follow(user['pk'])
# set this really long to avoid from suspension
sleep(30)
else:
print('Already following #' + user['username'])
sleep(15)
def unfollow_users(self):
api = self.api
api.login()
api.getSelfUserFollowers()
result = api.LastJson
for user in result['users']:
follower_users.append({'pk':user['pk'], 'username':user['username']})
api.getSelfUsersFollowing()
result = api.LastJson
for user in result['users']:
following_users.append({'pk':user['pk'],'username':user['username']})
for user in following_users:
if not user['pk'] in follower_users:
print('Unfollowing #' + user['username'])
api.unfollow(user['pk'])
sleep(20)
# set this really long to avoid from suspension

I would start by writing a function that follows a single user, and nothing more. It doesn't need to loop over a list or sleep or anything like that, it should just follow that one user.
For example:
def follow_user(self, user):
... code to follow this user ...
Next, define a function that pulls one user off of a list, calls the follow_user function, and then re-schedules itself to run again after a timeout.
The following example assumes there's a global variable named root which represents the root window, but you can use any widget you want. It also assumes that the class maintains a list of users to be followed in an instance variable named self.users.
def follow_users(self):
if self.users:
user = self.users.pop()
self.follow_user(user)
root.after(30000, self.follow_users)
Then, once your program starts, call this function exactly once. It will call itself once every 30 seconds. If there is at least one user in self.users it will pull it from the list and follow the user. 30 seconds later it will do it again, and then again, and so on. You can update self.users whenever you want and that user will eventually get followed.

Related

How do I use Tkinter and Telebot together?

I've been working on a Python project where I'm stuck at one point. Basically, I have a GUI that updates automatically once 5 seconds. I have done it using the method .after(5000, main) where main function looks like the following.
def main():
global lastthreshold
global run
global lastaction
global bakiye
global lif
global gsecenek
paributl, binancedolar, paribudolar, dolar = getData()
farklow, farkhigh = diff(paributl, binancedolar, paribudolar)
data = guiDataMaker(paributl, binancedolar, paribudolar, farklow, farkhigh)
gui = Gui(data, dolar, lastthreshold, alsatmiktar, bakiye)
#print(*data, sep="\n")
#print(*cooldown.items(), sep="\n")
if run:
lif = gui.gui()
for i in paributl.keys():
cooldown[i] = "NONE"
run = False
gui.secenek.set(gsecenek)
runbot = runBot(lastthreshold, bakiye, data, cooldown, alsatmiktar)
if runbot[0] != None:
currency, guncelfark, fiyat, newbakiye, buydate, islem = runbot
bakiye = newbakiye
guncelislem = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
lastaction = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
gui.updateGuiData(lif, cooldown, guncelislem)
else:
gui.updateGuiData(lif, cooldown, lastaction)
src.after(5000, main)
src.mainloop()
main()
#src.mainloop()
#bot.polling()
My actual code is something around 450 lines, so I think pasting it here would be a waste of space. Though, I can do that if it helps you help me more easily. Anyway, the above code works perfectly with a single thread which is created by src.mainloop().
In the project, what I'm trying to do is basically a trading bot. To help the user see what is going on, I created a gui using Tkinter library. I create the gui only once and I only update the data every 5 seconds.
What I want to achieve is to create a Telegram Bot which the user can interact with. For example use should be able to type /BTC-USD to get information about bitcoin-dolar. So far, I achieved to send the user every buy/sell operation of the bot in telegram. However, what I'm stuck right now is to get commands of the user since it requires me to use bot.polling() which is another thread. Wherever I put this line of code in my code, it causes GUI to "not responding".
So in simpler words, my question is how can I have a Telegram Bot interacting with a user by sending messages and getting commands, also a GUI that is updated automatically every 5 seconds?

How to create a non cumulative version of sleep() in Python

Please, accept my apologies for the delayed feedback. It was impossible before.
I'll explain it better.
I've built a Midi - OSC control surface for Reaper in Python. It receives and sends OSC messages.
I use UP and DOWN arrows on the keyboard to move through tracks in Reaper. I can move very fast through tracks until I find the one I want to stay on. Then, my script sends a OSC message to receive everything from Reaper using PythonOSC.
I will explain using code.
That function receives OSC messages (e.g.: /track/volume, etc.) Each time the client receives /select, it tells my script that a track changed in Reaper.
def OSC(self, *args):
s = re.search(r'/track/(.+)', args[0])
if s:
if s.group(1) == 'select' and args[1] == 0:
"""I want that next line to be executed only when about 0.5 second had passed
since the last time it enters that point. It is to avoid that PythonOSC sends
that line 10 times in 1 second and overloads the process."""
client.send_message('/device/track/follows/last_touched', 1)
I imagined something like that but I don't know if it's overkill, if I miss something.
Each time I press up or down, a timer starts in a thread or something. It resets each time.
When I land on my track, the timer continues until 0.5 second and then, launches my client.send_message() line.
Is it too much? The thing is, a thread will still have to be killed or something. I'm very new to Python.
The example given, with the decorator is great but it activates the function (in my case, it's finally only a line of code) when it's triggred within 0.5 second while I want it to be triggered after a 0.5 second pause following the last time it's called.
Note: I realize that my code example is all sent in a block in the preview box. I indented it but it breaks it. I'm very sorry for that, I can't see the option I need to use.
I'm not sure that I've interpreted your question correctly, but if you're trying to make function execution to take not less than provided time, you can use this decorator
from time import time, sleep
def timed(time_to_sleep):
def decorator(function):
def wrapper(*args, **kwargs):
start_time = time()
result = function(*args, **kwargs)
time_to_sleep_left = time_to_sleep - (time() - start_time)
if time_to_sleep_left > 0:
sleep(time_to_sleep_left)
return result
return wrapper
return decorator
Usage:
#timed(0.5)
def test():
pass

Python GUI call function repeatedly and have button Presses

I'm having this issue where my GUI program calls another class repeatedly (say 5 times a second), it calls class.Process(). I also have buttons that call class.Process() and the gentlemen who gave me the code for the class.Process() says that when I have the two operating at once (repeatedly calling and buttons) they could overlap and cause errors. Is there a way around this so I don't have a possible 'double-call' of Process()?
thread = threading.Thread(target=self.run,args=()) \
thread.daemon = True \
thread.start()
def run(self):
self.T.insert(END,'\n')
self.T.insert(END,'\n')
while True:
boolean = self.shutterclass.IsIdle()
while not boolean:
self.shutterclass.Process()
message = self.shutterclass.GetStatusMsg()
if message:
self.T.insert(END,message)
self.T2.insert(END,message)
t.sleep(0.2)
The above code calls the function repeatedly and the below code is a sample of a button press.
def isclicked(self,FPress,HPress,QBPress,SPress,EPress,JDPress,JCPress):
'''Function to tell if a button is pressed.'''
if FPress == True:
now = datetime.now()
self.shutterclass.Process(command = 'FIRE')
self.T.insert(END,"\n" + now.strftime("%H:%M:%S"))
self.T.insert(END,'\n' + 'Command: FIRE')
self.T2.insert(END,"\n" + now.strftime("%H:%M:%S"))
self.T2.insert(END,'\n' + 'Command: FIRE')
self.T.insert(END, "\n" + f"{self.shutterclass.GetStatusMsg()}")
You could make it so that instead of doing the call to Process in the while loop, you call the button press event instead. That way both user input and program call go through the button press method. Then if the button press method does not have any inherent state, you could possibly avoid any clashes due to instances of the button object.
From what I can tell, the thread automatically calls the shutterclass. Process when it is idle, I will assume that means the user is not having any input in that object? But then you call the process method, which will change it's status and so the while loop will evaluate to true. One way to fix this could be to put the isIdle status of the shutter class into a different memory object, which will implement a semaphore. That way the callers of that resource will have to queue up in order to modify it, thus making it less probably that they both access it simultaneously.

How to transfer a value from a function in one script to another script without re-running the function (python)?

I'm really new to programming in general and very inexperienced, and I'm learning python as I think it's more simple than other languages. Anyway, I'm trying to use Flask-Ask with ngrok to program an Alexa skill to check data online (which changes a couple of times per hour). The script takes four different numbers (from a different URL) and organizes it into a dictionary, and uses Selenium and phantomjs to access the data.
Obviously, this exceeds the 8-10 second maximum runtime for an intent before Alexa decides that it's taken too long and returns an error message (I know its timing out as ngrok and the python log would show if an actual error occurred, and it invariably occurs after 8-10 seconds even though after 8-10 seconds it should be in the middle of the script). I've read that I could just reprompt it, but I don't know how and that would only give me 8-10 more seconds, and the script usually takes about 25 seconds just to get the data from the internet (and then maybe a second to turn it into a dictionary).
I tried putting the getData function right after the intent that runs when the Alexa skill is first invoked, but it only runs when I initialize my local server and just holds the data for every new Alexa session. Because the data changes frequently, I want it to perform the function every time I start a new session for the skill with Alexa.
So, I decided just to outsource the function that actually gets the data to another script, and make that other script run constantly in a loop. Here's the code I used.
import time
def getData():
username = '' #username hidden for anonymity
password = '' #password hidden for anonymity
browser = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
browser.get("https://gradebook.com") #actual website name changed
browser.find_element_by_name("username").clear()
browser.find_element_by_name("username").send_keys(username)
browser.find_element_by_name("password").clear()
browser.find_element_by_name("password").send_keys(password)
browser.find_element_by_name("password").send_keys(Keys.RETURN)
global currentgrades
currentgrades = []
gradeids = ['2018202', '2018185', '2018223', '2018626', '2018473', '2018871', '2018886']
for x in range(0, len(gradeids)):
try:
gradeurl = "https://www.gradebook.com/grades/"
browser.get(gradeurl)
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:3]
if grade[2] != "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:4]
if grade[1] == "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:1]
currentgrades.append(grade)
except Exception:
currentgrades.append('No assignments found')
continue
dictionary = {"class1": currentgrades[0], "class2": currentgrades[1], "class3": currentgrades[2], "class4": currentgrades[3], "class5": currentgrades[4], "class6": currentgrades[5], "class7": currentgrades[6]}
return dictionary
def run():
dictionary = getData()
time.sleep(60)
That script runs constantly and does what I want, but then in my other script, I don't know how to just call the dictionary variable. When I use
from getdata.py import dictionary
in the Flask-ask script it just runs the loop and constantly gets the data. I just want the Flask-ask script to take the variable defined in the "run" function and then use it without running any of the actual scripts defined in the getdata script, which have already run and gotten the correct data. If it matters, both scripts are running in Terminal on a MacBook.
Is there any way to do what I'm asking about, or are there any easier workarounds? Any and all help is appreciated!
It sounds like you want to import the function, so you can run it; rather than importing the dictionary.
try deleting the run function and then in your other script
from getdata import getData
Then each time you write getData() it will run your code and get a new up-to-date dictionary.
Is this what you were asking about?
This issue has been resolved.
As for the original question, I didn't figure out how to make it just import the dictionary instead of first running the function to generate the dictionary. Furthermore, I realized there had to be a more practical solution than constantly running a script like that, and even then not getting brand new data.
My solution was to make the script that gets the data start running at the same time as the launch function. Here was the final script for the first intent (the rest of it remained the same):
#ask.intent("start_skill")
def start_skill():
welcome_message = 'What is the password?'
thread = threading.Thread(target=getData, args=())
thread.daemon = True
thread.start()
return question(welcome_message)
def getData():
#script to get data here
#other intents and rest of script here
By design, the skill requested a numeric passcode to make sure I was the one using it before it was willing to read the data (which was probably pointless, but this skill is at least as much for my own educational reasons as for practical reasons, so, for the extra practice, I wanted this to have as many features as I could possibly justify). So, by the time you would actually be able to ask for the data, the script to get the data will have finished running (I have tested this and it seems to work without fail).

Python Memory leak using Yocto

I'm running a python script on a raspberry pi that constantly checks on a Yocto button and when it gets pressed it puts data from a different sensor in a database.
a code snippet of what constantly runs is:
#when all set and done run the program
Active = True
while Active:
if ResponseType == "b":
while Active:
try:
if GetButtonPressed(ResponseValue):
DoAllSensors()
time.sleep(5)
else:
time.sleep(0.5)
except KeyboardInterrupt:
Active = False
except Exception, e:
print str(e)
print "exeption raised continueing after 10seconds"
time.sleep(10)
the GetButtonPressed(ResponseValue) looks like the following:
def GetButtonPressed(number):
global buttons
if ModuleCheck():
if buttons[number - 1].get_calibratedValue() < 300:
return True
else:
print "module not online"
return False
def ModuleCheck():
global moduleb
return moduleb.isOnline()
I'm not quite sure about what might be going wrong. But it takes about an hour before the RPI runs out of memory.
The memory increases in size constantly and the button is only pressed once every 15 minutes or so.
That already tells me that the problem must be in the code displayed above.
The problem is that the yocto_api.YAPI object will continue to accumulate _Event objects in its _DataEvents dict (a class-wide attribute) until you call YAPI.YHandleEvents. If you're not using the API's callbacks, it's easy to think (I did, for hours) that you don't need to ever call this. The API docs aren't at all clear on the point:
If your program includes significant loops, you may want to include a call to this function to make sure that the library takes care of the information pushed by the modules on the communication channels. This is not strictly necessary, but it may improve the reactivity of the library for the following commands.
I did some playing around with API-level callbacks before I decided to periodically poll the sensors in my own code, and it's possible that some setting got left enabled in them that is causing these events to accumulate. If that's not the case, I can't imagine why they would say calling YHandleEvents is "not strictly necessary," unless they make ARM devices with unlimited RAM in Switzerland.
Here's the magic static method that thou shalt call periodically, no matter what. I'm doing so once every five seconds and that is taking care of the problem without loading down the system at all. API code that would accumulate unwanted events still smells to me, but it's time to move on.
#noinspection PyUnresolvedReferences
#staticmethod
def HandleEvents(errmsgRef=None):
"""
Maintains the device-to-library communication channel.
If your program includes significant loops, you may want to include
a call to this function to make sure that the library takes care of
the information pushed by the modules on the communication channels.
This is not strictly necessary, but it may improve the reactivity
of the library for the following commands.
This function may signal an error in case there is a communication problem
while contacting a module.
#param errmsg : a string passed by reference to receive any error message.
#return YAPI.SUCCESS when the call succeeds.
On failure, throws an exception or returns a negative error code.
"""
errBuffer = ctypes.create_string_buffer(YAPI.YOCTO_ERRMSG_LEN)
#noinspection PyUnresolvedReferences
res = YAPI._yapiHandleEvents(errBuffer)
if YAPI.YISERR(res):
if errmsgRef is not None:
#noinspection PyAttributeOutsideInit
errmsgRef.value = YByte2String(errBuffer.value)
return res
while len(YAPI._DataEvents) > 0:
YAPI.yapiLockFunctionCallBack(errmsgRef)
if not (len(YAPI._DataEvents)):
YAPI.yapiUnlockFunctionCallBack(errmsgRef)
break
ev = YAPI._DataEvents.pop(0)
YAPI.yapiUnlockFunctionCallBack(errmsgRef)
ev.invokeData()
return YAPI.SUCCESS

Categories