Pygtk StatusIcon not loading? - python

I'm currently working on a small script that needs to use gtk.StatusIcon(). For some reason, I'm getting some weird behavior with it. If I go into the python interactive shell and type:
>> import gtk
>> statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")
Pygtk does exactly what it should do, and shows an icon (lin_idle.png) in the system tray:
However, if I try to do the same task in my script:
def gtkInit(self):
self.statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")
When gtkInit() gets called, I see this instead:
I made I ran the script in the same working directory as the interactive python shell, so I'm pretty sure it's finding the image, so I'm stumped... Any ideas anyone? Thanks in advance.
Update: For some reason or another, after calling gtk.status_icon_new_from_file() a few times in the script, it does eventually create the icon, but this issue still remains unfortunately. Does anyone at all have any ideas as to what could be going wrong?
As requested: Here's the full script. This is actually an application that I'm in the very early stages of making, but it does work at the moment if you get it setup correctly, so feel free to play around with it if you want (and also help me!), you just need to get an imgur developer key and put it in linup_control.py
Linup.py
#
# Linup - A dropbox alternative for Linux!
# Written by Nakedsteve
# Released under the MIT License
#
import os
import time
import ConfigParser
from linup_control import Linup
cfg = ConfigParser.RawConfigParser()
# See if we have a .linuprc file
home = os.path.expanduser("~")
if not os.path.exists(home+"/.linuprc"):
# Nope, so let's make one
cfg.add_section("paths")
cfg.set("paths","watch_path", home+"/Desktop/screenshot1.png")
# Now write it to the file
with open(home+"/.linuprc","wb") as configfile:
cfg.write(configfile)
else:
cfg.read(home+"/.linuprc")
linup = Linup()
# Create the GUI (status icon, menus, etc.)
linup.gtkInit()
# Enter the main loop, where we check to see if there's a shot to upload
# every 1 second
path = cfg.get("paths","watch_path")
while 1:
if(os.path.exists(path)):
linup.uploadImage(path)
url = linup.getURL()
linup.toClipboard(url)
linup.json = ""
print "Screenshot uploaded!"
os.remove(path)
else:
# If you're wondering why I'm using time.sleep()
# it's because I found that without it, my CPU remained
# at 50% at all times while running linup. If you have a better
# method for doing this, please contact me about it (I'm relatively new at python)
time.sleep(1)
linup_control.py
import gtk
import json
import time
import pycurl
import os
class Linup:
def __init__(self):
self.json = ""
def uploadImage(self, path):
# Set the status icon to busy
self.statusIcon.set_from_file("img/lin_busy.png")
# Create new pycurl instance
cu = pycurl.Curl()
# Set the POST variables to the image and dev key
vals = [
("key","*************"),
("image", (cu.FORM_FILE, path))
]
# Set the URL to send to
cu.setopt(cu.URL, "http://imgur.com/api/upload.json")
# This lets us get the json returned by imgur
cu.setopt(cu.WRITEFUNCTION, self.resp_callback)
cu.setopt(cu.HTTPPOST, vals)
# Do eet!
cu.perform()
cu.close()
# Set the status icon to done...
self.statusIcon.set_from_file("img/lin_done.png")
# Wait 3 seconds
time.sleep(3)
# Set the icon to idle
self.statusIcon.set_from_file("img/lin_idle.png")
# Used for getting the response json from imgur
def resp_callback(self, buff):
self.json += buff
# Extracts the image URL from the json data
def getURL(self):
js = json.loads(self.json)
return js['rsp']['image']['original_image']
# Inserts the text variable into the clipboard
def toClipboard(self, text):
cb = gtk.Clipboard()
cb.set_text(text)
cb.store()
# Initiates the GUI elements of Linup
def gtkInit(self):
self.statusIcon = gtk.StatusIcon()
self.statusIcon.set_from_file("img/lin_idle.png")

You need to call the gtk.main function like qba said, however the correct way to call a function every N milliseconds is to use the gobject.timeout_add function. In most cases you would want to have anything that could tie up the gui in a separate thread, however in your case where you just have an icon you don't need to. Unless you are planning on making the StatusIcon have a menu. Here is the part of Linup.py that I changed:
# Enter the main loop, where we check to see if there's a shot to upload
# every 1 second
path = cfg.get("paths","watch_path")
def check_for_new():
if(os.path.exists(path)):
linup.uploadImage(path)
url = linup.getURL()
linup.toClipboard(url)
linup.json = ""
print "Screenshot uploaded!"
os.remove(path)
# Return True to keep calling this function, False to stop.
return True
if __name__ == "__main__":
gobject.timeout_add(1000, check_for_new)
gtk.main()
You will have to import gobject somewhere too.
I don't know for sure if this works because I can't get pycurl installed.
EDIT: In linup_control.py, I would try changing
# Wait 3 seconds
time.sleep(3)
# Set the icon to idle
self.statusIcon.set_from_file("img/lin_idle.png")
to
gobject.timeout_add(3000, self.statusIcon.set_from_file, "img/lin_idle.png")

You made two mistakes. One is important one is not.
At first if you want to use stock icon use .set_from_stock( stock_id ) method. If you want to use your own icon then the .set_from_file(/path/to/img.png) is ok.
The other think witch is the probably the main problem is that when you write gtk application you have to call gtk.main() function. This is main gtk loop where all signal handling/window drawing and all other gtk stuff is done. If you don't do this, simply your icon is not drawing.
The solution in your case is to make two threads - one for gui, second for your app. In the first one you simply call gtk.main(). In second you put your main program loop. Of course when you call python program you have one thread already started:P
If you aren't familiar whit threads there is other solution. Gtk have function which calls function specified by you with some delay:
def call_me:
print "Hello World!"
gtk.timeout_add( 1000 , call_me )
gtk.timeout_add( 1000 , call_me )
gtk.main()
But it seems to be deprecated now. Probably they have made a better solution.

Related

How to pause/sleep() function without crashing app?

I have an app I'm trying to add website updates to every minute. That part works just fine at the moment. The problem I'm experiencing with this current excerpt of code is that, when I go to close/exit the app, I have to hit the "X" button a few times and it completely crashes and freezes.
If my understanding is correct, I believe that is happening because time.sleep() is still "running" constantly when I try to exit.
How can I run a regular update like this that wont throw the app into a fit when I want to close it? Can someone please help me with a solution here?
I have added just a 5 second sleep in this working example here instead of my intended 60 seconds to save time when you test it.
import time
from threading import Thread
from kivy.app import App
class Test(App):
def build(self):
self.thread_for_update()
def thread_for_update(self):
p1 = Thread(target=lambda: self.check_for_update())
p1.start()
def check_for_update(self):
time.sleep(5)
print("Update")
# Here I'm checking an online data source for changes
# I will also notify user if theres changes
self.thread_for_update()
Test().run()
You could use threading.Timer, and update it in another function.
threading.Timer takes 2 argument, the delay (in seconds) and the function to call.
def check_for_update(self):
Timer(5, self.update).start()
def update(self):
print("Update")
self.thread_for_update()
I think I may have fixed it. when I add p1.daemon = True it seems to exit the program well when I try to exit.

Tkinter - Creating a responsive GUI with a progressbar

Using PyGubu (tool to create Tkinter interfaces) I obtained the following GUI:
Current situation:
When I click the Button "Create", a function is called. This function takes quite some time, and the graphical interfaces is just frozen. As I would like to keep the graphical interface and the functional part as much separated as possible, I don't want to update the Progress Bar or the interface in general from the function I call
Desired situation
The best case for me would be a solution without Threading: I would like, upon clicking "Create", that my function runs, while the Progress Bar updates itself (just to show a feedback to the user, and to tell him "look, I am doing something") and the interface remains responsive, so the user can actually interact with it while the function finish.
Current attempts
I tried to solve this using Threading:
#I have this code in my main.py:
from threading import Thread
from queue import Queue, Empty
my_queue=Queue()
#And this is a simplified version of the Command of the "Create" Button:
def create_command(self):
#Show the progress bar and start it
self.show_item(self.progress)
self.progress.start()
#Run the big function
thrd = Thread(target = my_big_function, args=(some_arguments, my_queue))
thrd.start()
do_retry = True
while do_retry: #Repeat until you have a result
try:
result = my_queue.get(False) #If you have a result, exit loop. Else, throw Empty
do_retry = False
except Empty: #Queue is still empty
self.progress_var.set(self.progress_var.get()+1)
sleep(0.05)
self.mainwindow.update() #Update the progress bar
q.task_done()
self.progress.stop()
Problem of the current attempt
As I am not used to work with threads, I am facing two problems:
In some runs (not all of them, just some) I have a RuntimeError stating
RuntimeError: main thread is not in main loop
I tried to overcome this looking at other question in StackOverflow, but now it just happens randomly, and I don't know how to avoid it. The module mtTinker is no more maintained for python 3.x (there is a vague attempt full of ToDoes and blood)
If there is some kind of exception in the big function, I don't know how to handle it. The program would just run forever waiting for a result that will never come back
So
How can I obtain my desired outcome? Thanks in advance
You can try adding root.update() inside the function you call, inside the main loop. Hope that's helpful!

AppJar threading to allow for GUI updates

I have been studying the AppJar documentation for the last few hours, but I really can't seem to figure out how to get the GUI to update during the data processing. I split the 4 main functions into different threads, and within the threads I added the update function as a .queuefunction, but the GUI still hangs until everything has completed.
This is the update function I wrote:
label_status = ["Ready"]
def update_label():
app.setLabel("status_label", label_status[-1])
I then broke down the process into 4 threads, but it didn't change anything compared to before. So I'm guessing I missed something pretty obvious here, but I can't find it.
def press(button):
""" Process a button press
Args:
button: The name of the button. Either Process of Quit
"""
if button == "Process":
global label_status
global output_directory
global filename_out
src_file = app.getEntry("input_file")
output_directory = app.getEntry("output_directory")
filename_out = app.getEntry("output_name")
errors, error_msg = validate_inputs(src_file, output_directory, filename_out)
if errors:
label_status.append("Error")
update_label()
app.errorBox("Error", "\n".join(error_msg), parent=None)
return label_status
else:
#Create single xlsx doc from data
trimmed_input = src_file[:-4]
app.thread(create_xlsx_file(trimmed_input))
# add graphs to excel file
app.thread(add_graphs())
#clean temporary files
app.thread(clean_files())
#move output.xlsx to location chosen with filename chosen
app.thread(move_output())
I have attempted to update the GUI within in threads in the following way:
def clean_files():
label_status.append("Cleaning temporary files")
app.queueFunction(update_label())
file_path = os.path.join("csv_output/" + "temp*")
del_files = glob.glob(file_path)
for files in del_files:
os.remove(files)
Since I'm appending to a list, I can see all statuses are being added, but only the first and last are displayed to the user. What am I missing here?
It looks like all your calls to app.thread() and app.queueFunction() are being done wrong.
I think you should be passing just the name of a function. But, because you’ve put brackets after the function names, you’re actually passing the result of the functions.
Try it without including brackets after the function names, eg. app.queueFunction(update_label)

Chaining Python Scripts

I have two user defined python scripts. First takes a file and processes it, while the second script takes the output of first and runs an executable, and supplies the output of first script to program with additional formatting.
I need to run these scripts via another python script, which is my main executable script.
I searched a bit about this topic and;
I can use importlib to gather the content of scripts so that I can call them at appropriate times. This requires the scripts to be under my directory/or modification to path environment variable. So it is a bit ugly looking at best, not seem pythonish.
Built-in eval function. This requires the user to write a server-client like structure, cause the second script might have to run the said program more than one time while the first script still gives output.
I think I'm designing something wrong, but I cannot come up with a better approach.
A more detailed explenation(maybe gibberish)
I need to benchmark some programs, while doing so I have a standard form of data, and this data needs to be supplied to benchmark programs. The scripts are (due to nature of benchmark) special to each program, and needs to be bundled with benchmark definition, yet I need to create this program as a standalone configurable tester. I think, I have designed something wrong, and would love to hear the design approaches.
PS: I do not want to limit the user, and this is the reason why I choose to run python scripts.
I created a few test scripts to make sure this works.
The first one (count_01.py) sleeps for 100 seconds, then counts from 0 to 99 and sends it to count_01.output.
The second one (count_02.py) reads the output of first one (count_01.output) and adds 1 to each number and writes that to count_02.output.
The third script (chaining_programs.py) runs the first one and waits for it to finish before calling the second one.
# count_01.py --------------------
from time import sleep
sleep(100)
filename = "count_01.output"
file_write = open(filename,"w")
for i in range(100):
#print " i = " + str(i)
output_string = str(i)
file_write.write(output_string)
file_write.write("\n")
file_write.close()
# ---------------------------------
# count_02.py --------------------
file_in = "count_01.output"
file_out = "count_02.output"
file_read = open(file_in,"r")
file_write = open(file_out,"w")
for i in range(100):
line_in = file_read.next()
line_out = str(int(line_in) + 1)
file_write.write(line_out)
file_write.write("\n")
file_read.close()
file_write.close()
# ---------------------------------
# chaining_programs.py -------------------------------------------------------
import subprocess
import sys
#-----------------------------------------------------------------------------
path_python = 'C:\Python27\python.exe' # 'C:\\Python27\\python.exe'
#
# single slashes did not work
#program_to_run = 'C:\Users\aaaaa\workspace\Rich_Project_044_New_Snippets\source\count.py'
program_to_run_01 = 'C:\\Users\\aaaaa\\workspace\\Rich_Project_044_New_Snippets\\source\\count_01.py'
program_to_run_02 = 'C:\\Users\\aaaaa\\workspace\\Rich_Project_044_New_Snippets\\source\\count_02.py'
#-----------------------------------------------------------------------------
# waits
sys.pid = subprocess.call([path_python, program_to_run_01])
# does not wait
sys.pid = subprocess.Popen([path_python, program_to_run_02])
#-----------------------------------------------------------------------------

Python run an outside python program

Im trying to lock the volume of the sonos using SoCo in a webapp. I need to run a separate script to do this. So when the user presses the lock button it runs a loop constantly setting the volume to the value until the program gets called or toggled stopping it.
I need this done in a different script as if its in the main code i'm unable to get any user input to unlock it.
#app.route("/lock")
def lock():
run(togglelock.py)
return "ok"
#togglelock.py
toggle("F","T")
sound1 = (sonos.volume)
if toggle == "T":
sonos1.volume = sound1
else:
break
As long as the other python file is in the same directory, you can simply import it when you want to run it.
def lock():
import togglelock
return "ok"
#do more stuff....

Categories

Resources