Changeset 145
- Timestamp:
- Fri Dec 15 12:28:17 2006
- Files:
-
- trunk/common.py (modified) (diff)
- trunk/plugins/comment/__init__.py (modified) (diff)
- trunk/plugins/comment/comment.py (modified) (diff)
- trunk/plugins/comment/comment.glade (modified) (diff)
- trunk/plugins/multiexport/__init__.py (modified) (diff)
- trunk/plugins/multiexport/winexport.py (modified) (diff)
- trunk/plugins/multiexport/libs/flickr.py (modified) (diff)
- trunk/plugins/multiexport/winexport.glade (modified) (diff)
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="Gtk VBox" 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="Gtk HScrollbar" 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="Gtk ScrolledWindow" 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
