1
0
mirror of https://github.com/gryf/pygtktalog.git synced 2025-12-17 11:30:19 +01:00

* Added details MVC files.

* Change of katalog file organization.
 * Small bugfixes.
 * Code clean up.
 * Added README file.
This commit is contained in:
2008-03-24 09:15:05 +00:00
parent 2a1cb3c1ba
commit bc4f329d15
14 changed files with 3095 additions and 2223 deletions

77
README Normal file
View File

@@ -0,0 +1,77 @@
pyGTKtalog 0.8
==============
pyGTKtalog Linux/FreeBSD program for indexing CD/DVD or directories on
filesystem. It is similar to gtktalog <http://www.nongnu.org/gtktalog/> or
gwhere <http://www.gwhere.org/home.php3>. There is no coincidence in name of
application, because it's ment to be replacement (in some way) for gtktalog,
which seems to be dead project for years.
FEATURES
========
- scanning for files in selected media
- generating thumbnails
- tagging files <http://en.wikipedia.org/wiki/Tag_%28metadata%29>
REQUIREMENTS
============
pyGTKtalog is written in python with following dependencies:
- pygtk <http://www.pygtk.org>
- pysqlite2 <http://pysqlite.org/> (unnecessary, if python 2.5 is used)
# - mx.DateTime <http://www.egenix.com>
Optional modules:
- PIL <http://www.pythonware.com/products/pil/index.htm> for image manipulation
- pyExcelerator <http://sourceforge.net/projects/pyexcelerator> for export to
excel capability
Additional pyGTKtalog uses EXIF module by Gene Cash which is included in
sources.
pyGTKtalog extensivly uses external programs in unix spirit, however there is
small possibility of using it Windows (probably with liitations) and quite big
possiblity to run it on other sofisticated unix-like systems (i.e.
BeOS/ZETA/Haiku, QNX or MacOSX).
INSTALATION
===========
All you have to do is:
- put pyGTKtalog directory into your destination of choice (/usr/local/share,
/opt or ~/ is typical choice)
- modify pyGTKtalog/pyGTKtalog line 4 to match right directory
- copy/link pyGTKtalog/pyGTKtalog shell script to /usr/bin, /usr/local/bin or in
other place, where PATH variable is pointing or you feel like.
Then, just run pyGTKtalog script.
TODO
====
- searching database
- taggin files
- files properties
- adding images
- generating/saving thumbnails
- command line query support (text output)
- internationalization support
- statistics
- moving hardcoded files extensions into config
NOTES
=====
Catalog file is tared and optionaly compressed sqlite database and directory
with thumbnails. If there are more images, the size of catalog file will grow.
So be carefull with adding big images in your catalog file!
BUGS
====
All bugs please report to Roman 'gryf' Dobosz <roman.dobosz@gmail.com>

View File

@@ -1,7 +1,11 @@
#!/bin/sh
# Simple wraper to launch python application from desired place.
# adjust to your needs:
data_dir="/home/gryf/Devel/Python/pyGTKtalog"
python_intrpreter="/usr/bin/python"
# don't change anything below.
current_dir="`pwd`"
cd ~/Devel/Python/pyGTKtalog
#export PYTHONPATH="$PYTHONPATH:/home/gryf/Devel/Python/pyGTKtalog/mvc"
#exec python -OO pygtktalog.py $@
exec python pygtktalog.py "$current_dir" "$@"
cd $data_dir
#exec $python_intrpreter -OO pygtktalog.py "$current_dir" "$@"
exec $python_intrpreter pygtktalog.py "$current_dir" "$@"

View File

@@ -68,11 +68,11 @@ def check_requirements():
except:
print "pyGTKtalog uses SQLite DB.\nYou'll need to get it and the python bindings as well.\nhttp://www.sqlite.org\nhttp://initd.org/tracker/pysqlite"
sys.exit(1)
try:
import mx.DateTime
except:
print "pyGTKtalog uses Egenix mx.DateTime.\nYou can instal it from your distribution repositry,\nor get it at: http://www.egenix.com"
sys.exit(1)
#try:
# import mx.DateTime
#except:
# print "pyGTKtalog uses Egenix mx.DateTime.\nYou can instal it from your distribution repositry,\nor get it at: http://www.egenix.com"
# sys.exit(1)
if conf.confd['exportxls']:
try:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,7 @@ class ConfigController(Controller):
self.view['ch_thumb'].set_active(self.model.confd['pil'])
self.view['ch_exif'].set_active(self.model.confd['exif'])
self.view['ch_gthumb'].set_active(self.model.confd['gthumb'])
self.view['ch_compress'].set_active(self.model.confd['compress'])
# initialize tree view
self.__setup_category_tree()
@@ -92,18 +93,22 @@ class ConfigController(Controller):
self.model.confd['pil'] = self.view['ch_thumb'].get_active()
self.model.confd['exif'] = self.view['ch_exif'].get_active()
self.model.confd['gthumb'] = self.view['ch_gthumb'].get_active()
self.model.confd['compress'] = self.view['ch_compress'].get_active()
self.model.save()
self.view['config'].destroy()
return
def on_button_ejt_clicked(self,button):
def on_button_ejt_clicked(self, button):
self.__show_filechooser()
return
def on_button_mnt_clicked(self,button):
def on_button_mnt_clicked(self, button):
self.__show_dirchooser()
return
def on_ch_retrive_toggled(self, widget):
return
############################
# private controller methods
def __setup_category_tree(self):
@@ -166,157 +171,4 @@ class ConfigController(Controller):
dialog.destroy()
pass # end of class
'''
import sys
import os
import pygtk
import gtk
import gtk.glade
import gobject
from config import Config
import dialogs
class Preferences:
def __init__(self):
self.category_dict = {'Disk options':'disk_group','General':'general_group','Scan options':'scan_group'}
self.category_order = ['General','Disk options','Scan options']
self.conf = Config()
self.conf.load()
self.gladefile = "glade/prefs.glade"
self.glade = gtk.glade.XML(self.gladefile,"prefs")
dic = {
"on_button_ejt_clicked" :self.show_filechooser,
"on_button_mnt_clicked" :self.show_dirchooser,
"on_category_tree_cursor_changed" :self.activate_pan,
}
self.glade.signal_autoconnect(dic)
self.pref = self.glade.get_widget("prefs")
self.pref.set_title("Preferences - pyGTKtalog")
self.desc = self.glade.get_widget("desc")
self.cd = self.glade.get_widget("mnt_entry")
self.cd.set_text(self.conf.confd['cd'])
self.eject = self.glade.get_widget("ejt_entry")
self.eject.set_text(self.conf.confd['ejectapp'])
self.ch_win = self.glade.get_widget("ch_win")
self.ch_win.set_active(self.conf.confd['savewin'])
self.ch_pan = self.glade.get_widget("ch_pan")
self.ch_pan.set_active(self.conf.confd['savepan'])
self.ch_eject = self.glade.get_widget("ch_eject")
self.ch_eject.set_active(self.conf.confd['eject'])
self.ch_xls = self.glade.get_widget("ch_xls")
self.ch_xls.set_active(self.conf.confd['exportxls'])
self.ch_quit = self.glade.get_widget("ch_quit")
self.ch_quit.set_active(self.conf.confd['confirmquit'])
self.ch_wrnmount = self.glade.get_widget("ch_wrnmount")
self.ch_wrnmount.set_active(self.conf.confd['mntwarn'])
self.ch_warnnew = self.glade.get_widget("ch_warnnew")
self.ch_warnnew.set_active(self.conf.confd['confirmabandon'])
self.ch_thumb = self.glade.get_widget("ch_thumb")
self.ch_thumb.set_active(self.conf.confd['pil'])
self.ch_exif = self.glade.get_widget("ch_exif")
self.ch_exif.set_active(self.conf.confd['exif'])
self.ch_gthumb = self.glade.get_widget("ch_gthumb")
self.ch_gthumb.set_active(self.conf.confd['gthumb'])
self.tree = self.glade.get_widget("category_tree")
self.model = gtk.ListStore(gobject.TYPE_STRING)
self.model.clear()
self.tree.set_model(self.model)
self.tree.set_headers_visible(False)
self.tree.show()
for i in self.category_order:
myiter = self.model.insert_before(None,None)
self.model.set_value(myiter,0,i)
renderer=gtk.CellRendererText()
column=gtk.TreeViewColumn("Name",renderer, text=0)
column.set_resizable(True)
self.tree.append_column(column)
if self.pref.run() == gtk.RESPONSE_OK:
self.conf.confd['cd'] = self.cd.get_text()
self.conf.confd['ejectapp'] = self.eject.get_text()
self.conf.confd['savewin'] = self.ch_win.get_active()
self.conf.confd['savepan'] = self.ch_pan.get_active()
self.conf.confd['eject'] = self.ch_eject.get_active()
self.conf.confd['pil'] = self.ch_thumb.get_active()
self.conf.confd['exif'] = self.ch_exif.get_active()
self.conf.confd['gthumb'] = self.ch_gthumb.get_active()
self.conf.confd['exportxls'] = self.ch_xls.get_active()
self.conf.confd['confirmquit'] = self.ch_quit.get_active()
self.conf.confd['mntwarn'] = self.ch_wrnmount.get_active()
self.conf.confd['confirmabandon'] = self.ch_warnnew.get_active()
self.conf.save()
self.pref.destroy()
def show_filechooser(self,widget):
"""dialog for choose eject"""
dialog = gtk.FileChooserDialog(
title="Choose eject program",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN,
gtk.RESPONSE_OK
)
)
dialog.set_default_response(gtk.RESPONSE_OK)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.eject.set_text(dialog.get_filename())
dialog.destroy()
def show_dirchooser(self,widget):
"""dialog for point the mountpoint"""
dialog = gtk.FileChooserDialog(
title="Choose mount point",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN,
gtk.RESPONSE_OK
)
)
dialog.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
dialog.set_filename(self.conf.confd['cd'])
dialog.set_default_response(gtk.RESPONSE_OK)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.cd.set_text(dialog.get_filename())
dialog.destroy()
def activate_pan(self,treeview):
model = treeview.get_model()
selected = model.get_value(model.get_iter(treeview.get_cursor()[0]),0)
iterator = treeview.get_model().get_iter_first();
while iterator != None:
if model.get_value(iterator,0) == selected:
self.glade.get_widget(self.category_dict[model.get_value(iterator,0)]).show()
self.desc.set_markup("<b>%s</b>" % selected)
else:
self.glade.get_widget(self.category_dict[model.get_value(iterator,0)]).hide()
iterator = treeview.get_model().iter_next(iterator);
if __name__ == "__main__":
try:
app=Preferences()
gtk.main()
except KeyboardInterrupt:
gtk.main_quit
'''

37
src/ctrls/c_details.py Normal file
View File

@@ -0,0 +1,37 @@
# This Python file uses the following encoding: utf-8
#
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
#
# Copyright (C) 2007 by Roman 'gryf' Dobosz
#
# This file is part of pyGTKtalog.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# -------------------------------------------------------------------------
import os.path
from utils import deviceHelper
from gtkmvc import Controller
class DetailsController(Controller):
"""Controller for details view"""
def __init__(self, model):
"""Initialize controller"""
Controller.__init__(self, model)
return
def register_view(self, view):
Controller.register_view(self, view)

View File

@@ -22,7 +22,7 @@
# -------------------------------------------------------------------------
__version__ = "0.7"
__version__ = "0.8"
licence = \
"""
GPL v2
@@ -34,6 +34,7 @@ from utils import deviceHelper
from gtkmvc import Controller
from c_config import ConfigController
from c_details import DetailsController
from views.v_config import ConfigView
from models.m_config import ConfigModel
@@ -47,12 +48,12 @@ class MainController(Controller):
"""Controller for main application window"""
scan_cd = False
widgets = (
"discs","files","details",
"discs","files",
'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1',
'tb_save','tb_addcd','tb_find',
)
widgets_all = (
"discs","files","details",
"discs","files",
'file1','edit1','add_cd','add_directory1','help1',
'tb_save','tb_addcd','tb_find','tb_new','tb_open','tb_quit',
)
@@ -61,6 +62,7 @@ class MainController(Controller):
def __init__(self, model):
"""Initialize controller"""
Controller.__init__(self, model)
self.details = DetailsController(model.details)
return
def register_view(self, view):
@@ -74,21 +76,19 @@ class MainController(Controller):
for widget in self.widgets_cancel:
self.view[widget].set_sensitive(False)
# ukryj przycisk "debug", jeśli nie debugujemy.
# hide "debug" button, if production (i.e. python OT running with -OO option)
if __debug__:
self.view['debugbtn'].show()
else:
self.view['debugbtn'].hide()
# ustaw domyślne właściwości dla poszczególnych widżetów
self.view['main'].set_title('pyGTKtalog');
# self.view['main'].set_icon_list(gtk.gdk.pixbuf_new_from_file("pixmaps/mainicon.png"))
#self.view['detailplace'].set_sensitive(False)
self.view['details'].hide()
self.view['exifTab'].hide()
self.view['animeTab'].hide()
#self.view['exifTab'].hide()
#self.view['animeTab'].hide()
# załaduj konfigurację/domyślne ustawienia i przypisz je właściwościom
# load configuration/defaults and set it to properties
self.view['toolbar1'].set_active(self.model.config.confd['showtoolbar'])
if self.model.config.confd['showtoolbar']:
self.view['maintoolbar'].show()
@@ -115,10 +115,13 @@ class MainController(Controller):
if self.model.filename != None:
self.__activateUI(self.model.filename)
# register detail subview
#self.view.create_sub_view(self.details)
# generate recent menu
self.__generate_recent_menu()
# Show main window
self.view['main'].show();
return
#########################################################################
@@ -194,12 +197,12 @@ class MainController(Controller):
def on_discs_cursor_changed(self, widget):
"""Show files on right treeview, after clicking the left disc treeview."""
model = self.view['discs'].get_model()
selected_item = self.model.discsTree.get_value(self.model.discsTree.get_iter(self.view['discs'].get_cursor()[0]),0)
selected_item = self.model.discs_tree.get_value(self.model.discs_tree.get_iter(self.view['discs'].get_cursor()[0]),0)
if __debug__:
print "c_main.py, on_discs_cursor_changed()",selected_item
self.model.get_root_entries(selected_item)
self.__getItemInfo(selected_item)
self.__get_item_info(selected_item)
return
def on_discs_row_activated(self, treeview, path, treecolumn):
@@ -229,7 +232,7 @@ class MainController(Controller):
treeview.get_selection().unselect_all()
treeview.get_selection().select_path(path)
if self.model.discsTree.get_value(self.model.discsTree.get_iter(path),3) == 1:
if self.model.discs_tree.get_value(self.model.discs_tree.get_iter(path),3) == 1:
# if ancestor is 'root', then activate "update" menu item
self.view['update1'].set_sensitive(True)
else:
@@ -239,7 +242,7 @@ class MainController(Controller):
# elif event.button == 1: # Left click
# """Show files on right treeview, after clicking the left disc treeview."""
# model = self.view['discs'].get_model()
# selected_item = self.model.discsTree.get_value(self.model.discsTree.get_iter(path),0)
# selected_item = self.model.discs_tree.get_value(self.model.discs_tree.get_iter(path),0)
# if __debug__:
# print "c_main.py, on_discs_cursor_changed()",selected_item
# self.model.get_root_entries(selected_item)
@@ -266,16 +269,16 @@ class MainController(Controller):
itera = model.get_iter(paths[0])
if model.get_value(itera,4) == 1:
#directory, do nothin', just turn off view
self.view['details'].hide()
'''self.view['details'].hide()
buf = self.view['details'].get_buffer()
buf.set_text('')
self.view['details'].set_buffer(buf)
self.view['details'].set_buffer(buf)'''
if __debug__:
print "c_main.py: on_files_cursor_changed() directory selected"
else:
#file, show what you got.
selected_item = self.model.filesList.get_value(model.get_iter(treeview.get_cursor()[0]),0)
self.__getItemInfo(selected_item)
selected_item = self.model.files_list.get_value(model.get_iter(treeview.get_cursor()[0]),0)
self.__get_item_info(selected_item)
if __debug__:
print "c_main.py: on_files_cursor_changed() some other thing selected"
except:
@@ -285,11 +288,11 @@ class MainController(Controller):
def on_files_row_activated(self, files_obj, row, column):
"""On directory doubleclick in files listview dive into desired branch."""
# TODO: można by też podczepić klawisz backspace do przechodzenia poziom wyżej.
f_iter = self.model.filesList.get_iter(row)
current_id = self.model.filesList.get_value(f_iter,0)
# TODO: map backspace key for moving to upper level of directiories
f_iter = self.model.files_list.get_iter(row)
current_id = self.model.files_list.get_value(f_iter,0)
if self.model.filesList.get_value(f_iter,4) == 1:
if self.model.files_list.get_value(f_iter,4) == 1:
# ONLY directories. files are omitted.
self.model.get_root_entries(current_id)
@@ -298,12 +301,12 @@ class MainController(Controller):
if not self.view['discs'].row_expanded(d_path):
self.view['discs'].expand_row(d_path,False)
new_iter = self.model.discsTree.iter_children(self.model.discsTree.get_iter(d_path))
new_iter = self.model.discs_tree.iter_children(self.model.discs_tree.get_iter(d_path))
if new_iter:
while new_iter:
if self.model.discsTree.get_value(new_iter,0) == current_id:
self.view['discs'].set_cursor(self.model.discsTree.get_path(new_iter))
new_iter = self.model.discsTree.iter_next(new_iter)
if self.model.discs_tree.get_value(new_iter,0) == current_id:
self.view['discs'].set_cursor(self.model.discs_tree.get_path(new_iter))
new_iter = self.model.discs_tree.iter_next(new_iter)
return
def on_cancel1_activate(self, widget):
@@ -313,7 +316,7 @@ class MainController(Controller):
self.__abort()
def on_tb_find_clicked(self, widget):
# TODO: zaimplementować wyszukiwarkę
# TODO: implement searcher
return
def recent_item_response(self, path):
@@ -329,50 +332,47 @@ class MainController(Controller):
if self.model.get_source(path) == self.model.CD:
if self.__addCD(label):
self.model.delete(self.model.discsTree.get_iter(path[0],0))
self.model.delete(self.model.discs_tree.get_iter(path[0],0))
pass
elif self.model.get_source(path) == self.model.DR:
if self.__addDirectory(filepath, label):
self.model.delete(self.model.discsTree.get_iter(path[0]))
self.model.delete(self.model.discs_tree.get_iter(path[0]))
pass
return
def on_delete2_activate(self, menu_item):
model = self.view['discs'].get_model()
try:
selected_iter = self.model.discsTree.get_iter(self.view['discs'].get_cursor()[0])
selected_iter = self.model.discs_tree.get_iter(self.view['discs'].get_cursor()[0])
except:
return
if self.model.config.confd['delwarn']:
name = self.model.discsTree.get_value(selected_iter,1)
name = self.model.discs_tree.get_value(selected_iter,1)
obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name, 'Object will be permanently removed.')
if not obj.run():
return
self.model.delete(selected_iter)
self.model.unsaved_project = True
if self.model.filename != None:
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
self.__setTitle(filepath=self.model.filename, modified=True)
return
def on_debugbtn_clicked(self,widget):
"""Debug. To remove in stable version including button in GUI"""
"""Debug. To remove in stable version, including button in GUI"""
if __debug__:
print "\nc_main.py: on_debugbtn_clicked()"
print "------"
print "unsaved_project = %s" % self.model.unsaved_project
print "filename = %s" % self.model.filename
print "internal_filename = %s" % self.model.internal_filename
print "internal_filename = %s" % self.model.internal_dirname
print "db_connection = %s" % self.model.db_connection
print "abort = %s" % self.model.abort
print "self.model.config.recent = %s" % self.model.config.recent
it = self.model.discsTree.get_iter_first()
myit = self.model.discsTree.insert_before(None,None)
self.model.discsTree.set_value(myit,0,0)
self.model.discsTree.set_value(myit,1,"nazwa")
self.model.discsTree.set_value(myit,3,3)
self.model.discsTree.set_value(myit,2,gtk.STOCK_INFO)
it = self.model.tags_list.get_iter_first()
myit = self.model.tags_list.insert_before(None,None)
self.model.tags_list.set_value(myit,0,0)
self.model.tags_list.set_value(myit,1,"nazwa")
self.model.tags_list.set_value(myit,2,231233)
print "source: %s" % self.model.source
#####################
# observed properetis
@@ -423,7 +423,7 @@ class MainController(Controller):
if path:
if not self.model.open(path):
Dialogs.Err("Error opening file - pyGTKtalog","Cannot open file %s." % self.opened_catalog)
Dialogs.Err("Error opening file - pyGTKtalog","Cannot open file %s." % path)
else:
self.__generate_recent_menu()
self.__activateUI(path)
@@ -431,10 +431,9 @@ class MainController(Controller):
def __save(self):
"""Save catalog to file"""
#{{{
if self.model.filename:
self.model.save()
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
self.__setTitle(filepath=self.model.filename)
else:
self.__save_as()
pass
@@ -443,7 +442,7 @@ class MainController(Controller):
"""Save database to file under different filename."""
path = Dialogs.ChooseDBFilename().show_dialog()
if path:
self.view['main'].set_title("%s - pyGTKtalog" % path)
self.__setTitle(filepath=path)
self.model.save(path)
self.model.config.add_recent(path)
pass
@@ -461,11 +460,8 @@ class MainController(Controller):
self.view[widget].set_sensitive(False)
self.model.source = self.model.CD
self.model.scan(self.model.config.confd['cd'],label)
self.unsaved_project = True
if self.model.filename != None:
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
self.model.unsaved_project = True
self.__setTitle(filepath=self.model.filename, modified=True)
return True
else:
Dialogs.Wrn("Error mounting device - pyGTKtalog",
@@ -485,11 +481,8 @@ class MainController(Controller):
self.scan_cd = False
self.model.source = self.model.DR
self.model.scan(path, label)
self.unsaved_project = True
if self.model.filename != None:
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
self.model.unsaved_project = True
self.__setTitle(filepath=self.model.filename, modified=True)
return True
def __doQuit(self):
@@ -516,10 +509,10 @@ class MainController(Controller):
self.model.new()
# clear "details" buffer
txt = ""
'''txt = ""
buf = self.view['details'].get_buffer()
buf.set_text(txt)
self.view['details'].set_buffer(buf)
self.view['details'].set_buffer(buf)'''
self.__activateUI()
@@ -527,7 +520,27 @@ class MainController(Controller):
def __setup_disc_treeview(self):
"""Setup TreeView discs widget as tree."""
self.view['discs'].set_model(self.model.discsTree)
self.view['discs'].set_model(self.model.discs_tree)
c = gtk.TreeViewColumn('Filename')
# one row contains image and text
cellpb = gtk.CellRendererPixbuf()
cell = gtk.CellRendererText()
c.pack_start(cellpb, False)
c.pack_start(cell, True)
c.set_attributes(cellpb, stock_id=2)
c.set_attributes(cell, text=1)
self.view['discs'].append_column(c)
# registration of treeview signals:
return
def __setup_tags_treeview(self):
"""Setup TreeView discs widget as tree."""
self.view['tags'].set_model(self.model.tagsTree)
c = gtk.TreeViewColumn('Filename')
@@ -547,7 +560,7 @@ class MainController(Controller):
def __setup_files_treeview(self):
"""Setup TreeView files widget, as columned list."""
self.view['files'].set_model(self.model.filesList)
self.view['files'].set_model(self.model.files_list)
self.view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE)
@@ -588,10 +601,10 @@ class MainController(Controller):
self.model.abort = True
return
def __activateUI(self, name='untitled'):
def __activateUI(self, name=False):
"""Make UI active, and set title"""
self.model.unsaved_project = False
self.view['main'].set_title("%s - pyGTKtalog" % name)
self.__setTitle(filepath=name)
for widget in self.widgets:
try:
self.view[widget].set_sensitive(True)
@@ -602,6 +615,19 @@ class MainController(Controller):
gtk.main_iteration()
return
def __setTitle(self, filepath=None, modified=False):
"""Set main window title"""
if modified:
mod = " *"
else:
mod = ""
if filepath:
self.view['main'].set_title("%s - pyGTKtalog%s" % (os.path.basename(filepath), mod))
else:
self.view['main'].set_title("untitled - pyGTKtalog%s" % mod)
return
def __storeSettings(self):
"""Store window size and pane position in config file (using config object from model)"""
if self.model.config.confd['savewin']:
@@ -620,8 +646,6 @@ class MainController(Controller):
self.recent_menu = gtk.Menu()
for i in self.model.config.recent:
name = os.path.basename(i)
if name.endswith(".pgt"):
name = name[:-4]
item = gtk.MenuItem("%s" % name)
item.connect_object("activate", self.recent_item_response, i)
self.recent_menu.append(item)
@@ -629,11 +653,11 @@ class MainController(Controller):
self.view['recent_files1'].set_submenu(self.recent_menu)
return
def __getItemInfo(self, item):
self.view['details'].show()
def __get_item_info(self, item):
'''self.view['details'].show()
txt = self.model.get_file_info(item)
buf = self.view['details'].get_buffer()
buf.set_text(txt)
self.view['details'].set_buffer(buf)
self.view['details'].set_buffer(buf)'''
return
pass # end of class

View File

@@ -76,6 +76,7 @@ class ConfigModel(Model):
'showtoolbar':True,
'showstatusbar':True,
'delwarn':True,
'compress':True,
}
dictconf = {
@@ -96,6 +97,7 @@ class ConfigModel(Model):
'confirm abandon current catalog':'confirmabandon',
'show toolbar':'showtoolbar',
'show statusbar and progress bar':'showstatusbar',
'compress collection':'compress',
}
dbool = (
@@ -112,6 +114,8 @@ class ConfigModel(Model):
'confirmabandon',
'showtoolbar',
'showstatusbar',
'delwarn',
'compress',
)
recent = []

34
src/models/m_details.py Normal file
View File

@@ -0,0 +1,34 @@
# This Python file uses the following encoding: utf-8
#
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
#
# Copyright (C) 2007 by Roman 'gryf' Dobosz
#
# This file is part of pyGTKtalog.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# -------------------------------------------------------------------------
from gtkmvc import Model
class DetailsModel(Model):
def __init__(self):
Model.__init__(self)
return
def __str__(self):
"""show prefs in string way"""
return "fubar"

View File

@@ -22,76 +22,107 @@
# -------------------------------------------------------------------------
import os
import sys
import base64
import shutil
import tarfile
import gtk
import gobject
from gtkmvc.model_mt import ModelMT
from pysqlite2 import dbapi2 as sqlite
from datetime import datetime
#import mx.DateTime
try:
import threading as _threading
except ImportError:
if __debug__:
print "m_main.py: import exception: _threading"
import dummy_threading as _threading
try:
import Image, ImageEnhance
except:
if __debug__:
print "m_main.py: import exception: Image|ImageEnhance"
pass
from pysqlite2 import dbapi2 as sqlite
import datetime
import mx.DateTime
import os
import sys
import mimetypes
import bz2
import gtk
import gobject
from utils import EXIF
from m_config import ConfigModel
from m_details import DetailsModel
class Thumbnail(object):
def __init__(self, *args):
self.x = None
self.y = None
class Picture(object):
def __init__(self, *args):
self.x = None
self.y = None
class MainModel(ModelMT):
"""Create, load, save, manipulate db file which is container for our data"""
"""Create, load, save, manipulate db file which is container for data"""
__properties__ = {'busy': False, 'statusmsg': '', 'progress': 0}
config = ConfigModel()
unsaved_project = False
filename = None
internal_filename = None
db_connection = None
db_cursor = None
abort = False
# constants instead of dictionary tables
# type of files
LAB = 0 # root of the tree - label/collection name
DIR = 1 # directory
FIL = 2 # file
LIN = 3 # symbolic link
# filetype kind of
F_UNK = 0 # unknown - default
F_IMG = 1 # images - jpg, gif, tiff itd
F_MOV = 2 # movies and clips - mpg, ogm, mkv, avi, asf, wmv itd
F_MUS = 4 # music - flac, mp3, mpc, ogg itd
F_APP = 5 # applications
F_DOC = 6 # all kind of documents txt/pdf/doc/odf itd
# Drzewo katalogów: id, nazwa, ikonka, typ
discsTree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING, str, gobject.TYPE_INT)
# Lista plików wskazanego katalogu: child_id (?), filename, size, date, typ, ikonka
filesList = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_UINT64, gobject.TYPE_STRING, gobject.TYPE_INT, gobject.TYPE_STRING, str)
LAB = 0 # label/nazwa kolekcji --- najwyższy poziom drzewa przypiętego do korzenia
DIR = 1 # katalog - podlega innemu katalogu lub lejbelu
FIL = 2 # plik - podleka katalogu lub lejbelu
CD = 1 # podczas skanowania, wstawiane jest źródło na płytę CD/DVD
DR = 2 # podczas skanowania, wstawiane jest źródło na katalog w fs
# default source is CD/DVD
source = CD
CD = 1 # sorce: cd/dvd
DR = 2 # source: filesystem
def __init__(self):
ModelMT.__init__(self)
self.config = ConfigModel()
self.unsaved_project = False
self.filename = None # collection saved/opened filename
self.internal_dirname = None
self.db_connection = None
self.db_cursor = None
self.abort = False
self.source = self.CD
self.config.load()
self.details = DetailsModel()
# Directory tree: id, nazwa, ikonka, typ
self.discs_tree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING,
str, gobject.TYPE_INT)
# File list of selected directory: child_id(?), filename, size,
# date, type, icon
self.files_list = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING,
gobject.TYPE_UINT64,
gobject.TYPE_STRING, gobject.TYPE_INT,
gobject.TYPE_STRING, str)
# Tag list: id, name, count
self.tags_list = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING,
gobject.TYPE_UINT64, str)
return
def cleanup(self):
self.__close_db_connection()
if self.internal_filename != None:
if self.internal_dirname != None:
try:
os.unlink(self.internal_filename)
shutil.rmtree(self.internal_dirname)
except:
if __debug__:
print "m_main.py: cleanup()", self.internal_filename
pass
try:
os.unlink(self.internal_filename + '-journal')
except:
if __debug__:
print "m_main.py: cleanup()", self.internal_filename+'-journal'
print "m_main.py: cleanup()", self.internal_dirname
pass
return
@@ -100,19 +131,11 @@ class MainModel(ModelMT):
def new(self):
self.unsaved_project = False
self.__create_internal_filename()
self.__create_internal_dirname()
self.__connect_to_db()
self.__create_database()
try:
self.discsTree.clear()
except:
pass
try:
self.filesList.clear()
except:
pass
self.__clear_trees()
return
@@ -125,42 +148,23 @@ class MainModel(ModelMT):
def open(self, filename=None):
"""try to open db file"""
self.unsaved_project = False
self.__create_internal_filename()
self.__create_internal_dirname()
self.filename = filename
try:
source = bz2.BZ2File(filename, 'rb')
tar = tarfile.open(filename, "r:gz")
except:
print "%s: file cannot be read!" % self.filename
self.filename = None
self.internal_filename = None
return
destination = open(self.internal_filename, 'wb')
while True:
try:
data = source.read(1024000)
tar = tarfile.open(filename, "r")
except:
# smth went wrong
print "%s: Wrong database format!" % self.filename
if __debug__:
print "m_main.py: open() something goes bad"
print "%s: file cannot be read!" % filename
self.filename = None
self.internal_filename = None
try:
self.discsTree.clear()
except:
pass
self.internal_dirname = None
return
try:
self.filesList.clear()
except:
pass
return False
if not data: break
destination.write(data)
destination.close()
source.close()
os.chdir(self.internal_dirname)
tar.extractall()
tar.close()
self.__connect_to_db()
self.__fetch_db_into_treestore()
@@ -168,7 +172,7 @@ class MainModel(ModelMT):
return True
def scan(self,path,label):
def scan(self, path, label):
"""scan files in separated thread"""
# flush buffer to release db lock.
@@ -183,9 +187,10 @@ class MainModel(ModelMT):
self.thread.start()
return
def get_root_entries(self,id=None):
def get_root_entries(self, id=None):
"""Get all children down from sepcified root"""
try:
self.filesList.clear()
self.files_list.clear()
except:
pass
# parent for virtual '..' dir
@@ -194,50 +199,61 @@ class MainModel(ModelMT):
#self.filemodel.set_value(myiter,0,self.cur.fetchone()[0])
#self.filemodel.set_value(myiter,1,'..')
#if __debug__:
# print datetime.datetime.fromtimestamp(ch[3])
# print datetime.fromtimestamp(ch[3])
# directories first
self.db_cursor.execute("SELECT id, filename, size, date FROM files WHERE parent_id=? AND type=1 ORDER BY filename",(id,))
self.db_cursor.execute("SELECT id, filename, size, date FROM files \
WHERE parent_id=? AND type=1 \
ORDER BY filename", (id,))
data = self.db_cursor.fetchall()
for ch in data:
myiter = self.filesList.insert_before(None,None)
self.filesList.set_value(myiter,0,ch[0])
self.filesList.set_value(myiter,1,ch[1])
self.filesList.set_value(myiter,2,ch[2])
self.filesList.set_value(myiter,3,datetime.datetime.fromtimestamp(ch[3]))
self.filesList.set_value(myiter,4,1)
self.filesList.set_value(myiter,5,'direktorja')
self.filesList.set_value(myiter,6,gtk.STOCK_DIRECTORY)
myiter = self.files_list.insert_before(None, None)
self.files_list.set_value(myiter, 0, ch[0])
self.files_list.set_value(myiter, 1, ch[1])
self.files_list.set_value(myiter, 2, ch[2])
self.files_list.set_value(myiter, 3,
datetime.fromtimestamp(ch[3]))
self.files_list.set_value(myiter, 4, 1)
self.files_list.set_value(myiter, 5, 'direktorja')
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 id, filename, size, date, type \
FROM files WHERE parent_id=? AND type!=1 \
ORDER BY filename", (id,))
data = self.db_cursor.fetchall()
for ch in data:
myiter = self.filesList.insert_before(None,None)
self.filesList.set_value(myiter,0,ch[0])
self.filesList.set_value(myiter,1,ch[1])
self.filesList.set_value(myiter,2,ch[2])
self.filesList.set_value(myiter,3,datetime.datetime.fromtimestamp(ch[3]))
self.filesList.set_value(myiter,4,ch[4])
self.filesList.set_value(myiter,5,'kategoria srategoria')
self.filesList.set_value(myiter,6,gtk.STOCK_FILE)
#print datetime.datetime.fromtimestamp(ch[3])
myiter = self.files_list.insert_before(None, None)
self.files_list.set_value(myiter, 0, ch[0])
self.files_list.set_value(myiter, 1, ch[1])
self.files_list.set_value(myiter, 2, ch[2])
self.files_list.set_value(myiter, 3, datetime.fromtimestamp(ch[3]))
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)
elif ch[4] == self.LIN:
self.files_list.set_value(myiter, 6, gtk.STOCK_INDEX)
return
def get_file_info(self, id):
"""get file info from database"""
self.db_cursor.execute("SELECT filename, date, size, type FROM files WHERE id = ?",(id,))
self.db_cursor.execute("SELECT filename, date, size, type \
FROM files WHERE id = ?", (id,))
set = self.db_cursor.fetchone()
if set == None:
return ''
string = "Filename: %s\nDate: %s\nSize: %s\ntype: %s" % (set[0],datetime.datetime.fromtimestamp(set[1]),set[2],set[3])
string = "Filename: %s\nDate: %s\nSize: %s\ntype: %s" % \
(set[0], datetime.fromtimestamp(set[1]), set[2], set[3])
return string
def get_source(self, path):
"""get source of top level directory"""
bid = self.discsTree.get_value(self.discsTree.get_iter(path[0]),0)
self.db_cursor.execute("select source from files where id = ?", (bid,))
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
0)
self.db_cursor.execute("select source from files where id = ?",
(bid,))
res = self.db_cursor.fetchone()
if res == None:
return False
@@ -245,8 +261,10 @@ class MainModel(ModelMT):
def get_label_and_filepath(self, path):
"""get source of top level directory"""
bid = self.discsTree.get_value(self.discsTree.get_iter(path[0]),0)
self.db_cursor.execute("select filepath, filename from files where id = ? and parent_id = 1", (bid,))
bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
0)
self.db_cursor.execute("select filepath, filename from files \
where id = ? and parent_id = 1", (bid,))
res = self.db_cursor.fetchone()
if res == None:
return None, None
@@ -255,13 +273,38 @@ class MainModel(ModelMT):
def delete(self, branch_iter):
if not branch_iter:
return
self.__remove_branch_form_db(self.discsTree.get_value(branch_iter,0))
self.discsTree.remove(branch_iter)
self.__remove_branch_form_db(self.discs_tree.get_value(branch_iter,0))
self.discs_tree.remove(branch_iter)
return
# private class functions
def __clear_trees(self):
self.__clear_tags_tree()
self.__clear_files_tree()
self.__clear_discs_tree()
def __clear_tags_tree(self):
try:
self.tags_list.clear()
except:
pass
def __clear_discs_tree(self):
try:
self.discs_tree.clear()
except:
pass
def __clear_files_tree(self):
try:
self.files_list.clear()
except:
pass
def __connect_to_db(self):
self.db_connection = sqlite.connect("%s" % self.internal_filename, detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
self.db_connection = sqlite.connect("%s" % \
(self.internal_dirname + '/db.sqlite'),
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
self.db_cursor = self.db_connection.cursor()
return
@@ -274,22 +317,28 @@ class MainModel(ModelMT):
self.db_connection = None
return
def __create_internal_filename(self):
def __create_internal_dirname(self):
self.cleanup()
self.internal_filename = "/tmp/pygtktalog%d.db" % datetime.datetime.now().microsecond
self.internal_dirname = "/tmp/pygtktalog%d" % datetime.now().microsecond
try:
os.mkdir(self.internal_dirname)
except:
if __debug__:
print "m_main.py: __create_internal_dirname(): cannot create \
temporary directory, or directory exists"
pass
return
def __compress_and_save(self):
source = open(self.internal_filename, 'rb')
destination = bz2.BZ2File(self.filename, 'w')
if self.config.confd['compress']:
tar = tarfile.open(self.filename, "w:gz")
else:
tar = tarfile.open(self.filename, "w")
while True:
data = source.read(1024000)
if not data: break
destination.write(data)
os.chdir(self.internal_dirname)
tar.add('.')
tar.close()
destination.close()
source.close()
self.unsaved_project = False
return
@@ -303,24 +352,44 @@ class MainModel(ModelMT):
date datetime,
size integer,
type integer,
source integer);""")
self.db_cursor.execute("insert into files values(1, 1, 'root', null, 0, 0, 0, 0);")
source integer,
size_x integer,
size_y integer,
filetype integer,
note TEXT);""")
self.db_cursor.execute("""create table
tags(id INTEGER PRIMARY KEY AUTOINCREMENT,
file_id INTEGER,
tag TEXT);""")
self.db_cursor.execute("""create table
descriptions(id INTEGER PRIMARY KEY AUTOINCREMENT,
file_id INTEGER,
desc TEXT,
image TEXT,
image_x INTEGER,
image_y INTEGER,
thumb TEXT,
thumb_x INTEGER,
thumb_y INTEGER,
thumb_mode TEXT);""")
self.db_cursor.execute("insert into files values(1, 1, 'root', null, \
0, 0, 0, 0, null, null, null, null);")
def __scan(self):
"""scan content of the given path"""
self.busy = True
# jako, że to jest w osobnym wątku, a sqlite się przypieprza, że musi mieć
# konekszyn dla tego samego wątku, więc robimy osobne połączenie dla tego zadania.
db_connection = sqlite.connect("%s" % self.internal_filename,
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES,
isolation_level="EXCLUSIVE")
# new conection for this task, because it's running in separate thread
db_connection = sqlite.connect("%s" % \
(self.internal_dirname + '/db.sqlite'),
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES,
isolation_level="EXCLUSIVE")
db_cursor = db_connection.cursor()
timestamp = datetime.datetime.now()
timestamp = datetime.now()
mime = mimetypes.MimeTypes()
mov_ext = ('mkv','avi','ogg','mpg','wmv','mp4','mpeg')
# 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
@@ -328,8 +397,13 @@ class MainModel(ModelMT):
self.statusmsg = "Calculating number of files in directory tree..."
count = 0
for root, dirs, files in os.walk(self.path):
count += len(files)
try:
for root, dirs, files in os.walk(self.path):
count += len(files)
except:
if __debug__:
print 'm_main.py: os.walk in %s' % self.path
pass
if count > 0:
step = 1.0/count
@@ -342,45 +416,81 @@ class MainModel(ModelMT):
self.fresh_disk_iter = None
def __recurse(parent_id, name, path, date, size, filetype, discs_tree_iter=None):
"""recursive scans the path"""
def __recurse(parent_id, name, path, date, size, filetype,
discs_tree_iter=None):
"""recursive scans given path"""
if self.abort:
return -1
_size = size
myit = self.discsTree.append(discs_tree_iter,None)
myit = self.discs_tree.append(discs_tree_iter,None)
if parent_id == 1:
self.fresh_disk_iter = myit
self.discsTree.set_value(myit,2,gtk.STOCK_CDROM)
db_cursor.execute("insert into files(parent_id, filename, filepath, date, size, type, source) values(?,?,?,?,?,?,?)",
(parent_id, name, path, date, size, filetype, self.source))
self.discs_tree.set_value(myit,2,gtk.STOCK_CDROM)
sql = """insert into
files(parent_id, filename, filepath, date, size, type, source)
values(?,?,?,?,?,?,?)"""
db_cursor.execute(sql, (parent_id, name, path, date, size,
filetype, self.source))
else:
self.discsTree.set_value(myit,2,gtk.STOCK_DIRECTORY)
db_cursor.execute("insert into files(parent_id, filename, filepath, date, size, type) values(?,?,?,?,?,?)",
self.discs_tree.set_value(myit,2,gtk.STOCK_DIRECTORY)
sql = """
insert into
files(parent_id, filename, filepath, date, size, type)
values(?,?,?,?,?,?)
"""
db_cursor.execute(sql,
(parent_id, name, path, date, size, filetype))
db_cursor.execute("select seq FROM sqlite_sequence WHERE name='files'")
sql = """select seq FROM sqlite_sequence WHERE name='files'"""
db_cursor.execute(sql)
currentid=db_cursor.fetchone()[0]
self.discsTree.set_value(myit,0,currentid)
self.discsTree.set_value(myit,1,name)
self.discsTree.set_value(myit,3,parent_id)
self.discs_tree.set_value(myit,0,currentid)
self.discs_tree.set_value(myit,1,name)
self.discs_tree.set_value(myit,3,parent_id)
root,dirs,files = os.walk(path).next()
try:
root,dirs,files = os.walk(path).next()
except:
if __debug__:
print "m_main.py: cannot access ", path
#return -1
return 0
for i in dirs:
if self.fsenc:
j = i.decode(self.fsenc)
else:
j = i
try:
st = os.stat(os.path.join(root,i))
st_mtime = st.st_mtime
except OSError:
st_mtime = 0
dirsize = __recurse(currentid, j, os.path.join(path,i), st_mtime, 0, self.DIR, myit)
# do NOT follow symbolic links
if os.path.islink(os.path.join(root,i)):
l = os.readlink(os.path.join(root,i))
if self.fsenc:
l = l.decode(self.fsenc)
else:
l = l
sql = """
insert into files(parent_id, filename, filepath, date, size, type)
values(?,?,?,?,?,?)
"""
db_cursor.execute(sql, (currentid, j + " -> " + l,
os.path.join(path,i), st_mtime, 0,
self.LIN))
dirsize = 0
else:
dirsize = __recurse(currentid, j, os.path.join(path,i),
st_mtime, 0, self.DIR, myit)
if dirsize == -1:
break
@@ -399,15 +509,20 @@ class MainModel(ModelMT):
st_mtime = 0
st_size = 0
### scan files
# if i[-3:].lower() in mov_ext or \
# mime.guess_type(i)!= (None,None) and \
# mime.guess_type(i)[0].split("/")[0] == 'video':
### TODO: scan files
#if i.split('.').[-1].lower() in mov_ext:
# # video only
# info = filetypeHelper.guess_video(os.path.join(root,i))
# elif i[-3:].lower() in img_ext or \
# mime.guess_type(i)!= (None,None) and \
# mime.guess_type(i)[0].split("/")[0] == 'image':
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
@@ -424,20 +539,26 @@ class MainModel(ModelMT):
j = i
if self.fsenc:
j = i.decode(self.fsenc)
db_cursor.execute("insert into files(parent_id, filename, filepath, date, size, type) values(?,?,?,?,?,?)",
(currentid, j, os.path.join(path,i), st_mtime, st_size, self.FIL))
db_cursor.execute("update files set size=? where id=?",(_size, currentid))
sql = """
insert into files(parent_id, filename, filepath, date, size, type)
values(?,?,?,?,?,?)
"""
db_cursor.execute(sql, (currentid, j, os.path.join(path,i),
st_mtime, st_size, self.FIL))
sql = """update files set size=? where id=?"""
db_cursor.execute(sql,(_size, currentid))
if self.abort:
return -1
else:
return _size
if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1:
if __debug__:
print "m_main.py: __scan() __recurse() interrupted self.abort = True"
self.discsTree.remove(self.fresh_disk_iter)
print "m_main.py: __scan() __recurse() \
interrupted self.abort = True"
self.discs_tree.remove(self.fresh_disk_iter)
db_cursor.close()
db_connection.rollback()
else:
@@ -447,7 +568,7 @@ class MainModel(ModelMT):
db_connection.commit()
db_connection.close()
if __debug__:
print "m_main.py: __scan() time: ", (datetime.datetime.now() - timestamp)
print "m_main.py: __scan() time: ", (datetime.now() - timestamp)
self.busy = False
@@ -458,22 +579,27 @@ class MainModel(ModelMT):
def __fetch_db_into_treestore(self):
"""load data from DB to tree model"""
# cleanup treeStore
self.discsTree.clear()
self.__clear_discs_tree()
#connect
db_connection = sqlite.connect("%s" % self.internal_filename,
db_connection = sqlite.connect("%s" % \
(self.internal_dirname + '/db.sqlite'),
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
db_cursor = db_connection.cursor()
# fetch all the directories
try:
db_cursor.execute("SELECT id, parent_id, filename FROM files WHERE type=1 ORDER BY parent_id, filename")
sql = """
SELECT id, parent_id, filename FROM files
WHERE type=1 ORDER BY parent_id, filename
"""
db_cursor.execute(sql)
data = db_cursor.fetchall()
except:
# cleanup
self.cleanup()
self.filename = None
self.internal_filename = None
self.internal_dirname = None
print "%s: Wrong database format!" % self.filename
return
@@ -481,36 +607,40 @@ class MainModel(ModelMT):
"""fetch all children and place them in model"""
for row in data:
if row[1] == parent_id:
myiter = self.discsTree.insert_before(iterator,None)
self.discsTree.set_value(myiter,0,row[0]) # id
self.discsTree.set_value(myiter,1,row[2]) # name
self.discsTree.set_value(myiter,3,row[1]) # parent_id
myiter = self.discs_tree.insert_before(iterator, None)
self.discs_tree.set_value(myiter, 0, row[0]) # id
self.discs_tree.set_value(myiter, 1, row[2]) # name
self.discs_tree.set_value(myiter, 3, row[1]) # parent_id
get_children(row[0], myiter)
# isroot?
if iterator == None:
self.discsTree.set_value(myiter,2,gtk.STOCK_CDROM)
self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM)
else:
self.discsTree.set_value(myiter,2,gtk.STOCK_DIRECTORY)
self.discs_tree.set_value(myiter, 2,
gtk.STOCK_DIRECTORY)
return
if __debug__:
start_date = datetime.datetime.now()
start_date = datetime.now()
# launch scanning.
get_children()
if __debug__:
print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.datetime.now() - start_date)
print "m_main.py: __fetch_db_into_treestore() tree generation time: ",
(datetime.now() - start_date)
db_connection.close()
return
def __remove_branch_form_db(self, root_id):
parent_ids = [root_id,]
self.db_cursor.execute("select id from files where parent_id = ? and type = 1", (root_id,))
sql = """select id from files where parent_id = ? and type = 1"""
self.db_cursor.execute(sql, (root_id,))
ids = self.db_cursor.fetchall()
def get_children(fid):
parent_ids.append(fid)
self.db_cursor.execute("select id from files where parent_id = ? and type = 1", (fid,))
sql = """select id from files where parent_id = ? and type = 1"""
self.db_cursor.execute(sql, (fid,))
res = self.db_cursor.fetchall()
for i in res:
get_children(i[0])
@@ -522,8 +652,10 @@ class MainModel(ModelMT):
for c in parent_ids:
yield (c,)
self.db_cursor.executemany("delete from files where type = 1 and parent_id = ?", generator())
self.db_cursor.executemany("delete from files where id = ?",generator())
sql = """delete from files where type = 1 and parent_id = ?"""
self.db_cursor.executemany(sql, generator())
sql = """delete from files where id = ?"""
self.db_cursor.executemany(sql, generator())
self.db_connection.commit()
return
@@ -531,36 +663,40 @@ class MainModel(ModelMT):
def __append_added_volume(self):
"""append branch from DB to existing tree model"""
#connect
db_connection = sqlite.connect("%s" % self.internal_filename,
db_connection = sqlite.connect("%s" % \
(self.internal_dirname + '/db.sqlite'),
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
db_cursor = db_connection.cursor()
db_cursor.execute("SELECT id, parent_id, filename FROM files WHERE type=1 ORDER BY parent_id, filename")
sql = """SELECT id, parent_id, filename FROM files WHERE type=1 ORDER BY parent_id, filename"""
db_cursor.execute(sql)
data = db_cursor.fetchall()
def get_children(parent_id = 1, iterator = None):
"""fetch all children and place them in model"""
for row in data:
if row[1] == parent_id:
myiter = self.discsTree.insert_before(iterator,None)
self.discsTree.set_value(myiter,0,row[0])
self.discsTree.set_value(myiter,1,row[2])
self.discsTree.set_value(myiter,3,row[1])
myiter = self.discs_tree.insert_before(iterator, None)
self.discs_tree.set_value(myiter, 0, row[0])
self.discs_tree.set_value(myiter, 1, row[2])
self.discs_tree.set_value(myiter, 3, row[1])
get_children(row[0], myiter)
# isroot?
if iterator == None:
self.discsTree.set_value(myiter,2,gtk.STOCK_CDROM)
self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM)
else:
self.discsTree.set_value(myiter,2,gtk.STOCK_DIRECTORY)
self.discs_tree.set_value(myiter, 2,
gtk.STOCK_DIRECTORY)
return
if __debug__:
start_date = datetime.datetime.now()
start_date = datetime.now()
# launch scanning.
get_children()
if __debug__:
print "m_main.py: __fetch_db_into_treestore() tree generation time: ", (datetime.datetime.now() - start_date)
print "m_main.py: __fetch_db_into_treestore() tree generation time: ",
(datetime.now() - start_date)
db_connection.close()
return

1193
src/utils/EXIF.py Normal file

File diff suppressed because it is too large Load Diff

43
src/views/v_details.py Normal file
View File

@@ -0,0 +1,43 @@
# This Python file uses the following encoding: utf-8
#
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
#
# Copyright (C) 2007 by Roman 'gryf' Dobosz
#
# This file is part of pyGTKtalog.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# -------------------------------------------------------------------------
from gtkmvc import View
import os.path
import utils.globals
class DetailsView(View):
""" """
GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
def __init__(self, ctrl, stand_alone=True):
top_widget = "hbox_details"
if stand_alone:
top_widget = "window_details"
View.__init__(self, ctrl, self.GLADE, top_widget)
return
def is_stand_alone(self):
return self["window_details"] is not None
pass # end of class

View File

@@ -22,9 +22,10 @@
# -------------------------------------------------------------------------
from gtkmvc import View
import os.path
import utils.globals
from gtkmvc import View
from v_details import DetailsView
class MainView(View):
"""This handles only the graphical representation of the
@@ -33,6 +34,13 @@ class MainView(View):
GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
def __init__(self, ctrl):
View.__init__(self, ctrl, self.GLADE)
self.details = None
return
def create_sub_view(self, details_ctrl):
"""attach sub view"""
self.details = DetailsView(details_ctrl, False)
vpan = self['vpaned1']
vpan.add2(self.details.get_top_widget())
return
pass # end of class