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 #!/bin/sh
# Simple wraper to launch python application from desired place. # 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`" current_dir="`pwd`"
cd ~/Devel/Python/pyGTKtalog cd $data_dir
#export PYTHONPATH="$PYTHONPATH:/home/gryf/Devel/Python/pyGTKtalog/mvc" #exec $python_intrpreter -OO pygtktalog.py "$current_dir" "$@"
#exec python -OO pygtktalog.py $@ exec $python_intrpreter pygtktalog.py "$current_dir" "$@"
exec python pygtktalog.py "$current_dir" "$@"

View File

@@ -68,11 +68,11 @@ def check_requirements():
except: 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" 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) sys.exit(1)
try: #try:
import mx.DateTime # import mx.DateTime
except: #except:
print "pyGTKtalog uses Egenix mx.DateTime.\nYou can instal it from your distribution repositry,\nor get it at: http://www.egenix.com" # 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) # sys.exit(1)
if conf.confd['exportxls']: if conf.confd['exportxls']:
try: 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_thumb'].set_active(self.model.confd['pil'])
self.view['ch_exif'].set_active(self.model.confd['exif']) self.view['ch_exif'].set_active(self.model.confd['exif'])
self.view['ch_gthumb'].set_active(self.model.confd['gthumb']) self.view['ch_gthumb'].set_active(self.model.confd['gthumb'])
self.view['ch_compress'].set_active(self.model.confd['compress'])
# initialize tree view # initialize tree view
self.__setup_category_tree() self.__setup_category_tree()
@@ -92,17 +93,21 @@ class ConfigController(Controller):
self.model.confd['pil'] = self.view['ch_thumb'].get_active() self.model.confd['pil'] = self.view['ch_thumb'].get_active()
self.model.confd['exif'] = self.view['ch_exif'].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['gthumb'] = self.view['ch_gthumb'].get_active()
self.model.confd['compress'] = self.view['ch_compress'].get_active()
self.model.save() self.model.save()
self.view['config'].destroy() self.view['config'].destroy()
return return
def on_button_ejt_clicked(self,button): def on_button_ejt_clicked(self, button):
self.__show_filechooser() self.__show_filechooser()
return return
def on_button_mnt_clicked(self,button): def on_button_mnt_clicked(self, button):
self.__show_dirchooser() self.__show_dirchooser()
return return
def on_ch_retrive_toggled(self, widget):
return
############################ ############################
# private controller methods # private controller methods
@@ -166,157 +171,4 @@ class ConfigController(Controller):
dialog.destroy() dialog.destroy()
pass # end of class 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 = \ licence = \
""" """
GPL v2 GPL v2
@@ -34,6 +34,7 @@ from utils import deviceHelper
from gtkmvc import Controller from gtkmvc import Controller
from c_config import ConfigController from c_config import ConfigController
from c_details import DetailsController
from views.v_config import ConfigView from views.v_config import ConfigView
from models.m_config import ConfigModel from models.m_config import ConfigModel
@@ -47,12 +48,12 @@ class MainController(Controller):
"""Controller for main application window""" """Controller for main application window"""
scan_cd = False scan_cd = False
widgets = ( widgets = (
"discs","files","details", "discs","files",
'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1', 'save1','save_as1','cut1','copy1','paste1','delete1','add_cd','add_directory1',
'tb_save','tb_addcd','tb_find', 'tb_save','tb_addcd','tb_find',
) )
widgets_all = ( widgets_all = (
"discs","files","details", "discs","files",
'file1','edit1','add_cd','add_directory1','help1', 'file1','edit1','add_cd','add_directory1','help1',
'tb_save','tb_addcd','tb_find','tb_new','tb_open','tb_quit', 'tb_save','tb_addcd','tb_find','tb_new','tb_open','tb_quit',
) )
@@ -61,6 +62,7 @@ class MainController(Controller):
def __init__(self, model): def __init__(self, model):
"""Initialize controller""" """Initialize controller"""
Controller.__init__(self, model) Controller.__init__(self, model)
self.details = DetailsController(model.details)
return return
def register_view(self, view): def register_view(self, view):
@@ -74,21 +76,19 @@ class MainController(Controller):
for widget in self.widgets_cancel: for widget in self.widgets_cancel:
self.view[widget].set_sensitive(False) 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__: if __debug__:
self.view['debugbtn'].show() self.view['debugbtn'].show()
else: else:
self.view['debugbtn'].hide() self.view['debugbtn'].hide()
# ustaw domyślne właściwości dla poszczególnych widżetów # 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['main'].set_icon_list(gtk.gdk.pixbuf_new_from_file("pixmaps/mainicon.png"))
#self.view['detailplace'].set_sensitive(False) #self.view['detailplace'].set_sensitive(False)
self.view['details'].hide() #self.view['exifTab'].hide()
self.view['exifTab'].hide() #self.view['animeTab'].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']) self.view['toolbar1'].set_active(self.model.config.confd['showtoolbar'])
if self.model.config.confd['showtoolbar']: if self.model.config.confd['showtoolbar']:
self.view['maintoolbar'].show() self.view['maintoolbar'].show()
@@ -115,10 +115,13 @@ class MainController(Controller):
if self.model.filename != None: if self.model.filename != None:
self.__activateUI(self.model.filename) self.__activateUI(self.model.filename)
# register detail subview
#self.view.create_sub_view(self.details)
# generate recent menu
self.__generate_recent_menu() self.__generate_recent_menu()
# Show main window # Show main window
self.view['main'].show(); self.view['main'].show();
return return
######################################################################### #########################################################################
@@ -194,12 +197,12 @@ class MainController(Controller):
def on_discs_cursor_changed(self, widget): def on_discs_cursor_changed(self, widget):
"""Show files on right treeview, after clicking the left disc treeview.""" """Show files on right treeview, after clicking the left disc treeview."""
model = self.view['discs'].get_model() 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__: if __debug__:
print "c_main.py, on_discs_cursor_changed()",selected_item print "c_main.py, on_discs_cursor_changed()",selected_item
self.model.get_root_entries(selected_item) self.model.get_root_entries(selected_item)
self.__getItemInfo(selected_item) self.__get_item_info(selected_item)
return return
def on_discs_row_activated(self, treeview, path, treecolumn): def on_discs_row_activated(self, treeview, path, treecolumn):
@@ -229,7 +232,7 @@ class MainController(Controller):
treeview.get_selection().unselect_all() treeview.get_selection().unselect_all()
treeview.get_selection().select_path(path) 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 # if ancestor is 'root', then activate "update" menu item
self.view['update1'].set_sensitive(True) self.view['update1'].set_sensitive(True)
else: else:
@@ -239,7 +242,7 @@ class MainController(Controller):
# elif event.button == 1: # Left click # elif event.button == 1: # Left click
# """Show files on right treeview, after clicking the left disc treeview.""" # """Show files on right treeview, after clicking the left disc treeview."""
# model = self.view['discs'].get_model() # 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__: # if __debug__:
# print "c_main.py, on_discs_cursor_changed()",selected_item # print "c_main.py, on_discs_cursor_changed()",selected_item
# self.model.get_root_entries(selected_item) # self.model.get_root_entries(selected_item)
@@ -266,16 +269,16 @@ class MainController(Controller):
itera = model.get_iter(paths[0]) itera = model.get_iter(paths[0])
if model.get_value(itera,4) == 1: if model.get_value(itera,4) == 1:
#directory, do nothin', just turn off view #directory, do nothin', just turn off view
self.view['details'].hide() '''self.view['details'].hide()
buf = self.view['details'].get_buffer() buf = self.view['details'].get_buffer()
buf.set_text('') buf.set_text('')
self.view['details'].set_buffer(buf) self.view['details'].set_buffer(buf)'''
if __debug__: if __debug__:
print "c_main.py: on_files_cursor_changed() directory selected" print "c_main.py: on_files_cursor_changed() directory selected"
else: else:
#file, show what you got. #file, show what you got.
selected_item = self.model.filesList.get_value(model.get_iter(treeview.get_cursor()[0]),0) selected_item = self.model.files_list.get_value(model.get_iter(treeview.get_cursor()[0]),0)
self.__getItemInfo(selected_item) self.__get_item_info(selected_item)
if __debug__: if __debug__:
print "c_main.py: on_files_cursor_changed() some other thing selected" print "c_main.py: on_files_cursor_changed() some other thing selected"
except: except:
@@ -285,11 +288,11 @@ class MainController(Controller):
def on_files_row_activated(self, files_obj, row, column): def on_files_row_activated(self, files_obj, row, column):
"""On directory doubleclick in files listview dive into desired branch.""" """On directory doubleclick in files listview dive into desired branch."""
# TODO: można by też podczepić klawisz backspace do przechodzenia poziom wyżej. # TODO: map backspace key for moving to upper level of directiories
f_iter = self.model.filesList.get_iter(row) f_iter = self.model.files_list.get_iter(row)
current_id = self.model.filesList.get_value(f_iter,0) 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. # ONLY directories. files are omitted.
self.model.get_root_entries(current_id) self.model.get_root_entries(current_id)
@@ -298,12 +301,12 @@ class MainController(Controller):
if not self.view['discs'].row_expanded(d_path): if not self.view['discs'].row_expanded(d_path):
self.view['discs'].expand_row(d_path,False) 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: if new_iter:
while new_iter: while new_iter:
if self.model.discsTree.get_value(new_iter,0) == current_id: if self.model.discs_tree.get_value(new_iter,0) == current_id:
self.view['discs'].set_cursor(self.model.discsTree.get_path(new_iter)) self.view['discs'].set_cursor(self.model.discs_tree.get_path(new_iter))
new_iter = self.model.discsTree.iter_next(new_iter) new_iter = self.model.discs_tree.iter_next(new_iter)
return return
def on_cancel1_activate(self, widget): def on_cancel1_activate(self, widget):
@@ -313,7 +316,7 @@ class MainController(Controller):
self.__abort() self.__abort()
def on_tb_find_clicked(self, widget): def on_tb_find_clicked(self, widget):
# TODO: zaimplementować wyszukiwarkę # TODO: implement searcher
return return
def recent_item_response(self, path): def recent_item_response(self, path):
@@ -329,50 +332,47 @@ class MainController(Controller):
if self.model.get_source(path) == self.model.CD: if self.model.get_source(path) == self.model.CD:
if self.__addCD(label): 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 pass
elif self.model.get_source(path) == self.model.DR: elif self.model.get_source(path) == self.model.DR:
if self.__addDirectory(filepath, label): 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 pass
return return
def on_delete2_activate(self, menu_item): def on_delete2_activate(self, menu_item):
model = self.view['discs'].get_model() model = self.view['discs'].get_model()
try: 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: except:
return return
if self.model.config.confd['delwarn']: 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.') obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name, 'Object will be permanently removed.')
if not obj.run(): if not obj.run():
return return
self.model.delete(selected_iter) self.model.delete(selected_iter)
self.model.unsaved_project = True self.model.unsaved_project = True
if self.model.filename != None: self.__setTitle(filepath=self.model.filename, modified=True)
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
return return
def on_debugbtn_clicked(self,widget): 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__: if __debug__:
print "\nc_main.py: on_debugbtn_clicked()" print "\nc_main.py: on_debugbtn_clicked()"
print "------" print "------"
print "unsaved_project = %s" % self.model.unsaved_project print "unsaved_project = %s" % self.model.unsaved_project
print "filename = %s" % self.model.filename 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 "db_connection = %s" % self.model.db_connection
print "abort = %s" % self.model.abort print "abort = %s" % self.model.abort
print "self.model.config.recent = %s" % self.model.config.recent print "self.model.config.recent = %s" % self.model.config.recent
it = self.model.discsTree.get_iter_first() it = self.model.tags_list.get_iter_first()
myit = self.model.discsTree.insert_before(None,None) myit = self.model.tags_list.insert_before(None,None)
self.model.discsTree.set_value(myit,0,0) self.model.tags_list.set_value(myit,0,0)
self.model.discsTree.set_value(myit,1,"nazwa") self.model.tags_list.set_value(myit,1,"nazwa")
self.model.discsTree.set_value(myit,3,3) self.model.tags_list.set_value(myit,2,231233)
self.model.discsTree.set_value(myit,2,gtk.STOCK_INFO) print "source: %s" % self.model.source
##################### #####################
# observed properetis # observed properetis
@@ -423,7 +423,7 @@ class MainController(Controller):
if path: if path:
if not self.model.open(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: else:
self.__generate_recent_menu() self.__generate_recent_menu()
self.__activateUI(path) self.__activateUI(path)
@@ -431,10 +431,9 @@ class MainController(Controller):
def __save(self): def __save(self):
"""Save catalog to file""" """Save catalog to file"""
#{{{
if self.model.filename: if self.model.filename:
self.model.save() self.model.save()
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename) self.__setTitle(filepath=self.model.filename)
else: else:
self.__save_as() self.__save_as()
pass pass
@@ -443,7 +442,7 @@ class MainController(Controller):
"""Save database to file under different filename.""" """Save database to file under different filename."""
path = Dialogs.ChooseDBFilename().show_dialog() path = Dialogs.ChooseDBFilename().show_dialog()
if path: if path:
self.view['main'].set_title("%s - pyGTKtalog" % path) self.__setTitle(filepath=path)
self.model.save(path) self.model.save(path)
self.model.config.add_recent(path) self.model.config.add_recent(path)
pass pass
@@ -461,11 +460,8 @@ class MainController(Controller):
self.view[widget].set_sensitive(False) self.view[widget].set_sensitive(False)
self.model.source = self.model.CD self.model.source = self.model.CD
self.model.scan(self.model.config.confd['cd'],label) self.model.scan(self.model.config.confd['cd'],label)
self.unsaved_project = True self.model.unsaved_project = True
if self.model.filename != None: self.__setTitle(filepath=self.model.filename, modified=True)
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
return True return True
else: else:
Dialogs.Wrn("Error mounting device - pyGTKtalog", Dialogs.Wrn("Error mounting device - pyGTKtalog",
@@ -485,11 +481,8 @@ class MainController(Controller):
self.scan_cd = False self.scan_cd = False
self.model.source = self.model.DR self.model.source = self.model.DR
self.model.scan(path, label) self.model.scan(path, label)
self.unsaved_project = True self.model.unsaved_project = True
if self.model.filename != None: self.__setTitle(filepath=self.model.filename, modified=True)
self.view['main'].set_title("%s - pyGTKtalog *" % self.model.filename)
else:
self.view['main'].set_title("untitled - pyGTKtalog *")
return True return True
def __doQuit(self): def __doQuit(self):
@@ -516,10 +509,10 @@ class MainController(Controller):
self.model.new() self.model.new()
# clear "details" buffer # clear "details" buffer
txt = "" '''txt = ""
buf = self.view['details'].get_buffer() buf = self.view['details'].get_buffer()
buf.set_text(txt) buf.set_text(txt)
self.view['details'].set_buffer(buf) self.view['details'].set_buffer(buf)'''
self.__activateUI() self.__activateUI()
@@ -527,7 +520,27 @@ class MainController(Controller):
def __setup_disc_treeview(self): def __setup_disc_treeview(self):
"""Setup TreeView discs widget as tree.""" """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') c = gtk.TreeViewColumn('Filename')
@@ -547,7 +560,7 @@ class MainController(Controller):
def __setup_files_treeview(self): def __setup_files_treeview(self):
"""Setup TreeView files widget, as columned list.""" """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) self.view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE)
@@ -588,10 +601,10 @@ class MainController(Controller):
self.model.abort = True self.model.abort = True
return return
def __activateUI(self, name='untitled'): def __activateUI(self, name=False):
"""Make UI active, and set title""" """Make UI active, and set title"""
self.model.unsaved_project = False self.model.unsaved_project = False
self.view['main'].set_title("%s - pyGTKtalog" % name) self.__setTitle(filepath=name)
for widget in self.widgets: for widget in self.widgets:
try: try:
self.view[widget].set_sensitive(True) self.view[widget].set_sensitive(True)
@@ -601,7 +614,20 @@ class MainController(Controller):
while gtk.events_pending(): while gtk.events_pending():
gtk.main_iteration() gtk.main_iteration()
return 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): def __storeSettings(self):
"""Store window size and pane position in config file (using config object from model)""" """Store window size and pane position in config file (using config object from model)"""
if self.model.config.confd['savewin']: if self.model.config.confd['savewin']:
@@ -620,8 +646,6 @@ class MainController(Controller):
self.recent_menu = gtk.Menu() self.recent_menu = gtk.Menu()
for i in self.model.config.recent: for i in self.model.config.recent:
name = os.path.basename(i) name = os.path.basename(i)
if name.endswith(".pgt"):
name = name[:-4]
item = gtk.MenuItem("%s" % name) item = gtk.MenuItem("%s" % name)
item.connect_object("activate", self.recent_item_response, i) item.connect_object("activate", self.recent_item_response, i)
self.recent_menu.append(item) self.recent_menu.append(item)
@@ -629,11 +653,11 @@ class MainController(Controller):
self.view['recent_files1'].set_submenu(self.recent_menu) self.view['recent_files1'].set_submenu(self.recent_menu)
return return
def __getItemInfo(self, item): def __get_item_info(self, item):
self.view['details'].show() '''self.view['details'].show()
txt = self.model.get_file_info(item) txt = self.model.get_file_info(item)
buf = self.view['details'].get_buffer() buf = self.view['details'].get_buffer()
buf.set_text(txt) buf.set_text(txt)
self.view['details'].set_buffer(buf) self.view['details'].set_buffer(buf)'''
return return
pass # end of class pass # end of class

View File

@@ -76,6 +76,7 @@ class ConfigModel(Model):
'showtoolbar':True, 'showtoolbar':True,
'showstatusbar':True, 'showstatusbar':True,
'delwarn':True, 'delwarn':True,
'compress':True,
} }
dictconf = { dictconf = {
@@ -96,6 +97,7 @@ class ConfigModel(Model):
'confirm abandon current catalog':'confirmabandon', 'confirm abandon current catalog':'confirmabandon',
'show toolbar':'showtoolbar', 'show toolbar':'showtoolbar',
'show statusbar and progress bar':'showstatusbar', 'show statusbar and progress bar':'showstatusbar',
'compress collection':'compress',
} }
dbool = ( dbool = (
@@ -112,6 +114,8 @@ class ConfigModel(Model):
'confirmabandon', 'confirmabandon',
'showtoolbar', 'showtoolbar',
'showstatusbar', 'showstatusbar',
'delwarn',
'compress',
) )
recent = [] 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 gtkmvc.model_mt import ModelMT
from pysqlite2 import dbapi2 as sqlite
from datetime import datetime
#import mx.DateTime
try: try:
import threading as _threading import threading as _threading
except ImportError: except ImportError:
if __debug__: if __debug__:
print "m_main.py: import exception: _threading" print "m_main.py: import exception: _threading"
import dummy_threading as _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 from utils import EXIF
import datetime
import mx.DateTime
import os
import sys
import mimetypes
import bz2
import gtk
import gobject
from m_config import ConfigModel 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): 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} __properties__ = {'busy': False, 'statusmsg': '', 'progress': 0}
config = ConfigModel() # constants instead of dictionary tables
unsaved_project = False # type of files
filename = None LAB = 0 # root of the tree - label/collection name
internal_filename = None DIR = 1 # directory
db_connection = None FIL = 2 # file
db_cursor = None LIN = 3 # symbolic link
abort = False
# 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 CD = 1 # sorce: cd/dvd
discsTree = gtk.TreeStore(gobject.TYPE_INT, gobject.TYPE_STRING, str, gobject.TYPE_INT) DR = 2 # source: filesystem
# 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
def __init__(self): def __init__(self):
ModelMT.__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.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 return
def cleanup(self): def cleanup(self):
self.__close_db_connection() self.__close_db_connection()
if self.internal_filename != None: if self.internal_dirname != None:
try: try:
os.unlink(self.internal_filename) shutil.rmtree(self.internal_dirname)
except: except:
if __debug__: if __debug__:
print "m_main.py: cleanup()", self.internal_filename print "m_main.py: cleanup()", self.internal_dirname
pass
try:
os.unlink(self.internal_filename + '-journal')
except:
if __debug__:
print "m_main.py: cleanup()", self.internal_filename+'-journal'
pass pass
return return
@@ -100,19 +131,11 @@ class MainModel(ModelMT):
def new(self): def new(self):
self.unsaved_project = False self.unsaved_project = False
self.__create_internal_filename() self.__create_internal_dirname()
self.__connect_to_db() self.__connect_to_db()
self.__create_database() self.__create_database()
try: self.__clear_trees()
self.discsTree.clear()
except:
pass
try:
self.filesList.clear()
except:
pass
return return
@@ -125,42 +148,23 @@ class MainModel(ModelMT):
def open(self, filename=None): def open(self, filename=None):
"""try to open db file""" """try to open db file"""
self.unsaved_project = False self.unsaved_project = False
self.__create_internal_filename() self.__create_internal_dirname()
self.filename = filename self.filename = filename
try: try:
source = bz2.BZ2File(filename, 'rb') tar = tarfile.open(filename, "r:gz")
except: 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: try:
data = source.read(1024000) tar = tarfile.open(filename, "r")
except: except:
# smth went wrong print "%s: file cannot be read!" % filename
print "%s: Wrong database format!" % self.filename
if __debug__:
print "m_main.py: open() something goes bad"
self.filename = None self.filename = None
self.internal_filename = None self.internal_dirname = None
try: return
self.discsTree.clear()
except: os.chdir(self.internal_dirname)
pass tar.extractall()
tar.close()
try:
self.filesList.clear()
except:
pass
return False
if not data: break
destination.write(data)
destination.close()
source.close()
self.__connect_to_db() self.__connect_to_db()
self.__fetch_db_into_treestore() self.__fetch_db_into_treestore()
@@ -168,7 +172,7 @@ class MainModel(ModelMT):
return True return True
def scan(self,path,label): def scan(self, path, label):
"""scan files in separated thread""" """scan files in separated thread"""
# flush buffer to release db lock. # flush buffer to release db lock.
@@ -183,9 +187,10 @@ class MainModel(ModelMT):
self.thread.start() self.thread.start()
return return
def get_root_entries(self,id=None): def get_root_entries(self, id=None):
"""Get all children down from sepcified root"""
try: try:
self.filesList.clear() self.files_list.clear()
except: except:
pass pass
# parent for virtual '..' dir # 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,0,self.cur.fetchone()[0])
#self.filemodel.set_value(myiter,1,'..') #self.filemodel.set_value(myiter,1,'..')
#if __debug__: #if __debug__:
# print datetime.datetime.fromtimestamp(ch[3]) # print datetime.fromtimestamp(ch[3])
# directories first # 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() data = self.db_cursor.fetchall()
for ch in data: for ch in data:
myiter = self.filesList.insert_before(None,None) myiter = self.files_list.insert_before(None, None)
self.filesList.set_value(myiter,0,ch[0]) self.files_list.set_value(myiter, 0, ch[0])
self.filesList.set_value(myiter,1,ch[1]) self.files_list.set_value(myiter, 1, ch[1])
self.filesList.set_value(myiter,2,ch[2]) self.files_list.set_value(myiter, 2, ch[2])
self.filesList.set_value(myiter,3,datetime.datetime.fromtimestamp(ch[3])) self.files_list.set_value(myiter, 3,
self.filesList.set_value(myiter,4,1) datetime.fromtimestamp(ch[3]))
self.filesList.set_value(myiter,5,'direktorja') self.files_list.set_value(myiter, 4, 1)
self.filesList.set_value(myiter,6,gtk.STOCK_DIRECTORY) self.files_list.set_value(myiter, 5, 'direktorja')
self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY)
# all the rest # 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() data = self.db_cursor.fetchall()
for ch in data: for ch in data:
myiter = self.filesList.insert_before(None,None) myiter = self.files_list.insert_before(None, None)
self.filesList.set_value(myiter,0,ch[0]) self.files_list.set_value(myiter, 0, ch[0])
self.filesList.set_value(myiter,1,ch[1]) self.files_list.set_value(myiter, 1, ch[1])
self.filesList.set_value(myiter,2,ch[2]) self.files_list.set_value(myiter, 2, ch[2])
self.filesList.set_value(myiter,3,datetime.datetime.fromtimestamp(ch[3])) self.files_list.set_value(myiter, 3, datetime.fromtimestamp(ch[3]))
self.filesList.set_value(myiter,4,ch[4]) self.files_list.set_value(myiter, 4, ch[4])
self.filesList.set_value(myiter,5,'kategoria srategoria') self.files_list.set_value(myiter, 5, 'kategoria srategoria')
self.filesList.set_value(myiter,6,gtk.STOCK_FILE) if ch[4] == self.FIL:
#print datetime.datetime.fromtimestamp(ch[3]) 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 return
def get_file_info(self, id): def get_file_info(self, id):
"""get file info from database""" """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() set = self.db_cursor.fetchone()
if set == None: if set == None:
return '' 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 return string
def get_source(self, path): def get_source(self, path):
"""get source of top level directory""" """get source of top level directory"""
bid = self.discsTree.get_value(self.discsTree.get_iter(path[0]),0) bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
self.db_cursor.execute("select source from files where id = ?", (bid,)) 0)
self.db_cursor.execute("select source from files where id = ?",
(bid,))
res = self.db_cursor.fetchone() res = self.db_cursor.fetchone()
if res == None: if res == None:
return False return False
@@ -245,8 +261,10 @@ class MainModel(ModelMT):
def get_label_and_filepath(self, path): def get_label_and_filepath(self, path):
"""get source of top level directory""" """get source of top level directory"""
bid = self.discsTree.get_value(self.discsTree.get_iter(path[0]),0) bid = self.discs_tree.get_value(self.discs_tree.get_iter(path[0]),
self.db_cursor.execute("select filepath, filename from files where id = ? and parent_id = 1", (bid,)) 0)
self.db_cursor.execute("select filepath, filename from files \
where id = ? and parent_id = 1", (bid,))
res = self.db_cursor.fetchone() res = self.db_cursor.fetchone()
if res == None: if res == None:
return None, None return None, None
@@ -255,13 +273,38 @@ class MainModel(ModelMT):
def delete(self, branch_iter): def delete(self, branch_iter):
if not branch_iter: if not branch_iter:
return return
self.__remove_branch_form_db(self.discsTree.get_value(branch_iter,0)) self.__remove_branch_form_db(self.discs_tree.get_value(branch_iter,0))
self.discsTree.remove(branch_iter) self.discs_tree.remove(branch_iter)
return return
# private class functions # 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): 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() self.db_cursor = self.db_connection.cursor()
return return
@@ -274,22 +317,28 @@ class MainModel(ModelMT):
self.db_connection = None self.db_connection = None
return return
def __create_internal_filename(self): def __create_internal_dirname(self):
self.cleanup() 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 return
def __compress_and_save(self): def __compress_and_save(self):
source = open(self.internal_filename, 'rb') if self.config.confd['compress']:
destination = bz2.BZ2File(self.filename, 'w') tar = tarfile.open(self.filename, "w:gz")
else:
tar = tarfile.open(self.filename, "w")
os.chdir(self.internal_dirname)
tar.add('.')
tar.close()
while True:
data = source.read(1024000)
if not data: break
destination.write(data)
destination.close()
source.close()
self.unsaved_project = False self.unsaved_project = False
return return
@@ -303,24 +352,44 @@ class MainModel(ModelMT):
date datetime, date datetime,
size integer, size integer,
type integer, type integer,
source integer);""") source integer,
self.db_cursor.execute("insert into files values(1, 1, 'root', null, 0, 0, 0, 0);") 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): def __scan(self):
"""scan content of the given path""" """scan content of the given path"""
self.busy = True self.busy = True
# jako, że to jest w osobnym wątku, a sqlite się przypieprza, że musi mieć # new conection for this task, because it's running in separate thread
# konekszyn dla tego samego wątku, więc robimy osobne połączenie dla tego zadania. db_connection = sqlite.connect("%s" % \
db_connection = sqlite.connect("%s" % self.internal_filename, (self.internal_dirname + '/db.sqlite'),
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES, detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES,
isolation_level="EXCLUSIVE") isolation_level="EXCLUSIVE")
db_cursor = db_connection.cursor() db_cursor = db_connection.cursor()
timestamp = datetime.datetime.now() timestamp = datetime.now()
mime = mimetypes.MimeTypes() # TODO: file types has to be moved to configuration model
mov_ext = ('mkv','avi','ogg','mpg','wmv','mp4','mpeg') mov_ext = ('mkv', 'avi', 'ogg', 'mpg', 'wmv', 'mp4', 'mpeg')
img_ext = ('jpg','jpeg','png','gif','bmp','tga','tif','tiff','ilbm','iff','pcx') img_ext = ('jpg','jpeg','png','gif','bmp','tga','tif','tiff','ilbm','iff','pcx')
# count files in directory tree # count files in directory tree
@@ -328,8 +397,13 @@ class MainModel(ModelMT):
self.statusmsg = "Calculating number of files in directory tree..." self.statusmsg = "Calculating number of files in directory tree..."
count = 0 count = 0
for root, dirs, files in os.walk(self.path): try:
count += len(files) 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: if count > 0:
step = 1.0/count step = 1.0/count
@@ -342,45 +416,81 @@ class MainModel(ModelMT):
self.fresh_disk_iter = None self.fresh_disk_iter = None
def __recurse(parent_id, name, path, date, size, filetype, discs_tree_iter=None): def __recurse(parent_id, name, path, date, size, filetype,
"""recursive scans the path""" discs_tree_iter=None):
"""recursive scans given path"""
if self.abort: if self.abort:
return -1 return -1
_size = size _size = size
myit = self.discsTree.append(discs_tree_iter,None) myit = self.discs_tree.append(discs_tree_iter,None)
if parent_id == 1: if parent_id == 1:
self.fresh_disk_iter = myit self.fresh_disk_iter = myit
self.discsTree.set_value(myit,2,gtk.STOCK_CDROM) self.discs_tree.set_value(myit,2,gtk.STOCK_CDROM)
db_cursor.execute("insert into files(parent_id, filename, filepath, date, size, type, source) values(?,?,?,?,?,?,?)", sql = """insert into
(parent_id, name, path, date, size, filetype, self.source)) files(parent_id, filename, filepath, date, size, type, source)
values(?,?,?,?,?,?,?)"""
db_cursor.execute(sql, (parent_id, name, path, date, size,
filetype, self.source))
else: else:
self.discsTree.set_value(myit,2,gtk.STOCK_DIRECTORY) self.discs_tree.set_value(myit,2,gtk.STOCK_DIRECTORY)
db_cursor.execute("insert into files(parent_id, filename, filepath, date, size, type) values(?,?,?,?,?,?)", sql = """
insert into
files(parent_id, filename, filepath, date, size, type)
values(?,?,?,?,?,?)
"""
db_cursor.execute(sql,
(parent_id, name, path, date, size, filetype)) (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] currentid=db_cursor.fetchone()[0]
self.discsTree.set_value(myit,0,currentid) self.discs_tree.set_value(myit,0,currentid)
self.discsTree.set_value(myit,1,name) self.discs_tree.set_value(myit,1,name)
self.discsTree.set_value(myit,3,parent_id) 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: for i in dirs:
if self.fsenc: if self.fsenc:
j = i.decode(self.fsenc) j = i.decode(self.fsenc)
else: else:
j = i j = i
try: try:
st = os.stat(os.path.join(root,i)) st = os.stat(os.path.join(root,i))
st_mtime = st.st_mtime st_mtime = st.st_mtime
except OSError: except OSError:
st_mtime = 0 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: if dirsize == -1:
break break
@@ -399,15 +509,20 @@ class MainModel(ModelMT):
st_mtime = 0 st_mtime = 0
st_size = 0 st_size = 0
### scan files ### TODO: scan files
# if i[-3:].lower() in mov_ext or \ #if i.split('.').[-1].lower() in mov_ext:
# mime.guess_type(i)!= (None,None) and \
# mime.guess_type(i)[0].split("/")[0] == 'video':
# # video only # # video only
# info = filetypeHelper.guess_video(os.path.join(root,i)) # 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 # pass
### end of scan ### end of scan
@@ -424,20 +539,26 @@ class MainModel(ModelMT):
j = i j = i
if self.fsenc: if self.fsenc:
j = i.decode(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)) 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))
db_cursor.execute("update files set size=? where id=?",(_size, currentid)) sql = """update files set size=? where id=?"""
db_cursor.execute(sql,(_size, currentid))
if self.abort: if self.abort:
return -1 return -1
else: else:
return _size return _size
if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1: if __recurse(1, self.label, self.path, 0, 0, self.DIR) == -1:
if __debug__: if __debug__:
print "m_main.py: __scan() __recurse() interrupted self.abort = True" print "m_main.py: __scan() __recurse() \
self.discsTree.remove(self.fresh_disk_iter) interrupted self.abort = True"
self.discs_tree.remove(self.fresh_disk_iter)
db_cursor.close() db_cursor.close()
db_connection.rollback() db_connection.rollback()
else: else:
@@ -447,7 +568,7 @@ class MainModel(ModelMT):
db_connection.commit() db_connection.commit()
db_connection.close() db_connection.close()
if __debug__: if __debug__:
print "m_main.py: __scan() time: ", (datetime.datetime.now() - timestamp) print "m_main.py: __scan() time: ", (datetime.now() - timestamp)
self.busy = False self.busy = False
@@ -458,22 +579,27 @@ class MainModel(ModelMT):
def __fetch_db_into_treestore(self): def __fetch_db_into_treestore(self):
"""load data from DB to tree model""" """load data from DB to tree model"""
# cleanup treeStore # cleanup treeStore
self.discsTree.clear() self.__clear_discs_tree()
#connect #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) detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
db_cursor = db_connection.cursor() db_cursor = db_connection.cursor()
# fetch all the directories # fetch all the directories
try: 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() data = db_cursor.fetchall()
except: except:
# cleanup # cleanup
self.cleanup() self.cleanup()
self.filename = None self.filename = None
self.internal_filename = None self.internal_dirname = None
print "%s: Wrong database format!" % self.filename print "%s: Wrong database format!" % self.filename
return return
@@ -481,36 +607,40 @@ class MainModel(ModelMT):
"""fetch all children and place them in model""" """fetch all children and place them in model"""
for row in data: for row in data:
if row[1] == parent_id: if row[1] == parent_id:
myiter = self.discsTree.insert_before(iterator,None) myiter = self.discs_tree.insert_before(iterator, None)
self.discsTree.set_value(myiter,0,row[0]) # id self.discs_tree.set_value(myiter, 0, row[0]) # id
self.discsTree.set_value(myiter,1,row[2]) # name self.discs_tree.set_value(myiter, 1, row[2]) # name
self.discsTree.set_value(myiter,3,row[1]) # parent_id self.discs_tree.set_value(myiter, 3, row[1]) # parent_id
get_children(row[0], myiter) get_children(row[0], myiter)
# isroot? # isroot?
if iterator == None: if iterator == None:
self.discsTree.set_value(myiter,2,gtk.STOCK_CDROM) self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM)
else: else:
self.discsTree.set_value(myiter,2,gtk.STOCK_DIRECTORY) self.discs_tree.set_value(myiter, 2,
gtk.STOCK_DIRECTORY)
return return
if __debug__: if __debug__:
start_date = datetime.datetime.now() start_date = datetime.now()
# launch scanning. # launch scanning.
get_children() get_children()
if __debug__: 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() db_connection.close()
return return
def __remove_branch_form_db(self, root_id): def __remove_branch_form_db(self, root_id):
parent_ids = [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() ids = self.db_cursor.fetchall()
def get_children(fid): def get_children(fid):
parent_ids.append(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() res = self.db_cursor.fetchall()
for i in res: for i in res:
get_children(i[0]) get_children(i[0])
@@ -522,8 +652,10 @@ class MainModel(ModelMT):
for c in parent_ids: for c in parent_ids:
yield (c,) yield (c,)
self.db_cursor.executemany("delete from files where type = 1 and parent_id = ?", generator()) sql = """delete from files where type = 1 and parent_id = ?"""
self.db_cursor.executemany("delete from files where id = ?",generator()) self.db_cursor.executemany(sql, generator())
sql = """delete from files where id = ?"""
self.db_cursor.executemany(sql, generator())
self.db_connection.commit() self.db_connection.commit()
return return
@@ -531,36 +663,40 @@ class MainModel(ModelMT):
def __append_added_volume(self): def __append_added_volume(self):
"""append branch from DB to existing tree model""" """append branch from DB to existing tree model"""
#connect #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) detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
db_cursor = db_connection.cursor() 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() data = db_cursor.fetchall()
def get_children(parent_id = 1, iterator = None): def get_children(parent_id = 1, iterator = None):
"""fetch all children and place them in model""" """fetch all children and place them in model"""
for row in data: for row in data:
if row[1] == parent_id: if row[1] == parent_id:
myiter = self.discsTree.insert_before(iterator,None) myiter = self.discs_tree.insert_before(iterator, None)
self.discsTree.set_value(myiter,0,row[0]) self.discs_tree.set_value(myiter, 0, row[0])
self.discsTree.set_value(myiter,1,row[2]) self.discs_tree.set_value(myiter, 1, row[2])
self.discsTree.set_value(myiter,3,row[1]) self.discs_tree.set_value(myiter, 3, row[1])
get_children(row[0], myiter) get_children(row[0], myiter)
# isroot? # isroot?
if iterator == None: if iterator == None:
self.discsTree.set_value(myiter,2,gtk.STOCK_CDROM) self.discs_tree.set_value(myiter, 2, gtk.STOCK_CDROM)
else: else:
self.discsTree.set_value(myiter,2,gtk.STOCK_DIRECTORY) self.discs_tree.set_value(myiter, 2,
gtk.STOCK_DIRECTORY)
return return
if __debug__: if __debug__:
start_date = datetime.datetime.now() start_date = datetime.now()
# launch scanning. # launch scanning.
get_children() get_children()
if __debug__: 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() db_connection.close()
return 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 os.path
import utils.globals import utils.globals
from gtkmvc import View
from v_details import DetailsView
class MainView(View): class MainView(View):
"""This handles only the graphical representation of the """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") GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
def __init__(self, ctrl): def __init__(self, ctrl):
View.__init__(self, ctrl, self.GLADE) View.__init__(self, ctrl, self.GLADE)
self.details = None
return 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 pass # end of class