~marc.stewart/crebs/jane

« back to all changes in this revision

Viewing changes to crebs/bin/crebs

Merged "Bella" branch: improved file addition, including drag-and-drop; xml.dom replaced by libxml2; readme and install script added

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
 
3
 
#       CreBS 0.9.1.1
 
3
#       CreBS 0.9.2.0
4
4
#   =====================
5
5
#
6
6
#               Cre(ate) B(ackground) S(lideshow):
11
11
#               <http://www.obfuscatepenguin.net/crebs/>
12
12
 
13
13
 
14
 
## ==== INFORMATION =====================================================
15
 
#
16
 
## ---- Licence ---------------------------------------------------------
 
14
## ==== LICENCE =========================================================
17
15
#
18
16
#   This program is free software: you can redistribute it and/or modify
19
17
#   it under the terms of the GNU General Public License as published by
28
26
#   You should have received a copy of the GNU General Public License
29
27
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
30
28
#
31
 
## --------------------------------------------------------- Licence ----
32
 
 
33
 
## ---- Important Assumptions -------------------------------------------
34
 
#
35
 
#   * The user running the program has a /home/<USER>/.gnome2 directory;
36
 
#   * It contains backgrounds.xml, for listing the user's backgrounds;
37
 
#   * Only PNG, JPEG, GIF, and SVG formats are used for backgrounds.
38
 
#
39
 
## ------------------------------------------- Important Assumptions ----
40
 
 
41
 
## ===================================================== INFORMATION ====
 
29
## ========================================================= LICENCE ====
42
30
 
43
31
 
44
32
import sys
45
33
import os
46
34
import gconf
47
35
import pynotify
48
 
from xml.dom import minidom
 
36
import libxml2
 
37
import urllib
 
38
import re
49
39
 
50
40
try:
51
41
    import pygtk
66
56
    def __init__(self):
67
57
        """Sets up default data and GUI."""
68
58
 
69
 
        print "CreBS 0.9.1.1"
 
59
        print "CreBS 0.9.2"
70
60
        print "Copyright (C) 2010 Marc Stewart"
71
61
        print "    This program comes with ABSOLUTELY NO WARRANTY."
72
62
        print "    This is free software, and you are welcome to redistribute it"
76
66
        print "Initiating CreBS class..."
77
67
 
78
68
        # Set up internal data
79
 
        self.fresh = True
 
69
        self.open_dir = os.getenv("HOME")
80
70
        self.lstImages = gtk.ListStore(str, gtk.gdk.Pixbuf, bool, int, int, bool, int)
81
71
            # filename, image, use default time, specific time, specific scale, use default transition, specific transition
82
72
        self.default_time = 15
88
78
        # Set up GUIcombine arrays
89
79
        b = gtk.Builder()
90
80
 
91
 
        if (os.path.dirname(os.path.abspath(__file__)) == '/usr/bin'):
92
 
            # CreBS in installed
93
 
            b.add_from_file("/usr/share/crebs/glade/create-background-slideshow.glade")
94
 
            b.add_from_file("/usr/share/crebs/glade/add-images.glade")
95
 
            b.add_from_file("/usr/share/crebs/glade/clear-images.glade")
96
 
        else:
97
 
            # Use local (uninstalled) files
98
 
            b.add_from_file("glade/create-background-slideshow.glade")
99
 
            b.add_from_file("glade/add-images.glade")
100
 
            b.add_from_file("glade/clear-images.glade")
 
81
        prefix = os.path.realpath(os.path.dirname(os.path.abspath(__file__)) + '/..')
 
82
        b.add_from_file(prefix + "/share/crebs/glade/create-background-slideshow.glade")
 
83
        b.add_from_file(prefix + "/share/crebs/glade/add-images.glade")
 
84
        b.add_from_file(prefix + "/share/crebs/glade/clear-images.glade")
101
85
 
102
86
        # Link to GUI actions
103
87
        b.connect_signals({
123
107
            "on_named" : self.check_if_saveable,
124
108
            "on_enter" : self.try_to_save,
125
109
            "on_save" : self.save_slideshow,
 
110
            "on_drop" : self.add_by_drop,
126
111
 
127
112
            "on_confirm_clear" : self.clear_images,
128
113
            "on_keep" : self.close_clear_confirmation,
138
123
 
139
124
        self.addFileFiltersToAddDialog()
140
125
 
 
126
        self.window.drag_dest_set( gtk.DEST_DEFAULT_MOTION |
 
127
                  gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
 
128
                  [ ( 'text/uri-list', 0, 80 ) ],
 
129
                  gtk.gdk.ACTION_COPY)
 
130
 
141
131
        self.icvImages = b.get_object("icvImages")
142
132
 
143
133
        self.btnAdd = b.get_object("btnAdd")
292
282
        try:
293
283
            f = open(potential, 'r')
294
284
            try:
295
 
                filestart = f.read(12)
 
285
                filestart = f.read(1024)
296
286
            finally:
297
287
                f.close()
298
288
        except IOError:
299
 
            print "    ERROR: Cannot read from potential background file (" + potential + ")."
 
289
            if os.path.isdir(potential):
 
290
                print "    " + potential + " is a directory!"
 
291
            else:
 
292
                print "    ERROR: Cannot read from potential background file (" + potential + ")."
300
293
            return False
301
294
        else:
302
 
            if filestart == '<background>':
 
295
            if  re.search('background', filestart) \
 
296
                and \
 
297
                re.search('starttime', filestart) \
 
298
                and \
 
299
                re.search('year', filestart) \
 
300
                and \
 
301
                re.search('month', filestart) \
 
302
                and \
 
303
                re.search('day', filestart):
303
304
                return True
304
305
            else:
305
306
                return False
307
308
 
308
309
    def show_add_dialog(self, widget):
309
310
        """Shows a file-chooser dialog to add images."""
310
 
        if (self.fresh == True):
311
 
            self.dlgAddImages.set_current_folder(os.getenv("HOME"))
312
 
            self.fresh == False
 
311
        self.dlgAddImages.set_current_folder(self.open_dir)
313
312
        self.dlgAddImages.show()
314
313
 
315
314
 
326
325
        target_size = 96
327
326
 
328
327
        print "Adding an image to the slideshow..."
329
 
        pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
330
 
        aspect_ratio = float(pixbuf.get_width()) / float(pixbuf.get_height())
331
 
 
332
 
        if (aspect_ratio >= 1):
333
 
            x = target_size
334
 
            y = int(target_size / aspect_ratio)
335
 
        else:
336
 
            y = target_size
337
 
            x = int(target_size * aspect_ratio)
338
 
 
339
 
        image.set_from_pixbuf(pixbuf.scale_simple(x,y,gtk.gdk.INTERP_BILINEAR))
340
 
        print "    " + filename
341
 
        print "        {1}x{2} ({0})".format(aspect_ratio, pixbuf.get_width(), pixbuf.get_height())
342
 
 
343
 
        if (spec_dur < 1) or (spec_trn < 0):
344
 
            self.lstImages.append([filename, image.get_pixbuf(), True, self.DurDefault.get_value(), self.cmbDurScaleDefault.get_active(), True, self.TrnDefault.get_value()])
345
 
        else:
346
 
            if spec_dur % 3600 == 0:
347
 
                #hours
348
 
                dur_scaled = spec_dur / 3600
349
 
                scale = 2
350
 
            else:
351
 
                if spec_dur % 60 == 0:
352
 
                    #minutes
353
 
                    dur_scaled = spec_dur / 60
354
 
                    scale = 1
355
 
                else:
356
 
                    #seconds
357
 
                    dur_scaled = spec_dur
358
 
                    scale = 0
359
 
 
360
 
            if (dur_scaled == self.DurDefault.get_value()) and (scale == self.cmbDurScaleDefault.get_active()):
361
 
                use_def_dur = True
362
 
            else:
363
 
                use_def_dur = False
364
 
 
365
 
            if (spec_trn == self.TrnDefault.get_value()):
366
 
                use_def_trn = True
367
 
            else:
368
 
                use_def_trn = False
369
 
 
370
 
            self.lstImages.append([filename, image.get_pixbuf(), use_def_dur, dur_scaled, scale, use_def_trn, spec_trn])
371
 
 
372
 
        print "    Slideshow now has " + str(len(self.lstImages)) + " image(s)."
373
 
        return True
 
328
        try:
 
329
            pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
 
330
        except:
 
331
            print "    Can't add", filename, "(not a valid image or can't generate thumbnail)"
 
332
            return False
 
333
        else:
 
334
            aspect_ratio = float(pixbuf.get_width()) / float(pixbuf.get_height())
 
335
 
 
336
            if (aspect_ratio >= 1):
 
337
                x = target_size
 
338
                y = int(target_size / aspect_ratio)
 
339
            else:
 
340
                y = target_size
 
341
                x = int(target_size * aspect_ratio)
 
342
 
 
343
            image.set_from_pixbuf(pixbuf.scale_simple(x,y,gtk.gdk.INTERP_BILINEAR))
 
344
            print "    " + filename
 
345
            print "        {1}x{2} ({0})".format(aspect_ratio, pixbuf.get_width(), pixbuf.get_height())
 
346
 
 
347
            if (spec_dur < 1) or (spec_trn < 0):
 
348
                self.lstImages.append([filename, image.get_pixbuf(), True, self.DurDefault.get_value(), self.cmbDurScaleDefault.get_active(), True, self.TrnDefault.get_value()])
 
349
            else:
 
350
                if spec_dur % 3600 == 0:
 
351
                    #hours
 
352
                    dur_scaled = spec_dur / 3600
 
353
                    scale = 2
 
354
                else:
 
355
                    if spec_dur % 60 == 0:
 
356
                        #minutes
 
357
                        dur_scaled = spec_dur / 60
 
358
                        scale = 1
 
359
                    else:
 
360
                        #seconds
 
361
                        dur_scaled = spec_dur
 
362
                        scale = 0
 
363
 
 
364
                if (dur_scaled == self.DurDefault.get_value()) and (scale == self.cmbDurScaleDefault.get_active()):
 
365
                    use_def_dur = True
 
366
                else:
 
367
                    use_def_dur = False
 
368
 
 
369
                if (spec_trn == self.TrnDefault.get_value()):
 
370
                    use_def_trn = True
 
371
                else:
 
372
                    use_def_trn = False
 
373
 
 
374
                self.lstImages.append([filename, image.get_pixbuf(), use_def_dur, dur_scaled, scale, use_def_trn, spec_trn])
 
375
 
 
376
            print "    Slideshow now has " + str(len(self.lstImages)) + " image(s)."
 
377
            return True
374
378
 
375
379
 
376
380
    def import_slideshow(self, filename):
377
381
        """Imports a pre-existing slideshow, including images and timings."""
378
382
        print "Importing slideshow " + filename + " ..."
379
383
        try:
380
 
            slides = minidom.parse(filename)
 
384
            slides = libxml2.parseFile(filename)
381
385
        except IOError:
382
386
            print "    Failed. Could not open and parse slideshow file."
383
387
            return False
384
388
        else:
385
 
            imagelist = slides.getElementsByTagName('file')
386
 
            st_list = slides.getElementsByTagName('static')
387
 
            tr_list = slides.getElementsByTagName('transition')
388
 
 
389
 
            for i in range(len(imagelist)):
390
 
                filename = imagelist[i].firstChild.data
391
 
 
392
 
                duration = -1
393
 
                for child in st_list[i].childNodes:
394
 
                    if child.nodeName == 'duration':
395
 
                        duration = float(child.firstChild.data)
396
 
 
397
 
                transition = -1
398
 
                for child in tr_list[i].childNodes:
399
 
                    if child.nodeName == 'duration':
400
 
                        transition = float(child.firstChild.data)
401
 
 
402
 
                added = self.add_image(filename, duration, transition)
 
389
            root = slides.getRootElement()
 
390
            if root.name != 'background':
 
391
                return False
 
392
            else:
 
393
                imagelist = []
 
394
                st_list = []
 
395
                tr_list = []
 
396
 
 
397
                b_children = root.children
 
398
                for b_child in b_children:
 
399
                    if b_child.name == 'static':
 
400
                        st_children = b_child.children
 
401
                        for st_child in st_children:
 
402
                            if st_child.name == 'file':
 
403
                                imagelist.append(str(st_child.children))
 
404
                            elif st_child.name == 'duration':
 
405
                                st_list.append(float(str(st_child.children)))
 
406
                    elif b_child.name == 'transition':
 
407
                        tr_children = b_child.children
 
408
                        for tr_child in tr_children:
 
409
                            if tr_child.name == 'duration':
 
410
                                tr_list.append(float(str(tr_child.children)))
 
411
 
 
412
                for i in range(len(imagelist)):
 
413
                    filename = imagelist[i]
 
414
                    duration = st_list[i]
 
415
                    transition = tr_list[i]
 
416
 
 
417
                    added = self.add_image(filename, duration, transition)
403
418
 
404
419
                success = False
405
420
                if added:
408
423
            return success
409
424
 
410
425
 
411
 
    def add_images(self, widget):
 
426
    def add_images(self, widget, filenames=False):
412
427
        """Add images to the slideshow."""
413
428
 
414
 
        self.dlgAddImages.hide()
415
 
        filenames = self.dlgAddImages.get_filenames()
 
429
        if not filenames:
 
430
            filenames = self.dlgAddImages.get_filenames()
 
431
            for filename in filenames:
 
432
                if os.path.isdir(filename):
 
433
                    self.dlgAddImages.set_current_folder(filename)
 
434
                    return False
 
435
            self.open_dir = self.dlgAddImages.get_current_folder()
 
436
            self.dlgAddImages.hide()
416
437
 
417
438
        for filename in filenames:
418
439
            if self.is_slideshow_file((filename, "this must be a tuple")):
428
449
        self.check_if_clearable()
429
450
 
430
451
 
 
452
    def add_by_drop(self, widget, context, x, y, selection, target_type, timestamp):
 
453
        """Add an image by dropping it onto the window."""
 
454
        print "Files dropped onto main window."
 
455
        if target_type == 80:
 
456
            print "Processing dropped files..."
 
457
            filenames=[]
 
458
            path_list = selection.data.strip('\r\n\x00').split()
 
459
            for path in path_list:
 
460
                if path.startswith('file://'):
 
461
                    path = path[7:]
 
462
                elif path.stratswith('file:'):
 
463
                    path = path[5:]
 
464
                # Fix characters escaped by Nautilus, e.g. %20 -> space
 
465
                path = urllib.url2pathname(path)
 
466
                filenames.append(path)
 
467
 
 
468
            self.add_images(widget, filenames)
 
469
            print "    Dropped file(s) processed."
 
470
 
 
471
 
431
472
    def copy_image(self, widget):
432
473
        """Copies the selected image."""
433
474