Changeset 145

Show
Ignore:
Timestamp:
Fri Dec 15 12:28:17 2006
Author:
manatlan
Message:

- plugin comment now display thumb when commenting one by one (thanks to
pieter)
- publig multiexport (flickr) ability to apply privacy to each picture
(thanks to pieter)

Files:

Legend:

Unmodified
Added
Removed
Modified
  • trunk/common.py

    r144 r145  
    12 12 ## GNU General Public License for more details.  
    13 13 ##  
      14 import gtk, gobject  
    14 15  
    15 16 def _(m):  
     
    68 69         runWith(["gnome-open","mozilla-firefox","firefox","konqueror","epiphany","galeon"],unicode(url),False)  
    69 70  
      71 # Class pictureselector  
      72 # This class provides a thumbnailview with a slider. It behaves as a normaol gtk class, and emits the signal "value_changed"  
      73 # whenever a user changes the slider  
      74  
      75 class PictureSelector(gtk.VBox):  
      76  
      77     def __init__(self, photo_list):  
      78         gtk.VBox.__init__(self)  
      79  
      80         self.photo_list = photo_list  
      81         #self.photo_list.sort(compa)  
      82         self.photo_list.sort(lambda f, s: int(int(s.date) - int(f.date))) # The integer cast is for the result is needed because the dates are usually of type long, and a comparison needs an int  
      83          
      84         # Create all the visual elements  
      85         self.thumb_display = gtk.Image()  
      86         self.slider        = gtk.HScrollbar()  
      87         self.text_display  = gtk.Label("Photo 1/1")  
      88         self.pack_start(self.thumb_display, expand = False)  
      89         self.pack_start(self.slider, expand = True, fill = True)  
      90         self.pack_start(self.text_display, expand = False)  
      91  
      92         # Initialize the slider  
      93         if (len(self.photo_list) > 1):  
      94             self.slider.set_range(1, len(self.photo_list))  
      95             self.slider.set_increments(1, 10)  
      96  
      97         # If we have only one picture, make the widget greyed out  
      98         if (len(self.photo_list) < 2):  
      99             self.set_sensitive(False)  
      100         self.updateDisplay()  
      101  
      102         # Create the callbacks  
      103         self.slider.connect("value_changed", self.onSliderChange)  
      104  
      105         # Display everything  
      106         self.slider.show()  
      107         self.text_display.show()  
      108         self.thumb_display.show()  
      109  
      110     def onSliderChange(self, widget):  
      111         # Update the display of the text widget and the thumbnail  
      112         self.updateDisplay()  
      113  
      114         # Emit a signal that we have changed  
      115         self.emit("value_changed", self)  
      116  
      117     def getValue(self):  
      118         # Returns the index value of the photolist. Watch out for off-by-one errors!  
      119         slider_value = int(self.slider.get_value())  
      120         if (slider_value == 0):  
      121             slider_value = 1  
      122         return slider_value - 1  
      123  
      124     def updateDisplay(self):  
      125         photo_num = self.getValue()  
      126         self.text_display.set_text("Photo %d/%d: %s" % (photo_num + 1, len(self.photo_list), self.photo_list[photo_num].name))  
      127         self.thumb_display.set_from_pixbuf(self.photo_list[photo_num].getThumb())  
      128  
      129     def set_sensitive(self, value):  
      130         # Make the widget greyed out or active. If the photo list has only one picture, it can never be active  
      131         if (value == True) and (len(self.photo_list) < 2):  
      132             value = False  
      133  
      134         if (value == True):  
      135             gtk.VBox.set_sensitive(self, True)  
      136         else:  
      137             gtk.VBox.set_sensitive(self, False)  
      138  
      139 # Create the "value_changed" signal and connect it to the PictureSelector class  
      140 gobject.signal_new("value_changed", PictureSelector, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))  
    70 141  
    71 142 if __name__ == "__main__":  
  • trunk/plugins/comment/__init__.py

    r138 r145  
    29 29         from comment import Wincomment  
    30 30  
    31           Wincomment.nodes = list  
    32           win = Wincomment()  
      31         win = Wincomment(list)  
    33 32         if len(list)>1:  
    34 33             win.notebook1.set_current_page(self.conf["mode"] or 0)  
  • trunk/plugins/comment/comment.py

    r138 r145  
    14 14 import os  
    15 15 import gtk  
      16 from common import PictureSelector  
    16 17  
    17 18 # phil miossec --------------------------------  
     
    27 28  
    28 29     #-- Wincomment.new {  
    29       def init(self):  
      30     def init(self, nodes):  
    29 30         #############################################################  
    30 31         ## IN in self.nodes:  
     
    41 42         self.main_widget.set_position(gtk.WIN_POS_CENTER)  
    42 43  
      44         self.nodes = nodes  
      45          
      46         self.selector = PictureSelector(self.nodes)  
      47         self.hbox1.pack_start(self.selector)  
      48         self.selector.connect("value_changed", self.on_selector_value_changed)  
      49         self.selector.show()  
      50          
    43 51         self.list=[]  
    44 52         for i in self.nodes:  
     
    53 61             self.tv_all_comment.grab_focus()  
    54 62         else:  
    55               self.hscrollbar1.set_range(0,len(self.list)-1)  
    56               self.hscrollbar1.set_increments(1,1)  
    57 63             self.setPhoto(0)  
    58 64  
    59    
    60 65     def setPhoto(self,i):  
    61 66         info = self.list[i]  
    62 67         node = info[0]  
    63           self.lbl_one.set_text( _("Photo %d/%d : %s ") % (i+1,len(self.list),node.name) )  
    64           self.image1.set_from_pixbuf( node.getThumb() )  
    65 68         self.tv_comment.set_buffer( info[1] )  
    66 69     def getTextFromBuffer(self,b):  
     
    83 86             self.tv_comment.grab_focus()  
    84 87  
    85       def on_hscrollbar1_value_changed(self, widget, *args):  
    86           sel = int(widget.get_value())  
      88     def on_selector_value_changed(self, *args):  
      89         sel = self.selector.getValue()  
    87 90         self.setPhoto(sel)  
    88 91  
  • trunk/plugins/comment/comment.glade

    r1 r145  
    19 19   <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>  
    20 20   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>  
    21     <property name="focus_on_map">True</property>  
    22 21   <signal name="delete_event" handler="on_winComment_delete_event" last_modification_time="Thu, 19 May 2005 08:19:46 GMT"/>  
    23 22  
     
    58 57                   <property name="xpad">0</property>  
    59 58                   <property name="ypad">0</property>  
    60                     <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>  
    61                     <property name="width_chars">-1</property>  
    62                     <property name="single_line_mode">False</property>  
    63                     <property name="angle">0</property>  
    64 59                 </widget>  
    65 60                 <packing>  
     
    87 82                       <property name="accepts_tab">False</property>  
    88 83                       <property name="justification">GTK_JUSTIFY_LEFT</property>  
    89                         <property name="wrap_mode">GTK_WRAP_CHAR</property>  
      84                       <property name="wrap_mode">GTK_WRAP_WORD</property>  
    89 84                       <property name="cursor_visible">True</property>  
    90 85                       <property name="pixels_above_lines">0</property>  
     
    125 120               <property name="xpad">0</property>  
    126 121               <property name="ypad">0</property>  
    127                 <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>  
    128                 <property name="width_chars">-1</property>  
    129                 <property name="single_line_mode">False</property>  
    130                 <property name="angle">0</property>  
    131 122             </widget>  
    132 123             <packing>  
     
    136 127  
    137 128           <child>  
    138               <widget class="GtkVBox" id="vbox2">  
      129             <widget class="GtkHBox" id="hbox1">  
    138 129               <property name="visible">True</property>  
    139 130               <property name="homogeneous">False</property>  
     
    142 133  
    143 134               <child>  
    144                   <widget class="GtkLabel" id="lbl_one">  
    145                     <property name="visible">True</property>  
    146                     <property name="label" translatable="yes">label4</property>  
    147                     <property name="use_underline">False</property>  
    148                     <property name="use_markup">False</property>  
    149                     <property name="justify">GTK_JUSTIFY_LEFT</property>  
    150                     <property name="wrap">False</property>  
    151                     <property name="selectable">False</property>  
    152                     <property name="xalign">0.5</property>  
    153                     <property name="yalign">0.5</property>  
    154                     <property name="xpad">0</property>  
    155                     <property name="ypad">0</property>  
    156                     <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>  
    157                     <property name="width_chars">-1</property>  
    158                     <property name="single_line_mode">False</property>  
    159                     <property name="angle">0</property>  
    160                   </widget>  
    161                   <packing>  
    162                     <property name="padding">0</property>  
    163                     <property name="expand">False</property>  
    164                     <property name="fill">False</property>  
    165                   </packing>  
      135                 <placeholder/>  
    166 136               </child>  
    167 137  
    168 138               <child>  
    169                   <widget class="GtkHScrollbar" id="hscrollbar1">  
      139                 <widget class="GtkScrolledWindow" id="scrolledwindow2">  
    169 139                   <property name="visible">True</property>  
    170                     <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>  
    171                     <property name="inverted">False</property>  
    172                     <property name="adjustment">0 0 0 0 0 0</property>  
    173                     <signal name="value_changed" handler="on_hscrollbar1_value_changed" last_modification_time="Thu, 19 May 2005 08:47:44 GMT"/>  
    174                   </widget>  
    175                   <packing>  
    176                     <property name="padding">2</property>  
    177                     <property name="expand">False</property>  
    178                     <property name="fill">True</property>  
    179                   </packing>  
    180                 </child>  
    181    
    182                 <child>  
    183                   <widget class="GtkHBox" id="hbox1">  
    184                     <property name="visible">True</property>  
    185                     <property name="homogeneous">False</property>  
    186                     <property name="spacing">0</property>  
    187    
    188                     <child>  
    189                       <widget class="GtkImage" id="image1">  
    190                         <property name="visible">True</property>  
    191                         <property name="xalign">0.5</property>  
    192                         <property name="yalign">0.5</property>  
    193                         <property name="xpad">0</property>  
    194                         <property name="ypad">0</property>  
    195                       </widget>  
    196                       <packing>  
    197                         <property name="padding">0</property>  
    198                         <property name="expand">False</property>  
    199                         <property name="fill">True</property>  
    200                       </packing>  
    201                     </child>  
      140                   <property name="can_focus">True</property>  
      141                   <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>  
      142                   <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>  
      143                   <property name="shadow_type">GTK_SHADOW_IN</property>  
      144                   <property name="window_placement">GTK_CORNER_TOP_LEFT</property>  
    202 145  
    203 146                   <child>  
    204                       <widget class="GtkScrolledWindow" id="scrolledwindow2">  
      147                     <widget class="GtkTextView" id="tv_comment">  
    204 147                       <property name="visible">True</property>  
    205 148                       <property name="can_focus">True</property>  
    206                         <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>  
    207                         <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>  
    208                         <property name="shadow_type">GTK_SHADOW_IN</property>  
    209                         <property name="window_placement">GTK_CORNER_TOP_LEFT</property>  
    210    
    211                         <child>  
    212                           <widget class="GtkTextView" id="tv_comment">  
    213                             <property name="visible">True</property>  
    214                             <property name="can_focus">True</property>  
    215                             <property name="editable">True</property>  
    216                             <property name="overwrite">False</property>  
    217                             <property name="accepts_tab">False</property>  
    218                             <property name="justification">GTK_JUSTIFY_LEFT</property>  
    219                             <property name="wrap_mode">GTK_WRAP_CHAR</property>  
    220                             <property name="cursor_visible">True</property>  
    221                             <property name="pixels_above_lines">0</property>  
    222                             <property name="pixels_below_lines">0</property>  
    223                             <property name="pixels_inside_wrap">0</property>  
    224                             <property name="left_margin">0</property>  
    225                             <property name="right_margin">0</property>  
    226                             <property name="indent">0</property>  
    227                             <property name="text" translatable="yes"></property>  
    228                             <signal name="key_press_event" handler="on_tv_comment_key_press_event" last_modification_time="Thu, 19 May 2005 08:34:49 GMT"/>  
    229                           </widget>  
    230                         </child>  
      149                       <property name="editable">True</property>  
      150                       <property name="overwrite">False</property>  
      151                       <property name="accepts_tab">False</property>  
      152                       <property name="justification">GTK_JUSTIFY_LEFT</property>  
      153                       <property name="wrap_mode">GTK_WRAP_WORD</property>  
      154                       <property name="cursor_visible">True</property>  
      155                       <property name="pixels_above_lines">0</property>  
      156                       <property name="pixels_below_lines">0</property>  
      157                       <property name="pixels_inside_wrap">0</property>  
      158                       <property name="left_margin">0</property>  
      159                       <property name="right_margin">0</property>  
      160                       <property name="indent">0</property>  
      161                       <property name="text" translatable="yes"></property>  
      162                       <signal name="key_press_event" handler="on_tv_comment_key_press_event" last_modification_time="Thu, 19 May 2005 08:34:49 GMT"/>  
    231 163                     </widget>  
    232                       <packing>  
    233                         <property name="padding">0</property>  
    234                         <property name="expand">True</property>  
    235                         <property name="fill">True</property>  
    236                       </packing>  
    237 164                   </child>  
    238 165                 </widget>  
     
    265 192               <property name="xpad">0</property>  
    266 193               <property name="ypad">0</property>  
    267                 <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>  
    268                 <property name="width_chars">-1</property>  
    269                 <property name="single_line_mode">False</property>  
    270                 <property name="angle">0</property>  
    271 194             </widget>  
    272 195             <packing>  
  • trunk/plugins/multiexport/__init__.py

    r142 r145  
    32 32 import os,time,shutil  
    33 33  
    34    
    35 34 class ExportConf(object):  
    36 35     __attrs={  
     
    76 75         "FR.public": 0,  
    77 76         "FR.friends": 0,  
    78           "FR.family": 0,       # 0 or 1  
      77         "FR.family": 0,  
      78         "FR.same_privacy":0,       # 0 or 1  
    79 79  
    80 80             "FR.resize":0,    # 0:NO, 1:PERCENT, 2:MAXSIDE  
     
    164 164         XSL = ["xsl/"+i for i in os.listdir("xsl") if i[0]!="."]  
    165 165  
    166           window_export = Windowexport(ec,"%d photos" % len(list),XSL)  
      166         window_export = Windowexport(ec,"%d photos" % len(list),list,XSL)  
    166 166  
    167 167         type=window_export.loop()[0]  
     
    305 305                             album.uploadPhoto(file,photo.comment)  
    306 306                         elif type == "FR":  
    307                               err=flickr_uploader.upload(file,photo.comment,photo.tags,(ec["FR.public"],ec["FR.friends"],ec["FR.family"]))  
      307                             err=flickr_uploader.upload(file,photo.comment,photo.tags,window_export.getPrivacyFR(photo))  
    307 307                             if err: raise err  
    308 308                         elif type == "SM":  
     
    379 379  
    380 380     def validateWin(self,url):  
    381            
    382 381         msg=_("""You need to give jBrout permission to upload your photos to Flickr.  
    383 382 This is done via a Flickr-session in your web browser to which you are logged in.  
  • trunk/plugins/multiexport/winexport.py

    r140 r145  
    15 15 import gtk  
    16 16 import time  
    17    
    18   """You need to give jBrout permission to upload your photos to Flickr. This is done via a Flickr-session in which you are logged in. Select your browser and press the Validate-button to open a browser window in which in which you can give your permission.  
    19   After you give your permission, you should return to this window and press the OK-button."""  
    20    
      17 from common import PictureSelector  
    21 18  
    22 19 from __main__ import GladeApp  
     
    44 41     return ret  
    45 42  
    46    
    47 43 class Windowexport(GladeApp):  
    48 44  
    49 45     glade=os.path.join(os.path.dirname(__file__), 'winexport.glade')  
    50 46  
    51       def init(self,conf,titre,templateList):  
      47     def init(self,conf,titre,photoList,templateList):  
    51 47         self.main_widget.set_modal(True)  
    52 48         #~ self.main_widget.set_keep_above(True)  
     
    55 51         self.lblTitre.set_text(titre)  
    56 52  
    57    
    58 53         cell = gtk.CellRendererText()  
    59 54         self.cbTemplate.pack_start(cell, True)  
     
    66 61         self.cbTemplate.set_model(m)  
    67 62  
      63         self.photoList = photoList  
      64  
      65         self.privacyFR = {}  
      66  
      67         # Create the Flickr thumbnail selector  
      68         self.psThumbSelectFR = PictureSelector(self.photoList)  
      69         self.psThumbSelectFR.connect("value_changed", self.on_psThumbSelectFR_value_changed)  
      70         self.tableFlickr.attach(self.psThumbSelectFR, 1, 2, 0, 1)  
      71         self.psThumbSelectFR.show()  
    68 72  
    69 73         self.initFromConf(conf)  
      74  
      75         self.setPrivacyFR(apply_to_all = True)  
      76  
    70 77         self.on_nbExport_switch_page(None)  #simulate tab changed  
    71 78  
     
    96 103         self.tbLoginPW.set_text( conf["PW.login"] )  
    97 104         self.tbPasswordPW.set_text( conf["PW.password"] )  
    98           self.cbPrivacyPW.set_active( conf["PW.privacy"] )  
      105         if (bool(conf["PW.privacy"])): self.rbPrivatePW.set_active(1)  
      106         else: self.rbPublicPW.set_active(1)  
    99 107  
    100 108         if (bool(conf["FR.public"])): self.rbPublicFR.set_active(1)  
     
    104 112         if (bool(conf["FR.family"])): self.cbFamilyFR.set_active(1)  
    105 113         else: self.cbFamilyFR.set_active(0)  
      114         if (bool(conf["FR.same_privacy"])): self.cbSelectAllFR.set_active(1)  
      115         else: self.cbSelectAllFR.set_active(0)  
    106 116  
    107 117         self.tbSmtp.set_text( conf["SM.smtp"] )  
     
    149 159             self.__conf["PW.login"]=self.tbLoginPW.get_text(  )  
    150 160             self.__conf["PW.password"]=self.tbPasswordPW.get_text(  )  
    151               self.__conf["PW.privacy"]=self.cbPrivacyPW.get_active(  )  
      161             self.__conf["PW.privacy"]=int(self.rbPrivatePW.get_active())  
    151 161         elif type == "FR":  
    152 162             self.__conf["FR.public"]=int(self.rbPublicFR.get_active())  
     
    217 227             self.tbFolderF.set_text( ret )  
    218 228  
      229     # Stuff to make the privacy selections work  
      230      
      231     def on_psThumbSelectFR_value_changed(self, widget, *args):  
      232         # This method is called when the value of the Flickr slider changes  
      233         photo_num = widget.getValue()  
      234         is_public, is_friends, is_family = self.privacyFR[self.photoList[photo_num]]  
      235         if (is_public):  
      236             self.rbPublicFR.set_active(True)  
      237         else:  
      238           self.rbPrivateFR.set_active(True)  
      239         self.cbFriendsFR.set_active(is_friends)  
      240         self.cbFamilyFR.set_active(is_family)  
      241  
      242     def on_cbSelectAllFR_toggled(self, widget, *args):  
      243         if (self.cbSelectAllFR.get_active()):  
      244             self.psThumbSelectFR.set_sensitive(False)  
      245             self.setPrivacyFR()  
      246         else:  
      247             self.psThumbSelectFR.set_sensitive(True)  
      248              
    219 249     def on_rbPrivateFR_toggled(self, widget, *args):  
    220           self.cbFriendsFR.set_sensitive(True)  
    221           self.cbFamilyFR.set_sensitive(True)  
    222    
    223       def on_rbPublicFR_toggled(self, widget, *args):  
    224           self.cbFriendsFR.set_sensitive(False)  
    225           self.cbFamilyFR.set_sensitive(False)  
    226    
    227    
      250         # (De)activates the friends and family checkbuttons in the Flickr tab when "public" is (de)selected  
      251         if (widget.get_active()):  
      252             self.cbFriendsFR.set_sensitive(True)  
      253             self.cbFamilyFR.set_sensitive(True)  
      254         else:  
      255             self.cbFriendsFR.set_sensitive(False)  
      256             self.cbFamilyFR.set_sensitive(False)  
      257         self.setPrivacyFR()  
      258  
      259     def on_friends_or_family_toggled(self, *args):  
      260         # The checkbuttons for privacy can't call setPrivacy directly,because they give extra arguments which setPrivacy can't use  
      261         self.setPrivacyFR()  
      262          
      263     def setPrivacyFR(self, apply_to_all = None):  
      264         # Set the Flickr privacy as indicated in the window to the photos indicated in te window  
      265         # If apply_to_all is given, it overrides the checkbutton (used for initialization)  
      266          
      267         if (apply_to_all == None):  
      268             apply_to_all = self.cbSelectAllFR.get_active()  
      269          
      270         photo = self.photoList[int(self.psThumbSelectFR.getValue())]  
      271         is_public = self.rbPublicFR.get_active()  
      272         is_friends = self.cbFriendsFR.get_active()  
      273         is_family = self.cbFamilyFR.get_active()  
      274         privacy = [is_public, is_friends, is_family]  
      275              
      276         if not (apply_to_all):  
      277             self.privacyFR[photo] = privacy  
      278         else:  
      279             for photo in self.photoList:  
      280                 self.privacyFR[photo] = privacy  
      281                  
      282     def getPrivacyFR(self, photo):  
      283         return self.privacyFR[photo]  
      284          
  • trunk/plugins/multiexport/libs/flickr.py

    r142 r145  
    26 26     # Call the Flickr API method with the args dict  
    27 27     # Return a sequence of True and a xml object if the method succeeds, otherwise a sequence of False and the error message  
    28    
      28      
    28 28     # Construct the post data  
    29 29     args["method"] = method  
    30 30     post_data = self.__makePost(args, sign)  
    31    
      31      
    31 31     # Make the REST request  
    32 32     return self.__makeRESTRequest("http://api.flickr.com/services/rest/", post_data)  
     
    37 37     # Upload the photo "filepath" with comment "comment", and a list of tags  
    38 38     # to Flickr. Privacy is [bool, bool, bool] (public, friend, family)  
    39    
      39      
    39 39     # Get the filename  
    40 40     assert type(filepath) == unicode  
     
    43 43     filename = os.path.split(filepath)[-1]  
    44 44  
      45     for index in range(len(privacy)):  
      46         if (privacy[index] == True): privacy[index] = 1  
      47         elif (privacy[index] == False): privacy[index] = 0  
      48      
    45 49     # Construct a string of escaped tags  
    46 50     tags_str = ""  
     
    53 57     response = self.__makeRESTRequest("http://api.flickr.com/services/upload/", post_data, boundary)  
    54 58     return response  
    55    
      59      
    55 59   def obtainToken(self, perms):  
    56 60     # Get a token with the appropriate permissions.  
    57    
      61      
    57 61     token = None  
    58 62  
     
    87 91             return True  
    88 92     return False  
    89    
      93        
    89 93   def __makeRESTRequest(self, url, post_data, boundary = None):  
    90 94     # Make a REST request to Flickr with the url and the post data. Boundary is needed for multipart/form-data  
    91    
      95      
    91 95     # Construct the request  
    92 96     req = urllib2.Request(url)  
     
    97 101       # We're dealing with multipart/form-data instead of application/x-www-form-urlencoded, ans we should tell our request so  
    98 102       req.add_header("Content-Type", "multipart/form-data; boundary=%s" % boundary)  
    99    
      103    
    99 103     # Get a response  
    100 104     response = urllib2.urlopen(req).read()  
    101 105     response_obj = minidom.parseString(response)  
    102    
      106      
    102 106     # Return failure or success  
    103 107     if (response_obj.childNodes[0].getAttribute("stat") == "fail"):  
     
    107 111     else:  
    108 112       return [True, response_obj]  
    109    
      113      
    109 113   def __makePost(self, args, sign = False):  
    110 114     # Construct POST data with api key and such  
    111    
      115      
    111 115     # Add the api_key to the args list  
    112 116     args["api_key"] = self.api_key  
    113    
      117      
    113 117     # Calculate the signature  
    114 118     if (sign):  
    115 119       args["api_sig"] = self.__getSignature(args)  
    116    
      120      
    116 120     return urllib.urlencode(args)  
    117 121  
    118 122   def __makeImagePost(self, args, filepath):  
    119 123     # Create the POST data for image uploading, which is a bit different then normal POST data  
    120    
      124      
    120 124     # Insert the API key  
    121 125     args["api_key"] = self.api_key  
    122    
      126      
    122 126     # Get the signature  
    123 127     args["api_sig"] = self.__getSignature(args)  
    124    
      128      
    124 128     # Choose a MIME boundary  
    125 129     boundary = mimetools.choose_boundary()  
     
    145 149     post_data += 'Content-Disposition: form-data; name=\"photo\"; filename=\"%s\"\r\n' % os.path.split(filepath)[-1]  
    146 150     post_data += "Content-Type: %s\r\n\r\n" % mimetypes.guess_type(filepath)[0]  
    147    
      151      
    147 151     photo = open(filepath).read()  
    148 152     post_data += photo  
     
    153 157     terminator = terminator.encode("utf_8")  
    154 158     post_dat