Pygtk 状态图标无法加载?

0 投票
2 回答
1464 浏览
提问于 2025-04-15 15:35

我现在正在写一个小脚本,需要用到 gtk.StatusIcon()。但不知道为什么,它的表现有点奇怪。如果我在 Python 的交互式命令行里输入:

>> import gtk
>> statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")

Pygtk 正常工作,会在系统托盘里显示一个图标(lin_idle.png):

这工作得很好

但是,如果我在我的脚本里尝试做同样的事情:

def gtkInit(self):
    self.statusIcon = gtk.status_icon_new_from_file("img/lin_idle.png")

当调用 gtkInit() 时,我看到的是这个:

为什么你不工作 GSFKLJFSGDJKHSFDGHJKL

我在和交互式 Python 命令行相同的工作目录下运行脚本,所以我很确定它能找到这个图像,但我还是搞不懂... 有人有什么想法吗?谢谢大家。

更新:不知道为什么,在脚本里调用 gtk.status_icon_new_from_file() 几次后,它最终会创建图标,但这个问题依然存在。不知道有没有人能帮我找出问题出在哪里?

应要求:这是完整的脚本。其实这是我正在早期阶段制作的一个应用程序,但如果你设置正确,它现在是可以工作的,所以如果你想的话可以随便试试(也可以帮我!),你只需要获取一个 imgur 开发者密钥,并把它放在 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")

2 个回答

1

你犯了两个错误,一个比较重要,另一个不太重要。

首先,如果你想使用系统自带的图标,可以用 .set_from_stock( stock_id ) 这个方法。如果你想用自己的图标,那就可以用 .set_from_file(/path/to/img.png) 这个方法。

另一个问题可能是主要原因,就是当你写 gtk 应用的时候,你必须调用 gtk.main() 这个函数。这个函数是 gtk 的主循环,负责处理所有信号、绘制窗口和其他 gtk 的事情。如果你不调用这个函数,你的图标就不会显示出来。

在你的情况下,解决方案是创建两个线程——一个用于图形界面(gui),另一个用于你的应用程序。在第一个线程中,你只需调用 gtk.main()。在第二个线程中,你放入你的主程序循环。当然,当你运行 Python 程序时,已经有一个线程在运行了:P

如果你对线程不太了解,还有其他解决方案。Gtk 有一个函数,可以在你指定的延迟后调用你指定的函数:

def call_me:
    print "Hello World!"
    gtk.timeout_add( 1000 , call_me )

gtk.timeout_add( 1000 , call_me )
gtk.main()

不过这个方法现在似乎已经不推荐使用了。可能他们已经找到了更好的解决方案。

4

你需要像qba说的那样调用gtk.main这个函数,不过每隔N毫秒调用一个函数的正确方法是使用gobject.timeout_add这个函数。在大多数情况下,你会希望把可能会让界面卡住的操作放在一个单独的线程里,但在你的情况中,因为你只是有一个图标,所以不需要这么做。除非你打算让这个状态图标有一个菜单。下面是我修改过的Linup.py的一部分:

# 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()

你还需要在某个地方import gobject

我不确定这个是否有效,因为我无法安装pycurl

编辑:linup_control.py中,我建议你尝试把

# Wait 3 seconds
time.sleep(3)
# Set the icon to idle
self.statusIcon.set_from_file("img/lin_idle.png")

改成

gobject.timeout_add(3000, self.statusIcon.set_from_file, "img/lin_idle.png")

撰写回答