diff --git a/resources/glade/config.glade b/resources/glade/config.glade
index e763ee7..506f255 100644
--- a/resources/glade/config.glade
+++ b/resources/glade/config.glade
@@ -89,16 +89,57 @@
3
3
-
+
True
0
- Eject program:
- ejt_entry
+ Mount point:
+ mnt_entry
+
+
+
+
+
+
+ 100
+ True
+ True
+
+
+ 1
+ 2
+
+
+
+
+
+ True
+ True
+ Browse...
+ True
+ 0
+
+
+
+
+ 2
+ 3
+
+
+
+
+
+
+ 100
+ True
+ True
+
+
+ 1
+ 2
1
2
-
@@ -122,56 +163,15 @@
-
- 100
- True
- True
-
-
- 1
- 2
- 1
- 2
-
-
-
-
-
- True
- True
- Browse...
- True
- 0
-
-
-
-
- 2
- 3
-
-
-
-
-
-
- 100
- True
- True
-
-
- 1
- 2
-
-
-
-
-
+
True
0
- Mount point:
- mnt_entry
+ Eject program:
+ ejt_entry
+ 1
+ 2
@@ -554,7 +554,19 @@ Movies extesions
2
3
-
+
+ True
+ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ Extension:
+ GTK_JUSTIFY_RIGHT
+ ext_entry
+
+
+ GTK_FILL
+
+
+
+
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
@@ -564,18 +576,6 @@ Movies extesions
2
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- Extension:
- GTK_JUSTIFY_RIGHT
- ext_add
-
-
- GTK_FILL
-
-
False
@@ -589,23 +589,25 @@ Movies extesions
3
GTK_BUTTONBOX_END
-
+
True
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
Add
0
+
-
+
True
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
Delete
0
+
1
@@ -630,7 +632,6 @@ Movies extesions
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- True
True
diff --git a/resources/glade/main.glade b/resources/glade/main.glade
index 9cab256..e80e8a0 100644
--- a/resources/glade/main.glade
+++ b/resources/glade/main.glade
@@ -233,11 +233,9 @@
-
@@ -259,6 +257,7 @@
Thumbnails
True
True
+ list1
diff --git a/src/ctrls/c_config.py b/src/ctrls/c_config.py
index 61ce782..ef9d3d2 100644
--- a/src/ctrls/c_config.py
+++ b/src/ctrls/c_config.py
@@ -22,6 +22,7 @@
# -------------------------------------------------------------------------
from gtkmvc import Controller
+import views.v_dialogs as Dialogs
import gtk
@@ -66,7 +67,7 @@ class ConfigController(Controller):
# initialize models for files extensions
self.view['ext_choose'].set_model(self.model.ext_list)
self.view['ext_choose'].set_active(0)
-
+ self.view['extension_tree'].get_selection().set_mode(gtk.SELECTION_MULTIPLE)
self.view['config'].show();
return
# Podłącz sygnały:
@@ -125,6 +126,50 @@ class ConfigController(Controller):
return
def on_ext_choose_changed(self, widget):
+ self.__setup_extension_tree()
+ self.view['ext_entry'].set_text('')
+ return
+
+ def on_ext_add_clicked(self, widget):
+ ext = self.view['ext_entry'].get_text().lower()
+ if len(ext) == 0:
+ Dialogs.Err("Config - pyGTKtalog", "Error", "Extension is empty")
+ return
+ if self.view['ext_choose'].get_active() == 0:
+ if ext not in self.model.confd['img_ext']:
+ self.model.confd['img_ext'].append(ext)
+ self.model.refresh_ext('img_ext')
+ else:
+ self.model.confd['mov_ext'].append(ext)
+
+ self.__setup_extension_tree()
+ return
+
+ def on_ext_del_clicked(self, widget):
+ model, selection = self.view['extension_tree'].get_selection().get_selected_rows()
+ if len(selection) == 0:
+ Dialogs.Err("Config - pyGTKtalog", "Error", "No item selected")
+ return
+ elif len(selection) == 1:
+ sufix = ''
+ else:
+ sufix = "s"
+
+ if self.model.confd['delwarn']:
+ obj = Dialogs.Qst('Delete extension%s' % sufix,
+ 'Delete extension%s?' % sufix,
+ 'Object%s will be permanently removed.' % sufix)
+ if not obj.run():
+ return
+
+ if self.view['ext_choose'].get_active() == 0:
+ n = 'img_ext'
+ else:
+ n = 'mov_ext'
+
+ for i in selection:
+ self.model.confd[n].remove(model.get_value(model.get_iter(i), 0))
+
self.__setup_extension_tree()
return
@@ -132,9 +177,11 @@ class ConfigController(Controller):
# private controller methods
def __setup_extension_tree(self):
if self.view['ext_choose'].get_active() == 0:
- self.view['extension_tree'].set_model(self.model.images_tree)
+ self.model.refresh_ext('img_ext')
else:
- self.view['extension_tree'].set_model(self.model.movies_tree)
+ self.model.refresh_ext('mov_ext')
+
+ self.view['extension_tree'].set_model(self.model.ext_tree)
for i in self.view['extension_tree'].get_columns():
self.view['extension_tree'].remove_column(i)
diff --git a/src/models/m_config.py b/src/models/m_config.py
index e7218f3..fa40065 100644
--- a/src/models/m_config.py
+++ b/src/models/m_config.py
@@ -56,33 +56,39 @@ class ConfigModel(Model):
__properties__ = {}
- extensions_list = ['Images extensions', 'Movies extesions']
+ filetype_list = ['Images', 'Movies']
confd = {
- 'savewin' : True,
- 'savepan' : True,
- 'wx' : 800,
- 'wy' : 600,
- 'h' : 200,
- 'v' : 300,
- 'exportxls' : False,
- 'cd' : '/cdrom',
- 'ejectapp' : 'eject -r',
- 'eject' : True,
- 'thumbs': False,
- 'gthumb':False,
- 'exif':False,
- 'confirmquit':True,
- 'mntwarn':True,
- 'confirmabandon':True,
+ 'savewin': True,
+ 'savepan': True,
+ 'wx': 800,
+ 'wy': 600,
+ 'h': 200,
+ 'v': 300,
+ 'eject': True,
+ 'compress': True,
+
+ 'exportxls': False,
+
+ 'confirmquit': True,
+ 'confirmabandon': True,
+ 'mntwarn': True,
+ 'delwarn': True,
+
+ 'cd': '/cdrom',
+ 'ejectapp': 'eject -r',
+
+ 'retrive': False,
+
+ 'thumbs': True,
+ 'exif': True,
+ 'gthumb': False,
+
+ 'mov_ext': ['avi', 'mkv', 'mpg', 'mpeg', 'wmv'],
+ 'img_ext': ['bmp', 'gif', 'jpg', 'jpeg', 'png'],
+
'showtoolbar':True,
'showstatusbar':True,
- 'delwarn':True,
- 'compress':True,
- 'compress':True,
- 'retrive':False,
- 'mov_ext':['mkv', 'avi', 'ogg', 'mpg', 'wmv', 'mp4', 'mpeg'],
- 'img_ext':['jpg','jpeg','png','gif','bmp','tga','tif','tiff','ilbm','iff','pcx'],
}
dictconf = {
@@ -141,27 +147,27 @@ class ConfigModel(Model):
def __init__(self):
Model.__init__(self)
self.category_tree = gtk.ListStore(gobject.TYPE_STRING)
- self.images_tree = gtk.ListStore(gobject.TYPE_STRING)
- self.confd['img_ext'].sort()
- for i in self.confd['img_ext']:
- myiter = self.images_tree.insert_before(None,None)
- self.images_tree.set_value(myiter,0,i)
- self.movies_tree = gtk.ListStore(gobject.TYPE_STRING)
- self.confd['mov_ext'].sort()
- for i in self.confd['mov_ext']:
- myiter = self.movies_tree.insert_before(None,None)
- self.movies_tree.set_value(myiter,0,i)
-
self.ext_list = gtk.ListStore(gobject.TYPE_STRING)
- for i in self.extensions_list:
+ for i in self.filetype_list:
myiter = self.ext_list.insert_before(None,None)
self.ext_list.set_value(myiter,0,i)
+
+ self.refresh_ext('img_ext')
return
+
+
+ def refresh_ext(self, key):
+ self.ext_tree = gtk.ListStore(gobject.TYPE_STRING)
+ self.confd[key].sort()
+ for i in self.confd[key]:
+ myiter = self.ext_tree.insert_before(None,None)
+ self.ext_tree.set_value(myiter,0,i)
def save(self):
try:
os.lstat("%s/.pygtktalog" % self.path)
except:
+ print "Saving preferences to %s/.pygtktalog" % self.path
if __debug__:
print "m_config.py: save() Saving preferences to %s/.pygtktalog" % self.path
newIni = Ini()
diff --git a/src/models/m_main.py b/src/models/m_main.py
index 2448926..ff4251c 100644
--- a/src/models/m_main.py
+++ b/src/models/m_main.py
@@ -55,9 +55,159 @@ from m_config import ConfigModel
from m_details import DetailsModel
class Thumbnail(object):
- def __init__(self, *args):
- self.x = None
- self.y = None
+ def __init__(self, filename=None, x=160, y=120, root='thumbnails', base=''):
+ self.root = root
+ self.x = x
+ self.y = y
+ self.filename = filename
+ self.base = base
+
+ def save(self, image_id):
+ """Save thumbnail into specific directory structure
+ return full path to the file and exif object or None"""
+ filepath = os.path.join(self.base, self.__get_and_make_path(image_id))
+ f = open(self.filename, 'rb')
+ exif = None
+ returncode = -1
+ try:
+ exif = EXIF.process_file(f)
+ f.close()
+ if exif.has_key('JPEGThumbnail'):
+ thumbnail = exif['JPEGThumbnail']
+ f = open(filepath,'wb')
+ f.write(thumbnail)
+ f.close()
+ if exif.has_key('Image Orientation'):
+ orientation = exif['Image Orientation'].values[0]
+ if orientation > 1:
+ t = "/tmp/thumb%d.jpg" % datetime.now().microsecond
+ im_in = Image.open(filepath)
+ im_out = None
+ if orientation == 8:
+ # Rotated 90 CCW
+ im_out = im_in.transpose(Image.ROTATE_90)
+ elif orientation == 6:
+ # Rotated 90 CW
+ im_out = im_in.transpose(Image.ROTATE_270)
+ elif orientation == 3:
+ # Rotated 180
+ im_out = im_in.transpose(Image.ROTATE_180)
+ elif orientation == 2:
+ # Mirrored horizontal
+ im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT)
+ elif orientation == 4:
+ # Mirrored vertical
+ im_out = im_in.transpose(Image.FLIP_TOP_BOTTOM)
+ elif orientation == 5:
+ # Mirrored horizontal then rotated 90 CCW
+ im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
+ elif orientation == 7:
+ # Mirrored horizontal then rotated 90 CW
+ im_out = im_in.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
+
+ if im_out:
+ im_out.save(t, 'JPEG')
+ shutil.move(t, filepath)
+ else:
+ f.close()
+ returncode = 0
+ else:
+ im = self.__scale_image(True)
+ if im:
+ im.save(filepath, "JPEG")
+ returncode = 1
+ except:
+ f.close()
+ im = self.__scale_image(True)
+ if im:
+ im.save(filepath, "JPEG")
+ returncode = 2
+ return filepath, exif, returncode
+
+ # private class functions
+ def __get_and_make_path(self, img_id):
+ """Make directory structure regards of id
+ and return filepath WITHOUT extension"""
+ t = os.path.join(self.base, self.root)
+ try: os.mkdir(t)
+ except: pass
+ h = hex(img_id)
+ if len(h[2:])>6:
+ try: os.mkdir(os.path.join(t, h[2:4]))
+ except: pass
+ try: os.mkdir(os.path.join(t, h[2:4], h[4:6]))
+ except: pass
+ path = os.path.join(t, h[2:4], h[4:6], h[6:8])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" % (h[8:], 'jpg')
+ elif len(h[2:])>4:
+ try: os.mkdir(os.path.join(t, h[2:4]))
+ except: pass
+ path = os.path.join(t, h[2:4], h[4:6])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" % (h[6:], 'jpg')
+ elif len(h[2:])>2:
+ path = os.path.join(t, h[2:4])
+ try: os.mkdir(path)
+ except: pass
+ img = "%s.%s" %(h[4:], 'jpg')
+ else:
+ path = t
+ img = "%s.%s" %(h[2:], 'jpg')
+ return(os.path.join(t, img))
+
+ def __scale_image(self, factor=False):
+ """generate scaled Image object for given file
+ args:
+ factor - if False, adjust height into self.y
+ if True, use self.x for scale portrait pictures height.
+ returns Image object, or False
+ """
+ try:
+ im = Image.open(self.filename).convert('RGB')
+ except:
+ return False
+ x, y = im.size
+
+ if x > self.x or y > self.y:
+ if x==y:
+ # square
+ imt = im.resize((self.y, self.y), Image.ANTIALIAS)
+ elif x > y:
+ # landscape
+ if int(y/(x/float(self.x))) > self.y:
+ # landscape image: height is non standard
+ self.x1 = int(float(self.y) * self.y / self.x)
+ if float(self.y) * self.y / self.x - self.x1 > 0.49:
+ self.x1 += 1
+ imt = im.resize(((int(x/(y/float(self.y))),self.y)),Image.ANTIALIAS)
+ elif x/self.x==y/self.y:
+ # aspect ratio ok
+ imt = im.resize((self.x, self.y), Image.ANTIALIAS)
+ else:
+ imt = im.resize((self.x,int(y/(x/float(self.x)))), 1)
+ else:
+ # portrait
+ if factor:
+ if y>self.x:
+ imt = im.resize(((int(x/(y/float(self.x))),self.x)),Image.ANTIALIAS)
+ else:
+ imt = im
+ else:
+ self.x1 = int(float(self.y) * self.y / self.x)
+ if float(self.y) * self.y / self.x - self.x1 > 0.49:
+ self.x1 += 1
+
+ if x/self.x1==y/self.y:
+ # aspect ratio ok
+ imt = im.resize((self.x1,self.y),Image.ANTIALIAS)
+ else:
+ imt = im.resize(((int(x/(y/float(self.y))),self.y)),Image.ANTIALIAS)
+ return imt
+ else:
+ return im
class Picture(object):
def __init__(self, *args):
@@ -163,7 +313,39 @@ class MainModel(ModelMT):
return
os.chdir(self.internal_dirname)
- tar.extractall()
+ try:
+ tar.extractall()
+ except AttributeError:
+ # python's 2.4 tarfile module lacks of method extractall()
+ directories = []
+ for tarinfo in tar:
+ if tarinfo.isdir():
+ # Extract directory with a safe mode, so that
+ # all files below can be extracted as well.
+ try:
+ os.makedirs(os.path.join(path, tarinfo.name), 0777)
+ except EnvironmentError:
+ pass
+ directories.append(tarinfo)
+ else:
+ tar.extract(tarinfo, '.')
+
+ # Reverse sort directories.
+ directories.sort(lambda a, b: cmp(a.name, b.name))
+ directories.reverse()
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ path = os.path.join(path, tarinfo.name)
+ try:
+ os.chown(tarinfo, '.')
+ os.utime(tarinfo, '.')
+ os.chmod(tarinfo, '.')
+ except ExtractError, e:
+ if tar.errorlevel > 1:
+ raise
+ else:
+ tar._dbg(1, "tarfile: %s" % e)
tar.close()
self.__connect_to_db()
@@ -218,9 +400,12 @@ class MainModel(ModelMT):
self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY)
# all the rest
- self.db_cursor.execute("SELECT id, filename, size, date, type \
- FROM files WHERE parent_id=? AND type!=1 \
- ORDER BY filename", (id,))
+ self.db_cursor.execute("SELECT f.id, f.filename, f.size, f.date, \
+ f.type, f.filetype, t.filename \
+ FROM files f \
+ LEFT JOIN thumbnails t ON f.id = t.file_id \
+ WHERE f.parent_id=? AND f.type!=1 \
+ ORDER BY f.filename", (id,))
data = self.db_cursor.fetchall()
for ch in data:
myiter = self.files_list.insert_before(None, None)
@@ -231,7 +416,10 @@ class MainModel(ModelMT):
self.files_list.set_value(myiter, 4, ch[4])
self.files_list.set_value(myiter, 5, 'kategoria srategoria')
if ch[4] == self.FIL:
- self.files_list.set_value(myiter, 6, gtk.STOCK_FILE)
+ if ch[5] == self.F_IMG and ch[6] != None:
+ self.files_list.set_value(myiter, 6, gtk.STOCK_FILE)
+ else:
+ self.files_list.set_value(myiter, 6, gtk.STOCK_FILE)
elif ch[4] == self.LIN:
self.files_list.set_value(myiter, 6, gtk.STOCK_INDEX)
return
@@ -372,6 +560,10 @@ class MainModel(ModelMT):
thumb_x INTEGER,
thumb_y INTEGER,
thumb_mode TEXT);""")
+ self.db_cursor.execute("""create table
+ thumbnails(id INTEGER PRIMARY KEY AUTOINCREMENT,
+ file_id INTEGER,
+ filename TEXT);""")
self.db_cursor.execute("insert into files values(1, 1, 'root', null, \
0, 0, 0, 0, null, null, null, null);")
@@ -388,10 +580,6 @@ class MainModel(ModelMT):
timestamp = datetime.now()
- # TODO: file types has to be moved to configuration model
- mov_ext = ('mkv', 'avi', 'ogg', 'mpg', 'wmv', 'mp4', 'mpeg')
- img_ext = ('jpg','jpeg','png','gif','bmp','tga','tif','tiff','ilbm','iff','pcx')
-
# count files in directory tree
count = 0
self.statusmsg = "Calculating number of files in directory tree..."
@@ -409,6 +597,7 @@ class MainModel(ModelMT):
step = 1.0/count
else:
step = 1.0
+
self.count = 0
# guess filesystem encoding
@@ -424,7 +613,7 @@ class MainModel(ModelMT):
_size = size
- myit = self.discs_tree.append(discs_tree_iter,None)
+ myit = self.discs_tree.append(discs_tree_iter, None)
if parent_id == 1:
self.fresh_disk_iter = myit
@@ -453,7 +642,7 @@ class MainModel(ModelMT):
self.discs_tree.set_value(myit,3,parent_id)
try:
- root,dirs,files = os.walk(path).next()
+ root, dirs, files = os.walk(path).next()
except:
if __debug__:
print "m_main.py: cannot access ", path
@@ -501,42 +690,15 @@ class MainModel(ModelMT):
if self.abort:
break
self.count = self.count + 1
+ current_path = os.path.join(root,i)
try:
- st = os.stat(os.path.join(root,i))
+ st = os.stat(current_path)
st_mtime = st.st_mtime
st_size = st.st_size
except OSError:
st_mtime = 0
st_size = 0
- ### TODO: scan files
- if self.config.confd['retrive']:
- pass
- #if i.split('.').[-1].lower() in mov_ext:
- # # video only
- # info = filetypeHelper.guess_video(os.path.join(root,i))
-
-
- if i.split('.')[-1].lower() in img_ext:
- exif_info = EXIF.process_file(open(os.path.join(path,i),
- 'rb'))
- if exif_info.has_key('JPEGThumbnail'):
- print "%s got thumbnail" % i
- else:
- print "%s has not got thumbnail" % i
-
- # pass
- ### end of scan
-
- ### progress/status
- # if wobj.sbid != 0:
- # wobj.status.remove(wobj.sbSearchCId, wobj.sbid)
- if self.count % 32 == 0:
- self.statusmsg = "Scannig: %s" % (os.path.join(root,i))
- self.progress = step * self.count
- # # PyGTK FAQ entry 23.20
- # while gtk.events_pending(): gtk.main_iteration()
-
_size = _size + st_size
j = i
if self.fsenc:
@@ -548,7 +710,36 @@ class MainModel(ModelMT):
"""
db_cursor.execute(sql, (currentid, j, os.path.join(path,i),
st_mtime, st_size, self.FIL))
-
+
+ if self.count % 32 == 0:
+ update = True
+ else:
+ update = False
+
+ ###########################
+ # fetch details about files
+ if self.config.confd['retrive']:
+ update = True
+ sql = """select seq FROM sqlite_sequence WHERE name='files'"""
+ db_cursor.execute(sql)
+ fileid=db_cursor.fetchone()[0]
+ if i.split('.')[-1].lower() in self.config.confd['img_ext']:
+ sql = """UPDATE files set filetype = ? where id = ?"""
+ db_cursor.execute(sql, (self.F_IMG, fileid))
+ if self.config.confd['thumbs']:
+ path, exif, ret_code = Thumbnail(current_path, base=self.internal_dirname).save(fileid)
+ if ret_code != -1:
+ sql = """insert into thumbnails(file_id, filename) values (?, ?)"""
+ db_cursor.execute(sql, (fileid, path.split(self.internal_dirname)[1][1:]))
+
+ #if i.split('.').[-1].lower() in mov_ext:
+ # # video only
+ # info = filetypeHelper.guess_video(os.path.join(root,i))
+ ### end of scan
+ if update:
+ self.statusmsg = "Scannig: %s" % current_path
+ self.progress = step * self.count
+
sql = """update files set size=? where id=?"""
db_cursor.execute(sql,(_size, currentid))
if self.abort:
@@ -628,8 +819,7 @@ class MainModel(ModelMT):
# launch scanning.
get_children()
if __debug__:
- print "m_main.py: __fetch_db_into_treestore() tree generation time: ",
- (datetime.now() - start_date)
+ print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.now() - start_date)
db_connection.close()
return
@@ -697,8 +887,7 @@ class MainModel(ModelMT):
# launch scanning.
get_children()
if __debug__:
- print "m_main.py: __fetch_db_into_treestore() tree generation time: ",
- (datetime.now() - start_date)
+ print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.now() - start_date)
db_connection.close()
return