OSDN Git Service

Add to get following/followers wizard
authorHirotaka Kawata <hirotaka@techno-st.net>
Wed, 27 Apr 2011 17:22:47 +0000 (02:22 +0900)
committerHirotaka Kawata <hirotaka@techno-st.net>
Wed, 27 Apr 2011 17:22:47 +0000 (02:22 +0900)
gwit/getfriendswizard.py [new file with mode: 0644]
gwit/glade/getfollow.glade [deleted file]
gwit/iconstore.py
gwit/main.py
gwit/statusview.py
gwit/twitterapi.py
gwit/userselection.py

diff --git a/gwit/getfriendswizard.py b/gwit/getfriendswizard.py
new file mode 100644 (file)
index 0000000..94c6997
--- /dev/null
@@ -0,0 +1,148 @@
+#-*- coding: utf-8 -*-
+
+'''Get Twitter following/followers wizard
+'''
+
+################################################################################
+#
+# Copyright (c) 2011 University of Tsukuba Linux User Group
+#
+# This file is part of "gwit".
+#
+# "gwit" is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# "gwit" is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with "gwit".  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+
+import threading
+
+class GetFriendsWizard(gtk.Assistant):
+    twitter = None
+    
+    def __init__(self):
+        gtk.Assistant.__init__(self)
+        self.connect("cancel", self.on_cancel_close)
+        self.connect("close", self.on_cancel_close)
+        self.connect("apply", self.on_apply)
+        
+        # page1
+        self.page1 = gtk.Label("""
+
+This process may take a few minutes.
+If you have too many followings/followers, 
+you should mind remaining API limit.
+(Get 100 followings/followers per call)
+
+""")
+        
+        # page2
+        self.chkfr = gtk.CheckButton("Get all friends")
+        self.chkfo = gtk.CheckButton("Get all followers")
+        self.chkfr.connect("toggled", self.on_check_toggled)
+        self.chkfo.connect("toggled", self.on_check_toggled)
+        
+        box = gtk.VBox(spacing = 10)
+        box.pack_start(self.chkfr)
+        box.pack_end(self.chkfo)
+        self.page2 = box
+        
+        # page3
+        buf = gtk.TextBuffer()
+        view = gtk.TextView(buf)
+        self.page3 = gtk.ScrolledWindow()
+        self.page3.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        self.page3.add(view)
+        view.set_editable(False)
+        
+        # page4
+        self.page4 = gtk.Label("""Get followings/followers information successfully!
+There will add to user selection after few seconds.""")
+        
+        self.append_page(self.page1)
+        self.set_page_title(self.page1, "Get followings/followers")
+        self.set_page_type(self.page1, gtk.ASSISTANT_PAGE_INTRO)
+        self.set_page_complete(self.page1, True)
+        
+        self.append_page(self.page2)
+        self.set_page_title(self.page2, "Get followings/followers")
+        self.set_page_type(self.page2, gtk.ASSISTANT_PAGE_CONFIRM)
+        
+        self.append_page(self.page3)
+        self.set_page_title(self.page3, "Getting...")
+        self.set_page_type(self.page3, gtk.ASSISTANT_PAGE_PROGRESS)
+        self.set_page_complete(self.page3, False)
+        
+        self.append_page(self.page4)
+        self.set_page_title(self.page4, "Complete!")
+        self.set_page_type(self.page4, gtk.ASSISTANT_PAGE_SUMMARY)
+    
+    # get users
+    def _get_users_in_thread(self):
+        def p(buf, text):
+            gtk.gdk.threads_enter()
+            buf.insert_at_cursor(text)
+            gtk.gdk.threads_leave()
+        
+        users = dict()
+        log = self.page3.get_child().get_buffer()
+        
+        if self.chkfr.get_active():            
+            p(log, "=== Getting friedns ===\n")
+            cursor = -1
+            while cursor != 0:
+                p(log, "GET: %s\n" % cursor)
+                
+                r = self.twitter.api_wrapper(self.twitter.api.friends, cursor = cursor)
+                cursor = r["next_cursor"]
+                users.update([(i.id, i) for i in r["users"]])
+                
+                p(log, "%d users\n" % len(users))
+            
+        if self.chkfo.get_active():
+            p(log, "=== Getting followers ===\n")
+            cursor = -1
+            while cursor != 0:
+                p(log, "GET: %s\n" % cursor)
+                
+                r = self.twitter.api_wrapper(self.twitter.api.followers, cursor = cursor)
+                cursor = r["next_cursor"]
+                users.update([(i.id, i) for i in r["users"]])
+                
+                p(log, "%d users\n" % len(users))
+        
+        p(log, "\nDone.\n")
+        gtk.gdk.threads_enter()
+        self.set_page_complete(self.page3, True)
+        gtk.gdk.threads_leave()
+
+        self.twitter.add_users(users)
+    
+    def on_cancel_close(self, widget):
+        widget.hide_all()
+        widget.destroy()
+
+    def on_apply(self, assistant):
+        threading.Thread(target = self._get_users_in_thread).start()
+    
+    # page2 checkbox
+    def on_check_toggled(self, check):
+        if self.chkfr.get_active() or self.chkfo.get_active():
+            self.set_page_complete(self.page2, True)
+        else:
+            self.set_page_complete(self.page2, False)
diff --git a/gwit/glade/getfollow.glade b/gwit/glade/getfollow.glade
deleted file mode 100644 (file)
index 0df337f..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-<?xml version="1.0"?>
-<interface>
-  <requires lib="gtk+" version="2.16"/>
-  <!-- interface-naming-policy project-wide -->
-  <object class="GtkAssistant" id="assistant_getfollow">
-    <property name="border_width">12</property>
-    <property name="title" translatable="yes">Get followings/followers</property>
-    <signal name="prepare" handler="on_assistant_getfollow_prepare"/>
-    <signal name="apply" handler="on_assistant_getfollow_apply"/>
-    <signal name="cancel" handler="on_assistant_getfollow_cancel"/>
-    <child>
-      <placeholder/>
-    </child>
-    <child>
-      <object class="GtkLabel" id="label1">
-        <property name="visible">True</property>
-        <property name="label" translatable="yes">Get all followings/followers wizard.
-This process may take a few minutes.
-If you have too many followings/followers, you should check remaining API limit.
-(Get 20 followings/followers per call)
-</property>
-        <property name="use_markup">True</property>
-      </object>
-      <packing>
-        <property name="page_type">intro</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkVBox" id="vbox1">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkHBox" id="hbox1">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkCheckButton" id="checkbutton1">
-                <property name="label" translatable="yes">Followings</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">False</property>
-                <property name="draw_indicator">True</property>
-              </object>
-              <packing>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkSpinButton" id="spinbutton1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="invisible_char">&#x25CF;</property>
-                <property name="climb_rate">20</property>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkHBox" id="hbox2">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkCheckButton" id="checkbutton2">
-                <property name="label" translatable="yes">Followers</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">False</property>
-                <property name="draw_indicator">True</property>
-              </object>
-              <packing>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkSpinButton" id="spinbutton2">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="invisible_char">&#x25CF;</property>
-                <property name="climb_rate">20</property>
-              </object>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="page_type">confirm</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkTextView" id="textview1">
-        <property name="visible">True</property>
-        <property name="can_focus">True</property>
-      </object>
-      <packing>
-        <property name="page_type">progress</property>
-      </packing>
-    </child>
-  </object>
-</interface>
index 5953186..e3f159f 100644 (file)
@@ -80,9 +80,15 @@ class IconStore(object):
         self.stores.append((store, n))
 
     def remove_store(self, store):
+        remove = None
+        
         for i in self.stores:
             if store == i[0]:
-                self.stores.remove(i)
+                remove = i
+                break
+        
+        if remove:
+            self.stores.remove(remove)
 
 class NewIcon(threading.Thread):
     def __init__(self, user, stores, icons, semaphore):
@@ -172,8 +178,8 @@ class NewIcon(threading.Thread):
         
         # Icon Refresh
         for store, n in self.stores:
-           for row in store:
-               # replace icon to all user's status
+            for row in store:
+                # replace icon to all user's status
                 if row[n] == self.user.id:
                     gtk.gdk.threads_enter()
                     store[row.path][0] = icopix
index 259e017..181402a 100644 (file)
@@ -47,6 +47,7 @@ from userselection import UserSelection
 from listsselection import ListsSelection, ListsView
 from statusdetail import StatusDetail
 from twittertools import TwitterTools
+from getfriendswizard import GetFriendsWizard
 
 # Main Class
 class Main(object):
@@ -71,8 +72,10 @@ class Main(object):
     # Constractor
     def __init__(self, screen_name, keys):
         # Gtk Multithread Setup
-        gtk.gdk.threads_init()
-        gobject.threads_init()
+        if sys.platform == "win32":
+            gobject.threads_init()
+        else:
+            gtk.gdk.threads_init()
         
         # init status timelines
         self.timelines = list()
@@ -130,11 +133,11 @@ class Main(object):
         ListsView.iconstore = self.iconstore
         UserSelection.twitter = self.twitter
         UserSelection.iconstore = self.iconstore
+        GetFriendsWizard.twitter = self.twitter
         
         self.initialize()
     
     def main(self):
-        gtk.gdk.threads_enter()
         window = self.builder.get_object("window1")
         
         # settings allocation
@@ -143,7 +146,6 @@ class Main(object):
         
         # Start gtk main loop
         gtk.main()
-        gtk.gdk.threads_leave()
     
     def read_settings(self):
         try:
@@ -373,10 +375,10 @@ class Main(object):
         return color
     
     def status_update_thread(self, status):
-        t = threading.Thread(target = self.status_update, args = (status,))
+        t = threading.Thread(target = self._status_update, args = (status,))
         t.start()
     
-    def status_update(self, status):
+    def _status_update(self, status):
         args = dict()
 
         gtk.gdk.threads_enter()
index f8e5c75..c58ce66 100644 (file)
@@ -105,6 +105,15 @@ class StatusView(gtk.TreeView):
         self._old_path = None
         # for width changed
         self._old_width = None
+
+        self.render_event = threading.Event()
+        self._render_color_only = False
+        
+        # background thread for rewrite
+        t = threading.Thread(target=self._render_thread)
+        t.setName("status render")
+        t.setDaemon(True)
+        t.start()
     
     # Get selected status
     def get_selected_status(self):
@@ -258,13 +267,27 @@ class StatusView(gtk.TreeView):
                 long(i), long(status.user.id),
                 background, favico)
     
+    # background render thread function
+    def _render_thread(self):
+        while True:
+            self.render_event.wait()
+            self.render_event.clear()
+            
+            status = self._render_color_only
+            self._render_color_only = False
+            
+            if status == False:
+                self._reset_status_text_in_thread()
+                status = None
+            
+            self._color_status_in_thread(status)
+    
     # Color status
     def color_status(self, status = None):
-        t = threading.Thread(target=self.color_status_in_thread, args=(status,))
-        t.setName("color_status")
-        t.start()
+        self._render_color_only = status
+        self.render_event.set()
     
-    def color_status_in_thread(self, status = None):
+    def _color_status_in_thread(self, status = None):
         myname = self.twitter.my_name
         myid = self.twitter.my_id
         
@@ -273,6 +296,8 @@ class StatusView(gtk.TreeView):
             status = self.get_selected_status()
         
         for row in self.store:
+            if self.render_event.is_set(): break
+            
             bg = None
             
             status_id = row[2]
@@ -302,6 +327,20 @@ class StatusView(gtk.TreeView):
             self.store[row.path][4] = bg
             gtk.gdk.threads_leave()
     
+    # Reset all data to change row height
+    def reset_status_text(self):
+        self.render_event.set()
+    
+    def _reset_status_text_in_thread(self):
+        for row in self.store:
+            if self.render_event.is_set(): break
+            
+            status_id = row[2]
+            packed_row = self.status_pack(status_id)
+            
+            gtk.gdk.threads_enter()
+            self.store[row.path] = packed_row
+            gtk.gdk.threads_leave()
     
     ########################################
     # Gtk Signal Events    
@@ -378,33 +417,14 @@ class StatusView(gtk.TreeView):
         
         # Really changed?
         # (this event is called when add statuses too.)
-        if self._old_width == text_width:
-            return
-        else:
+        if self._old_width != text_width:
             self._old_width = text_width
-        
-        # Set "Status" width
-        cellr = columns[1].get_cell_renderers()
-        cellr[0].set_property("wrap-width", text_width)
-        
-        def refresh_text():
-            # Reset all data to change row height
-            for row in self.store:
-                # Maybe no affects performance
-                # if treeview.allocation.width != width:
-                #     break
-                status_id = row[2]
-                packed_row = self.status_pack(status_id)
-                
-                gtk.gdk.threads_enter()
-                self.store[row.path] = packed_row
-                gtk.gdk.threads_leave()
             
-            self.color_status_in_thread()
-        
-        t = threading.Thread(target=refresh_text)
-        t.setName("refresh_text")
-        t.start()
+            # Set "Status" width
+            cellr = columns[1].get_cell_renderers()
+            cellr[0].set_property("wrap-width", text_width)
+            
+            self.reset_status_text()
     
     def on_treeview_destroy(self, treeview):
         self.iconstore.remove_store(self.store)
index 3a710bf..66c4f5b 100644 (file)
@@ -82,8 +82,8 @@ class TwitterAPI(object):
     def get_followers(self):
         self.followers.update([int(i) for i in self.api_wrapper(self.api.followers_ids)])
     
-    def add_statuses(self, slist):
-        for i in slist:
+    def add_statuses(self, statuses):
+        for i in statuses:
             self.add_status(i)
     
     def add_status(self, status):
@@ -93,6 +93,12 @@ class TwitterAPI(object):
         if status.retweeted_status != None:
             self.add_status(status.retweeted_status)
     
+    def add_users(self, users):
+        if isinstance(users, dict):
+            self.users.update(users)
+        else:
+            self.users.update([(i.id, i) for i in users])
+    
     def add_user(self, user):
         self.users[user.id] = user
     
@@ -137,7 +143,7 @@ class TwitterAPI(object):
                 self.on_twitterapi_requested()
                 self.apilock.release()
             
-            time.sleep(5)
+            time.sleep(1)
         
         return response
     
index fbb829e..ef4a27f 100644 (file)
@@ -37,6 +37,7 @@ import sched
 import time
 
 from twittertools import TwitterTools
+from getfriendswizard import GetFriendsWizard
 
 class UserSelection(gtk.VBox):
     twitter = None
@@ -60,7 +61,8 @@ class UserSelection(gtk.VBox):
         button_add.set_image(gtk.image_new_from_stock("gtk-add", gtk.ICON_SIZE_BUTTON))
         button_add.connect("clicked", self.on_button_add_clicked)
         hbox.pack_start(button_add, expand = False, padding = 5)
-
+        
+        # get followings/followers button
         button_refresh = gtk.Button()
         button_refresh.set_image(gtk.image_new_from_stock("gtk-refresh", gtk.ICON_SIZE_BUTTON))
         button_refresh.connect("clicked", self.on_button_refresh_clicked)
@@ -287,19 +289,7 @@ Web: %s</span></small>
         else:
             self.treeview.get_model().set_sort_column_id(n, gtk.SORT_ASCENDING if n == 1 else gtk.SORT_DESCENDING)
 
+    # open get followings followers wizard
     def on_button_refresh_clicked(self, button):
-        builder = gtk.Builder()
-        gladefile = os.path.join(os.path.dirname(__file__), "glade/getfollow.glade")
-        builder.add_from_file(gladefile)
-        builder.connect_signals(self)
-        win = builder.get_object("assistant_getfollow")
-        win.show_all()
-    
-    def on_assistant_getfollow_prepare(self, assistant, page):
-        pass
-
-    def on_assistant_getfollow_apply(self, assistant):
-        assistant.destroy()
-    
-    def on_assistant_getfollow_cancel(self, assistant):
-        assistant.destroy()
+        w = GetFriendsWizard()
+        w.show_all()