在socket.error之后重新启动gobject.timeout_add_seconds计数器

时间:2013-11-09 19:59:15

标签: python pygtk pygobject

我决定对天气托盘小程序 found here 进行一些修改。

经过多次测试,我发现 update_tray()在我的计算机花费一些时间休眠后停止更新。只手动更新它。

在检查代码后,我发现负责更新的行是:

gobject.timeout_add_seconds(self.args.delta * 60, self.update_tray)

gobject documentation 这个函数被反复调用,直到它返回FALSE,点,超时被自动销毁和功能将不会被再次调用处。但似乎计数器被破坏了,因为系统时钟突然变化了。

我想知道如何在发生此问题时重新启动计时器。这是完整的代码:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
#
#  Released under the terms of the GNU GPLv2.
#       

import gtk
import gobject
import time
import webbrowser
from urllib2 import urlopen, URLError
from argparse import ArgumentParser
from xml.dom import minidom

parser = ArgumentParser(description='Simple weather applet', epilog='Written by Roman Komkov and updated at 06.06.2012.\nPlease, report bugs to <r.komkov@gmail.com>')
parser.add_argument('-l', '--location', required=True, metavar='WOEID', help='location WOEID (more on http://developer.yahoo.com/weather/)')
parser.add_argument('-u', '--units', choices=['c','f'], default='c', metavar='c|f', help='units to display')
parser.add_argument('-d', '--delta', default='10', type=int, metavar='N', help='timeout in minutes between next weather data query')
parser.add_argument('-a', '--advanced', action = 'store_true', default=False, help='Advanced tooltip')

class Api:
    def __init__(self, location, units):
        self.params = (location,units)
        self.url = 'http://xml.weather.yahoo.com/forecastrss?w=%s&u=%s'
        self.namespace = 'http://xml.weather.yahoo.com/ns/rss/1.0'
        self.website = 'http://weather.yahoo.com/'

        self.codes = { #code:icon name
            '0':'weather-severe-alert',
            '1':'weather-severe-alert',
            '2':'weather-severe-alert',
            '3':'weather-severe-alert',
            '4':'weather-storm',
            '5':'weather-snow-rain',
            '6':'weather-snow-rain',
            '7':'weather-snow',
            '8':'weather-freezing-rain',
            '9':'weather-fog',
            '10':'weather-freezing-rain',
            '11':'weather-showers',
            '12':'weather-showers',
            '13':'weather-snow',
            '14':'weather-snow',
            '15':'weather-snow',
            '16':'weather-snow',
            '17':'weather-snow',
            '18':'weather-snow',
            '19':'weather-fog',
            '20':'weather-fog',
            '21':'weather-fog',
            '22':'weather-fog',
            '23':'weather-few-clouds',
            '24':'weather-few-clouds',
            '25':'weather-few-clouds',
            '26':'weather-overcast',
            '27':'weather-clouds-night',
            '28':'weather-clouds',
            '29':'weather-few-clouds-night',
            '30':'weather-few-clouds',
            '31':'weather-clear-night',
            '32':'weather-clear',
            '33':'weather-clear-night',
            '34':'weather-clear',
            '35':'weather-snow-rain',
            '36':'weather-clear',
            '37':'weather-storm',
            '38':'weather-storm',
            '39':'weather-storm',
            '40':'weather-showers-scattered',
            '41':'weather-snow',
            '42':'weather-snow',
            '43':'weather-snow',
            '44':'weather-few-clouds',
            '45':'weather-storm',
            '46':'weather-snow',
            '47':'weather-storm',
            '3200':'stock-unknown'
        }
    def conv_direction(self, value):
        value = int(value)
        if value >= 0 and value < 45:
            return u'\u2191 (N)'
        elif value >= 45 and value < 90:
            return u'\u2197 (NE)'
        elif value >= 90 and value < 135:
            return u'\u2192 (E)'
        elif value >= 135 and value < 180:
            return u'\u2198 (SE)'
        elif value >= 180 and value < 225:
            return u'\u2193 (S)'
        elif value >= 225 and value < 270:
            return u'\u2199 (SW)'
        elif value >= 270 and value < 315:
            return u'\u2190 (W)'
        elif value >= 315 and value < 360:
            return u'\u2196 (NW)'
        else:
            return u'\u2191 (N)'

    def get_data(self):
        try:
            url = self.url % self.params
            dom = minidom.parse(urlopen(url))
            units_node = dom.getElementsByTagNameNS(self.namespace, 'units')[0]
            units = {'temperature': units_node.getAttribute('temperature'),
                    'distance': units_node.getAttribute('distance'),
                    'pressure': units_node.getAttribute('pressure'),
                    'speed': units_node.getAttribute('speed')}
            forecasts = []
            for node in dom.getElementsByTagNameNS(self.namespace, 'forecast'):
                forecasts.append({
                    'date': node.getAttribute('date'),
                    'low': node.getAttribute('low')+u'\u00B0 '+units['temperature'],
                    'high': node.getAttribute('high')+u'\u00B0 '+units['temperature'],
                    'condition': node.getAttribute('text'),
                    'icon': self.codes.get(node.getAttribute('code'))
                })
            condition = dom.getElementsByTagNameNS(self.namespace, 'condition')[0]
            location = dom.getElementsByTagNameNS(self.namespace, 'location')[0]
            wind = dom.getElementsByTagNameNS(self.namespace, 'wind')[0]
            atmosphere = dom.getElementsByTagNameNS(self.namespace, 'atmosphere')[0]
            return {
                'current_condition': condition.getAttribute('text'),
                'current_icon': self.codes.get(condition.getAttribute('code')),
                'current_temp': condition.getAttribute('temp')+u'\u00B0 '+units['temperature'],
                'extra':{
                'wind': {'direction':self.conv_direction(wind.getAttribute('direction')),
                            'speed':wind.getAttribute('speed')+' '+units['speed']},
                'atmosphere': {'humidity':atmosphere.getAttribute('humidity')+'%',
                                'visibility':atmosphere.getAttribute('visibility')+' '+units['distance'],
                                'pressure':atmosphere.getAttribute('pressure')+' '+units['pressure']}},
                'forecasts': forecasts,
                'location' : {'city' : location.getAttribute('city'),'country' : location.getAttribute('country')}
            }
        except URLError, ex:
            return None

class MainApp:
    def __init__(self,args):
        self.args = args
        self.weather = None
        self.tooltip = None
        self.tray = gtk.StatusIcon()
        self.tray.connect('popup-menu', self.on_right_click)
        self.tray.connect('activate', self.on_left_click)
        self.tray.set_has_tooltip(True)
        if self.args.advanced:
            self.tray.connect('query-tooltip', self.on_tooltip_advanced)
        self.api = Api(self.args.location, self.args.units)
        self.update_tray()
        gobject.timeout_add_seconds(self.args.delta * 60, self.update_tray)

    def on_tooltip_advanced(self, widget, x, y, keyboard_mode, tooltip):
        #if self.tooltip:
            #tooltip.set_text(self.tooltip)
        if self.weather:
            weather = self.weather
            tooltip_text = '%s\n%s' % (self.weather['current_temp'],self.weather['current_condition'])
            vbox = gtk.VBox()
            header = gtk.Label()
            header.set_markup('<u><b>'+self.weather['location']['city']+', '+self.weather['location']['country']+'</b></u>')
            header.set_alignment(1.0, 0.5)
            separator_h = gtk.HSeparator()
            hbox = gtk.HBox()
            now_image = gtk.Image()
            now_image.set_padding(0,5)
            now_image.set_pixel_size(48)
            now_image.set_from_icon_name(weather['current_icon'],48)
            now_label = gtk.Label()
            now_label.set_markup('<b>'+tooltip_text+'</b>')
            now_label.set_padding(5,5)
            table = gtk.Table(columns=2, homogeneous=False)
            u = 0
            l = 1
            for k,v in self.weather['extra'].iteritems():
                h_label = gtk.Label()
                h_label.set_markup('<b>'+k+'</b>')
                h_label.set_alignment(0.0, 0.5)
                h_label.set_padding(5,0)
                table.attach(h_label,0,1,u,l)
                for i,j in v.iteritems():
                    u +=1
                    l +=1
                    k_label = gtk.Label(i)
                    k_label.set_alignment(0.0, 0.5)
                    v_label = gtk.Label(j)
                    v_label.set_alignment(0.0, 0.5)
                    table.attach(k_label,0,1,u,l)
                    table.attach(v_label,1,2,u,l)
                u +=1
                l +=1

            hbox.pack_start(now_image, False, False, 0)
            hbox.pack_start(now_label, False, False, 0)
            vbox.pack_start(header, True, False, 0)
            vbox.pack_start(separator_h, False, False, 0)
            vbox.pack_start(hbox, False, False, 0)
            vbox.pack_start(table, False, False, 0)
            vbox.show_all()
            tooltip.set_custom(vbox)
        else:
            tooltip.set_text('Connection error!')
        return True

    def on_refresh(self,widget):
        self.update_tray()

    def on_right_click(self, icon, event_button, event_time):
        menu = gtk.Menu()
        refresh = gtk.MenuItem('Refresh')
        refresh.show()
        refresh.connect('activate', self.on_refresh)
        quit = gtk.MenuItem('Quit')
        quit.show()
        quit.connect('activate', gtk.main_quit)
        menu.append(refresh)
        menu.append(quit)
        menu.popup(None, None, gtk.status_icon_position_menu,
                    event_button, event_time, self.tray)

    def on_left_click(self, widget):
        webbrowser.open(self.api.website)

    def update_tray(self):
        self.weather = self.api.get_data()
        if self.weather != None:
            self.tray.set_from_icon_name(self.weather['current_icon'])
            if not self.args.advanced:
                tooltip_text =  '%s / %s' % (self.weather['current_temp'],self.weather['current_condition'])
                self.tray.set_tooltip_markup(tooltip_text)
        else:
            if not self.args.advanced:
                self.tray.set_tooltip_text('Connection error!')
            self.tray.set_from_stock('gtk-dialog-error')
        return True

if __name__ == "__main__":
    try:
        args = parser.parse_args()
        MainApp(args)
        gtk.main()
    except KeyboardInterrupt:
        pass

根据@ J.F.Sebastian的要求,这是一个较小的代码示例:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import gtk, gobject, time
from datetime import datetime

class MainApp:
    def __init__(self):
        self.update_tray()
        gobject.timeout_add_seconds(15 * 60, self.update_tray)

    def update_tray(self):
        print time.strftime('%c')
        return True

if __name__ == "__main__":
    try:
        MainApp()
        gtk.main()
    except KeyboardInterrupt:
        pass

更新:我将其设置为每分钟更新,休眠,并在返回时,脚本继续工作。 10分钟后我得到了一个 socket.error ,它就不再更新了。我认为问题不在于互联网连接,因为我无法通过拔掉电缆来重现它。现在,我的问题是:如何在发现此错误时重新启动计数器,或者避免此错误,因为 get_data已经有try:语句?

Traceback (most recent call last):
  File "weatherboy", line 333, in update_tray
    self.weather = self.api.get_data()
  File "weatherboy", line 145, in get_data
    dom = minidom.parse(urlopen(url))
  File "/usr/lib/python2.7/urllib2.py", line 127, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 401, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 419, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 379, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1211, in http_open
    return self.do_open(httplib.HTTPConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1184, in do_open
    r = h.getresponse(buffering=True)
  File "/usr/lib/python2.7/httplib.py", line 1034, in getresponse
    response.begin()
  File "/usr/lib/python2.7/httplib.py", line 407, in begin
    version, status, reason = self._read_status()
  File "/usr/lib/python2.7/httplib.py", line 365, in _read_status
    line = self.fp.readline()
  File "/usr/lib/python2.7/socket.py", line 447, in readline
    data = self._sock.recv(self._rbufsize)
socket.error: [Errno 104] Connection reset by peer

1 个答案:

答案 0 :(得分:2)

如果要调用它,

update_tray()应该返回True。未处理的socket.error可以防止这种情况发生。您可以使用except EnvironmentError代替except URLError来捕获get_data()中更广泛的错误。

要忽略其他类型的错误,您可以将try: ... except Exception: log_it()添加到update_tray()