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

Clean up old files. Repair unit tests

This commit is contained in:
2016-08-19 18:54:22 +02:00
parent 4eac6820c5
commit 63f2d6fc11
83 changed files with 558 additions and 17194 deletions

View File

@@ -1,299 +0,0 @@
#!/usr/bin/env python
"""
Project: pyGTKtalog
Description: convert db created with v.1.x into v.2.x
Type: tool
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-08-14
"""
from datetime import datetime
from sqlite3 import dbapi2 as sqlite
from sqlite3 import OperationalError
from tempfile import mkstemp
import bz2
import errno
import os
import shutil
import sys
from sqlalchemy.dialects.sqlite import DATETIME
from pygtktalog.misc import mk_paths, calculate_image_path
PATH1 = os.path.expanduser("~/.pygtktalog/images")
PATH2 = os.path.expanduser("~/.pygtktalog/imgs2")
def mkdir_p(path):
"""Make directories recurively, like 'mkdir -p' command"""
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
return path
def get_images_path(cur):
"""
Calculate the data dir in order:
- config table
- old default path
- new default path
return first, which contain provided image filename
"""
image = cur.execute("select filename from images limit 1").fetchone()
if image and image[0]:
image = image[0]
try:
result = cur.execute("select value from config where "
"key='image_path'").fetchone()
if (result and result[0] and
os.path.exists(os.path.join(result[0].encode("utf-8"),
image.encode("utf-8")))):
return result[0]
except OperationalError:
# no such table like config. proceed.
pass
for path in (PATH1, PATH2):
if os.path.exists(os.path.join(path, image)):
return path
return None
def get_path(cur, image):
"""
Calculate the data dir in order:
- config table
- old default path
- new default path
return first, which contain provided image filename
"""
try:
result = cur.execute("select value from config where "
"key='image_path'").fetchone()
if (result and result[0] and
os.path.exists(os.path.join(result[0].encode("utf-8"),
image.encode("utf-8")))):
return result[0]
except OperationalError:
pass
for path in (PATH1, PATH2):
if os.path.exists(os.path.join(path, image)):
return path
return None
def old_style_image_handle(fname, source_dir, dest_dir):
"""
Deal with old-style images in DB. There is a flat list under
~/.pygtktalog/images/ directory, which should be converted to nested
structure.
"""
partial_path = mk_paths(os.path.join(source_dir, fname), dest_dir)
dest_file = os.path.join(dest_dir, *partial_path)
dest_thumb = os.path.join(dest_dir, *partial_path) + "_t"
shutil.copy(os.path.join(source_dir, fname), dest_file)
shutil.copy(os.path.join(source_dir, fname + "_t"), dest_thumb)
with open("log.txt", "a") as fobj:
fobj.write(os.path.join(fname) + "\n")
fobj.write(os.path.join(fname + "_t\n"))
return os.path.join(*partial_path), os.path.join(*partial_path) + "_t"
def new_style_image_handle(partial_path, source_dir, dest_dir):
"""
Deal with old-style images in DB. In the early version directory was
hardcoded to ~/.pygtktalog/imgs2/, and all the needed files (with the
paths) should be copied to the new place.
params:
partial_path: string holding the relative path to file, for example
`de/ad/be/ef.jpg'
source_dir: path, where at the moment image file resides. Might be the
full path, like `/home/user/.pygtktalog/imgs2`
dest_dir: path (might be relative or absolute), where we want to put
the images (i.e. `../foo-images')
"""
dest_dir = mkdir_p(os.path.join(dest_dir, os.path.dirname(partial_path)))
base, ext = os.path.splitext(partial_path)
thumb = os.path.join(source_dir, "".join([base, "_t", ext]))
filename = os.path.join(source_dir, partial_path)
shutil.copy(filename, dest_dir)
shutil.copy(thumb, dest_dir)
def copy_images_to_destination(cursor, image_path, dest):
"""Copy images to dest directory and correct the db entry, if needed"""
sql = "select id, filename from images"
update = "update images set filename=? where id=?"
t_select = "select id from thumbnails where filename=?"
t_update = "update thumbnails set filename=? where id=?"
count = -1
for count, (id_, filename) in enumerate(cursor.execute(sql).fetchall()):
if not image_path:
image_path = get_path(cursor, filename)
if not image_path:
raise OSError("Image file '%s' not found under data "
"directory, aborting" % filename)
if image_path == PATH1:
# old style filenames. Flat list.
fname, tname = old_style_image_handle(filename, image_path, dest)
cursor.execute(update, (fname, id_))
for (thumb_id,) in cursor.execute(t_select,
(filename,)).fetchall():
cursor.execute(t_update, (tname, thumb_id))
else:
# new style filenames. nested dirs
new_style_image_handle(filename, image_path, dest)
if count > 0:
print "copied %d files" % (count + 1)
def create_temporary_db_file():
"""create temporary db file"""
file_descriptor, fname = mkstemp()
os.close(file_descriptor)
return fname
def connect_to_db(filename):
"""initialize db connection and store it in class attributes"""
db_connection = sqlite.connect(filename, detect_types=
sqlite.PARSE_DECLTYPES |
sqlite.PARSE_COLNAMES)
db_cursor = db_connection.cursor()
return db_connection, db_cursor
def opendb(filename=None):
"""try to open db file"""
db_tmp_path = create_temporary_db_file()
try:
test_file = open(filename).read(15)
except IOError:
os.unlink(db_tmp_path)
return False
if test_file == "SQLite format 3":
db_tmp = open(db_tmp_path, "wb")
db_tmp.write(open(filename).read())
db_tmp.close()
elif test_file[0:10] == "BZh91AY&SY":
open_file = bz2.BZ2File(filename)
try:
curdb = open(db_tmp_path, "w")
curdb.write(open_file.read())
curdb.close()
open_file.close()
except IOError:
# file is not bz2
os.unlink(db_tmp_path)
return False
else:
os.unlink(db_tmp_path)
return False
return connect_to_db(db_tmp_path), db_tmp_path
def _update_dates(cursor, select_sql, update_sql):
"""update date format - worker function"""
for id_, date in cursor.execute(select_sql).fetchall():
try:
date = int(date)
except ValueError:
# most probably there is no need for updating this record.
continue
except TypeError:
date = 0
if date > 0:
val = DATETIME().bind_processor(None)(datetime.fromtimestamp(date))
else:
val = None
cursor.execute(update_sql, (val, id_))
def update_dates(cursor):
"""Update date format from plain int to datetime object"""
_update_dates(cursor,
"select id, date from files",
"update files set date=? where id=?")
_update_dates(cursor,
"select id, date from gthumb",
"update gthumb set date=? where id=?")
def main():
"""Main logic"""
if len(sys.argv) not in (4, 3):
print("usage: %s source_dbfile destination_dbfile [image_dir]\n"
"where image dir is a name where to put images. same name with"
"'_images' suffix by default"
% sys.argv[0])
exit()
if len(sys.argv) == 4:
source_dbfile, destination_dbfile, image_dir = sys.argv[1:]
else:
source_dbfile, destination_dbfile = sys.argv[1:]
image_dir = ":same_as_db:"
result = opendb(source_dbfile)
if not result:
print("unable to open src db file")
exit()
(connection, cursor), temporary_database_filename = result
cursor.close()
connection.close()
shutil.copy(temporary_database_filename, destination_dbfile)
os.unlink(temporary_database_filename)
connection = sqlite.connect(destination_dbfile)
cursor = connection.cursor()
if cursor.execute("select name from sqlite_master where type='table' "
"and name='table_name'").fetchone() is None:
cursor.execute("CREATE TABLE 'config' (\n\t'id'\tINTEGER NOT NULL,\n"
"\t'key'\tTEXT,\n\t'value'\tTEXT,\n\tPRIMARY "
"KEY(id)\n)")
if cursor.execute("select value from config where "
"key='image_path'").fetchone() is None:
cursor.execute("insert into config(key, value) "
"values('image_path', ?)", (image_dir,))
else:
cursor.execute("update config set value=? where key='image_path'",
(image_dir,))
if image_dir == ":same_as_db:":
db_fname = os.path.basename(destination_dbfile)
base, dummy = os.path.splitext(db_fname)
image_dir_path = os.path.join(os.path.dirname(destination_dbfile),
base + "_images")
else:
image_dir_path = image_dir
calculate_image_path(image_dir_path, True)
update_dates(cursor)
old_image_path = get_images_path(cursor)
copy_images_to_destination(cursor, old_image_path, image_dir_path)
connection.commit()
cursor.close()
connection.close()
if __name__ == "__main__":
main()

View File

@@ -1,375 +0,0 @@
#
# pygtktalog Language File
#
msgid ""
msgstr ""
"Project-Id-Version: pygtktalog\n"
"POT-Creation-Date: 2009-08-26 22:10:33.892815\n"
"Last-Translator: Roman Dobosz<gryf73@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: utf-8\n"
"Generated-By: slightly modified generate_pot.py\n"
#: pygtktalog/views/glade/main.glade.h:1
msgid "Add CD"
msgstr "Dodaj CD"
#: pygtktalog/views/glade/main.glade.h:2
msgid "Add CD/DVD to catalog"
msgstr "Dodaj CD/DVD do katalogu"
#: pygtktalog/views/glade/main.glade.h:3
msgid "Add Dir"
msgstr "Dodaj katalog"
#: pygtktalog/views/glade/main.glade.h:4
msgid "Add _CD/DVD"
msgstr "Dodaj _Cd/DVD"
#: pygtktalog/views/glade/main.glade.h:5
msgid "Add _Directory"
msgstr "Dodaj kata_log"
#: pygtktalog/views/glade/main.glade.h:6
#: pygtktalog/views/glade/files.glade.h:1
msgid "Add _Images"
msgstr "Dodaj _obrazy"
#: pygtktalog/views/glade/main.glade.h:7
#: pygtktalog/views/glade/files.glade.h:2
msgid "Add _Thumbnail"
msgstr "Dodaj miniaturę"
#: pygtktalog/views/glade/main.glade.h:8
#: pygtktalog/views/glade/files.glade.h:3
msgid ""
"Add images to file. If file have no thumbnail,\n"
"thumbnail from first image will be generated."
msgstr ""
"Dodanie obrazów do pliku. Jeśli plik nie posiada miniatury,\n"
"zostanie wygenerowana miniatura z pierwszego obrazu."
#: pygtktalog/views/glade/main.glade.h:10
#: pygtktalog/views/glade/main.glade.h:6
msgid "Cancel"
msgstr "Analuj"
#: pygtktalog/views/glade/main.glade.h:11
#: pygtktalog/views/glade/main.glade.h:7
msgid "Catalog _statistics"
msgstr "_Statystyka katalogu"
#: pygtktalog/views/glade/main.glade.h:12
#: pygtktalog/views/glade/discs.glade.h:1
msgid "Collapse all nodes"
msgstr "Zwiń wszystkie gałęzie"
#: pygtktalog/views/glade/main.glade.h:13
#: pygtktalog/views/glade/main.glade.h:8
msgid "Create new catalog"
msgstr "Utwórz nowy katalog"
#: pygtktalog/views/glade/main.glade.h:14
#: pygtktalog/views/glade/main.glade.h:9
msgid "Delete all images"
msgstr "Usuń wszystkie obrazy"
#: pygtktalog/views/glade/main.glade.h:15
#: pygtktalog/views/glade/main.glade.h:10
msgid "Delete all thumbnals"
msgstr "Usuń wszystkie miniatury"
#: pygtktalog/views/glade/main.glade.h:16
#: pygtktalog/views/glade/main.glade.h:11
msgid "Delete tag"
msgstr "Usuń tag"
#: pygtktalog/views/glade/main.glade.h:17
#: pygtktalog/views/glade/main.glade.h:12
msgid "Deletes all images from files in current colection"
msgstr "Usuwa wszystkie obrazy z plików w bieżącej kolekcji"
#: pygtktalog/views/glade/main.glade.h:18
#: pygtktalog/views/glade/main.glade.h:13
msgid "Discs"
msgstr "Dyski"
#: pygtktalog/views/glade/main.glade.h:19
#: pygtktalog/views/glade/details.glade.h:1
msgid "Double click to open image"
msgstr "Dwukrotne kliknięcie otwiera obraz"
#: pygtktalog/views/glade/main.glade.h:20
#: pygtktalog/views/glade/details.glade.h:2
msgid "EXIF"
msgstr "EXIF"
#: pygtktalog/views/glade/main.glade.h:21
#: pygtktalog/views/glade/discs.glade.h:2
msgid "Expand all nodes"
msgstr "Rozwiń wszystkie gałęzie"
#: pygtktalog/views/glade/main.glade.h:22
#: pygtktalog/views/glade/main.glade.h:14
msgid "Export"
msgstr "Eksport"
#: pygtktalog/views/glade/main.glade.h:23
#: pygtktalog/views/glade/details.glade.h:3
msgid "File info"
msgstr "Informacje o pliku"
#: pygtktalog/views/glade/main.glade.h:24
#: pygtktalog/views/glade/main.glade.h:15
msgid "Find file"
msgstr "Znajdź plik"
#: pygtktalog/views/glade/main.glade.h:25
#: pygtktalog/views/glade/details.glade.h:4
msgid "Images"
msgstr "Obrazy"
#: pygtktalog/views/glade/main.glade.h:26
#: pygtktalog/views/glade/main.glade.h:16
msgid "Import"
msgstr "Import"
#: pygtktalog/views/glade/main.glade.h:27
#: pygtktalog/views/glade/main.glade.h:17
msgid "Open catalog file"
msgstr "Otwórz plik katalogu"
#: pygtktalog/views/glade/main.glade.h:28
#: pygtktalog/views/glade/main.glade.h:18
msgid "Quit pyGTKtalog"
msgstr "Zakończ pyGTKtalog"
#: pygtktalog/views/glade/main.glade.h:29
#: pygtktalog/views/glade/files.glade.h:5
msgid "Re_move Thumbnail"
msgstr "Usuń miniaturę"
#: pygtktalog/views/glade/main.glade.h:30
#: pygtktalog/views/glade/main.glade.h:19
msgid "Recent files"
msgstr "Ostatnio używane pliki"
#: pygtktalog/views/glade/main.glade.h:31
#: pygtktalog/views/glade/files.glade.h:6
msgid "Rem_ove All Images"
msgstr "Usuń wszystkie obrazy"
#: pygtktalog/views/glade/main.glade.h:32
#: pygtktalog/views/glade/files.glade.h:7
msgid "Remo_ve tag"
msgstr "Usuń tag"
#: pygtktalog/views/glade/main.glade.h:33
#: pygtktalog/views/glade/main.glade.h:20
msgid "Save all images..."
msgstr "Zapisz wszytkie obrazy..."
#: pygtktalog/views/glade/main.glade.h:34
#: pygtktalog/views/glade/main.glade.h:21
msgid "Save catalog"
msgstr "Zapisz katalog"
#: pygtktalog/views/glade/main.glade.h:35
#: pygtktalog/views/glade/main.glade.h:22
msgid "Set as _thumbnail"
msgstr "Ustaw jako miniaturę"
#: pygtktalog/views/glade/main.glade.h:36
#: pygtktalog/views/glade/main.glade.h:23
msgid "Status bar"
msgstr "Pasek stanu"
#: pygtktalog/views/glade/main.glade.h:37
#: pygtktalog/views/glade/main.glade.h:24
msgid "Tags"
msgstr "Tagi"
#: pygtktalog/views/glade/main.glade.h:38
#: pygtktalog/views/glade/main.glade.h:25
msgid "Toolbar"
msgstr "Pasek narzędzi"
#: pygtktalog/views/glade/main.glade.h:39
#: pygtktalog/views/glade/main.glade.h:26
msgid "_Add images"
msgstr "Dodaj obrazy"
#: pygtktalog/views/glade/main.glade.h:40
#: pygtktalog/views/glade/files.glade.h:8
msgid "_Add tag"
msgstr "_Dodaj tag"
#: pygtktalog/views/glade/main.glade.h:41
#: pygtktalog/views/glade/main.glade.h:27
msgid "_Catalog"
msgstr "_Katalog"
#: pygtktalog/views/glade/main.glade.h:42
#: pygtktalog/views/glade/discs.glade.h:3
msgid "_Collapse all"
msgstr "_Zwiń wszystko"
#: pygtktalog/views/glade/main.glade.h:43
#: pygtktalog/views/glade/discs.glade.h:4
#: pygtktalog/views/glade/files.glade.h:9
msgid "_Delete"
msgstr "_Usuń"
#: pygtktalog/views/glade/main.glade.h:44
#: pygtktalog/views/glade/main.glade.h:28
msgid "_Delete images"
msgstr "Usuń obrazy"
#: pygtktalog/views/glade/main.glade.h:45
#: pygtktalog/views/glade/main.glade.h:29
#: pygtktalog/views/glade/files.glade.h:10
#: pygtktalog/views/glade/test.glade.h:1
msgid "_Edit"
msgstr "_Edycja"
#: pygtktalog/views/glade/main.glade.h:46
#: pygtktalog/views/glade/discs.glade.h:5
msgid "_Expand all"
msgstr "_Rozwiń wszystko"
#: pygtktalog/views/glade/main.glade.h:47 pygtktalog/views/main_menu.py:19
#: pygtktalog/views/glade/main.glade.h:30
#: pygtktalog/views/glade/test.glade.h:2
msgid "_File"
msgstr "_Plik"
#: pygtktalog/views/glade/main.glade.h:48
#: pygtktalog/views/glade/main.glade.h:31
#: pygtktalog/views/glade/test.glade.h:3
msgid "_Help"
msgstr "_Pomoc"
#: pygtktalog/views/glade/main.glade.h:49
#: pygtktalog/views/glade/main.glade.h:32
msgid "_Remove Thumbnail"
msgstr "Usuń miniaturę"
#: pygtktalog/views/glade/main.glade.h:50
#: pygtktalog/views/glade/discs.glade.h:6
#: pygtktalog/views/glade/files.glade.h:11
msgid "_Rename"
msgstr "_Zmień nazwę"
#: pygtktalog/views/glade/main.glade.h:51
#: pygtktalog/views/glade/main.glade.h:33
msgid "_Save images to..."
msgstr "Zapisz obrazy do..."
#: pygtktalog/views/glade/main.glade.h:52
#: pygtktalog/views/glade/discs.glade.h:7
msgid "_Statistics"
msgstr "_Statystyka"
#: pygtktalog/views/glade/main.glade.h:53
#: pygtktalog/views/glade/discs.glade.h:8
msgid "_Update"
msgstr "_Uaktualnij"
#: pygtktalog/views/glade/main.glade.h:54
#: pygtktalog/views/glade/main.glade.h:34
#: pygtktalog/views/glade/test.glade.h:4
msgid "_View"
msgstr "_Widok"
#: pygtktalog/views/glade/main.glade.h:55
msgid "gtk-clear"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:56
#: pygtktalog/views/glade/main.glade.h:35
msgid "pyGTKtalog"
msgstr "pyGTKtalog"
#: pygtktalog/views/glade/main.glade.h:57
#: pygtktalog/views/glade/main.glade.h:36
msgid "pyGTKtalog - Image"
msgstr "pyGTKtalog - Obraz"
#: pygtktalog/controllers/main.py:47
msgid "Do you really want to quit?"
msgstr "Czy na pewno chcesz zakończyć?"
#: pygtktalog/controllers/main.py:48
msgid "Current database is not saved, any changes will be lost."
msgstr "Bieżący katalog nie jest zapisany, wszelkie zmiany zostaną utracone."
#: pygtktalog/controllers/main.py:48 pygtktalog/controllers/main.py:49
msgid "Quit application"
msgstr "Zakończ aplikację"
#: pygtktalog/models/main.py:16 pygtktalog/models/main.py:31
msgid "Idle"
msgstr "Bezczynny"
#: pygtktalog/controllers/files.py:33
#, fuzzy
msgid "Disc"
msgstr "Dyski"
#: pygtktalog/controllers/files.py:39
#, fuzzy
msgid "Filename"
msgstr "_Zmień nazwę"
#: pygtktalog/controllers/files.py:50
msgid "Path"
msgstr ""
#: pygtktalog/controllers/files.py:56
msgid "Size"
msgstr ""
#: pygtktalog/controllers/files.py:61
msgid "Date"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:5
msgid "gtk-about"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:6
msgid "gtk-copy"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:7
msgid "gtk-cut"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:8
msgid "gtk-delete"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:9
msgid "gtk-new"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:10
msgid "gtk-open"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:11
msgid "gtk-paste"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:12
msgid "gtk-quit"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:13
msgid "gtk-save"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:14
msgid "gtk-save-as"
msgstr ""

View File

@@ -1,370 +0,0 @@
#
# pygtktalog Language File
#
msgid ""
msgstr ""
"Project-Id-Version: pygtktalog\n"
"POT-Creation-Date: 2009-08-26 22:10:33.892815\n"
"Last-Translator: Roman Dobosz<gryf73@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: utf-8\n"
#: pygtktalog/views/glade/main.glade.h:1
msgid "Add CD"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:2
msgid "Add CD/DVD to catalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:3
msgid "Add Dir"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:4
msgid "Add _CD/DVD"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:5
msgid "Add _Directory"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:6
#: pygtktalog/views/glade/files.glade.h:1
msgid "Add _Images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:7
#: pygtktalog/views/glade/files.glade.h:2
msgid "Add _Thumbnail"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:8
#: pygtktalog/views/glade/files.glade.h:3
msgid ""
"Add images to file. If file have no thumbnail,\n"
"thumbnail from first image will be generated."
msgstr ""
#: pygtktalog/views/glade/main.glade.h:10
#: pygtktalog/views/glade/main.glade.h:6
msgid "Cancel"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:11
#: pygtktalog/views/glade/main.glade.h:7
msgid "Catalog _statistics"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:12
#: pygtktalog/views/glade/discs.glade.h:1
msgid "Collapse all nodes"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:13
#: pygtktalog/views/glade/main.glade.h:8
msgid "Create new catalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:14
#: pygtktalog/views/glade/main.glade.h:9
msgid "Delete all images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:15
#: pygtktalog/views/glade/main.glade.h:10
msgid "Delete all thumbnals"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:16
#: pygtktalog/views/glade/main.glade.h:11
msgid "Delete tag"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:17
#: pygtktalog/views/glade/main.glade.h:12
msgid "Deletes all images from files in current colection"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:18
#: pygtktalog/views/glade/main.glade.h:13
msgid "Discs"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:19
#: pygtktalog/views/glade/details.glade.h:1
msgid "Double click to open image"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:20
#: pygtktalog/views/glade/details.glade.h:2
msgid "EXIF"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:21
#: pygtktalog/views/glade/discs.glade.h:2
msgid "Expand all nodes"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:22
#: pygtktalog/views/glade/main.glade.h:14
msgid "Export"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:23
#: pygtktalog/views/glade/details.glade.h:3
msgid "File info"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:24
#: pygtktalog/views/glade/main.glade.h:15
msgid "Find file"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:25
#: pygtktalog/views/glade/details.glade.h:4
msgid "Images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:26
#: pygtktalog/views/glade/main.glade.h:16
msgid "Import"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:27
#: pygtktalog/views/glade/main.glade.h:17
msgid "Open catalog file"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:28
#: pygtktalog/views/glade/main.glade.h:18
msgid "Quit pyGTKtalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:29
#: pygtktalog/views/glade/files.glade.h:5
msgid "Re_move Thumbnail"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:30
#: pygtktalog/views/glade/main.glade.h:19
msgid "Recent files"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:31
#: pygtktalog/views/glade/files.glade.h:6
msgid "Rem_ove All Images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:32
#: pygtktalog/views/glade/files.glade.h:7
msgid "Remo_ve tag"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:33
#: pygtktalog/views/glade/main.glade.h:20
msgid "Save all images..."
msgstr ""
#: pygtktalog/views/glade/main.glade.h:34
#: pygtktalog/views/glade/main.glade.h:21
msgid "Save catalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:35
#: pygtktalog/views/glade/main.glade.h:22
msgid "Set as _thumbnail"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:36
#: pygtktalog/views/glade/main.glade.h:23
msgid "Status bar"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:37
#: pygtktalog/views/glade/main.glade.h:24
msgid "Tags"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:38
#: pygtktalog/views/glade/main.glade.h:25
msgid "Toolbar"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:39
#: pygtktalog/views/glade/main.glade.h:26
msgid "_Add images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:40
#: pygtktalog/views/glade/files.glade.h:8
msgid "_Add tag"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:41
#: pygtktalog/views/glade/main.glade.h:27
msgid "_Catalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:42
#: pygtktalog/views/glade/discs.glade.h:3
msgid "_Collapse all"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:43
#: pygtktalog/views/glade/discs.glade.h:4
#: pygtktalog/views/glade/files.glade.h:9
msgid "_Delete"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:44
#: pygtktalog/views/glade/main.glade.h:28
msgid "_Delete images"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:45
#: pygtktalog/views/glade/main.glade.h:29
#: pygtktalog/views/glade/files.glade.h:10
#: pygtktalog/views/glade/test.glade.h:1
msgid "_Edit"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:46
#: pygtktalog/views/glade/discs.glade.h:5
msgid "_Expand all"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:47 pygtktalog/views/main_menu.py:19
#: pygtktalog/views/glade/main.glade.h:30
#: pygtktalog/views/glade/test.glade.h:2
msgid "_File"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:48
#: pygtktalog/views/glade/main.glade.h:31
#: pygtktalog/views/glade/test.glade.h:3
msgid "_Help"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:49
#: pygtktalog/views/glade/main.glade.h:32
msgid "_Remove Thumbnail"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:50
#: pygtktalog/views/glade/discs.glade.h:6
#: pygtktalog/views/glade/files.glade.h:11
msgid "_Rename"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:51
#: pygtktalog/views/glade/main.glade.h:33
msgid "_Save images to..."
msgstr ""
#: pygtktalog/views/glade/main.glade.h:52
#: pygtktalog/views/glade/discs.glade.h:7
msgid "_Statistics"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:53
#: pygtktalog/views/glade/discs.glade.h:8
msgid "_Update"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:54
#: pygtktalog/views/glade/main.glade.h:34
#: pygtktalog/views/glade/test.glade.h:4
msgid "_View"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:55
msgid "gtk-clear"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:56
#: pygtktalog/views/glade/main.glade.h:35
msgid "pyGTKtalog"
msgstr ""
#: pygtktalog/views/glade/main.glade.h:57
#: pygtktalog/views/glade/main.glade.h:36
msgid "pyGTKtalog - Image"
msgstr ""
#: pygtktalog/controllers/main.py:47
msgid "Do you really want to quit?"
msgstr ""
#: pygtktalog/controllers/main.py:48
msgid "Current database is not saved, any changes will be lost."
msgstr ""
#: pygtktalog/controllers/main.py:48 pygtktalog/controllers/main.py:49
msgid "Quit application"
msgstr ""
#: pygtktalog/models/main.py:16 pygtktalog/models/main.py:31
msgid "Idle"
msgstr ""
#: pygtktalog/controllers/files.py:33
msgid "Disc"
msgstr ""
#: pygtktalog/controllers/files.py:39
msgid "Filename"
msgstr ""
#: pygtktalog/controllers/files.py:50
msgid "Path"
msgstr ""
#: pygtktalog/controllers/files.py:56
msgid "Size"
msgstr ""
#: pygtktalog/controllers/files.py:61
msgid "Date"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:5
msgid "gtk-about"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:6
msgid "gtk-copy"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:7
msgid "gtk-cut"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:8
msgid "gtk-delete"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:9
msgid "gtk-new"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:10
msgid "gtk-open"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:11
msgid "gtk-paste"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:12
msgid "gtk-quit"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:13
msgid "gtk-save"
msgstr ""
#: pygtktalog/views/glade/test.glade.h:14
msgid "gtk-save-as"
msgstr ""

View File

@@ -121,7 +121,7 @@ def distclean():
shutil.rmtree(dirname, ignore_errors=True)
print "removed directory", dirname
for filename in ['paver-minilib.zip', 'setup.py', 'test/.coverage']:
for filename in ['paver-minilib.zip', 'setup.py', 'tests/.coverage']:
if os.path.exists(filename):
os.unlink(filename)
print "deleted", filename
@@ -203,7 +203,7 @@ if HAVE_LINT:
@cmdopts([('coverage', 'c', 'display coverage information')])
def test(options):
"""run unit tests"""
cmd = "PYTHONPATH=%s:$PYTHONPATH nosetests -w test" % _setup_env()
cmd = "PYTHONPATH=%s:$PYTHONPATH nosetests -w tests" % _setup_env()
if hasattr(options.test, 'coverage'):
cmd += " --with-coverage --cover-package pygtktalog"
os.system(cmd)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -1,18 +0,0 @@
"""
Project: pyGTKtalog
Description: Controller for Details NoteBook
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-08-30
"""
from gtkmvc import Controller
class DetailsController(Controller):
"""
Controller for details NoteBook.
"""
def register_view(self, view):
"""Default view registration stuff"""
pass

View File

@@ -1,214 +0,0 @@
"""
Project: pyGTKtalog
Description: Controller for Discs TreeView
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-08-30
"""
import gtk
from gtkmvc import Controller
from pygtktalog.logger import get_logger
LOG = get_logger("discs ctrl")
class DiscsController(Controller):
"""
Controller for discs TreeView
"""
def __init__(self, model, view):
"""
Initialize DiscsController
"""
LOG.debug(self.__init__.__doc__.strip())
Controller.__init__(self, model, view)
self.discs_model = self.model.discs.discs
def register_view(self, view):
"""
Do DiscTree registration
"""
LOG.debug(self.register_view.__doc__.strip())
view['discs'].set_model(self.discs_model)
# register observers
self.model.discs.register_observer(self)
# connect signals to popup menu - framework somehow omits automatic
# signal connection for subviews which are not included to widgets
# tree
sigs = {'expand_all': ('activate', self.on_expand_all_activate),
'collapse_all': ('activate', self.on_collapse_all_activate),
'update': ('activate', self.on_update_activate),
'rename': ('activate', self.on_rename_activate),
'delete': ('activate', self.on_delete_activate),
'statistics': ('activate', self.on_statistics_activate)}
for signal in sigs:
view.menu[signal].connect(sigs[signal][0], sigs[signal][1])
col = gtk.TreeViewColumn('discs_column')
cellpb = gtk.CellRendererPixbuf()
cell = gtk.CellRendererText()
# make cell text editabe
cell.set_property('editable', True)
cell.connect('edited', self.on_editing_done, self.discs_model)
# TODO: find a way how to disable default return keypress on editable
# fields
col.pack_start(cellpb, False)
col.pack_start(cell, True)
col.set_attributes(cellpb, stock_id=2)
col.set_attributes(cell, text=1)
view['discs'].append_column(col)
view['discs'].show()
# treeview signals
def on_discs_button_press_event(self, treeview, event):
"""
Handle right click on discs treeview - show popup menu.
"""
LOG.debug(self.on_discs_button_press_event.__doc__.strip())
pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
if event.button == 3:
if pathinfo:
path = pathinfo[0]
# Make sure, that there is selected row
sel = treeview.get_selection()
sel.unselect_all()
sel.select_path(path)
self._popup_menu(sel, event, event.button)
else:
self._popup_menu(None, event, event.button)
return True
def on_discs_cursor_changed(self, treeview):
"""
Show files on right treeview, after clicking the left disc treeview.
"""
LOG.debug(self.on_discs_cursor_changed.__doc__.strip())
selection = treeview.get_selection()
path = selection.get_selected_rows()[1][0]
self.model.files.refresh(self.discs_model.get_value(\
self.discs_model.get_iter(path), 0))
def on_discs_key_release_event(self, treeview, event):
"""
Watch for specific keys
"""
LOG.debug(self.on_discs_key_release_event.__doc__.strip())
if gtk.gdk.keyval_name(event.keyval) == 'Menu':
LOG.debug('Menu key pressed')
self._popup_menu(treeview.get_selection(), event, 0)
return True
return False
def on_discs_row_activated(self, treeview, path, treecolumn):
"""
If possible, expand or collapse branch of discs tree
"""
LOG.debug(self.on_discs_row_activated.__doc__.strip())
if treeview.row_expanded(path):
treeview.collapse_row(path)
else:
treeview.expand_row(path, False)
def on_editing_done(self, cell, path, new_text, model):
"""
Store changed filename text
"""
LOG.debug(self.on_editing_done.__doc__.strip())
model[path][0].filename = new_text
model[path][1] = new_text
return
# popup menu signals
def on_expand_all_activate(self, menu_item):
"""
Expand all
"""
LOG.debug(self.on_expand_all_activate.__doc__.strip())
self.view['discs'].expand_all()
def on_collapse_all_activate(self, menu_item):
"""
Collapse all
"""
LOG.debug(self.on_collapse_all_activate.__doc__.strip())
self.view['discs'].collapse_all()
def on_update_activate(self, menu_item):
"""
Trigger update specified tree entry
"""
LOG.debug(self.on_update_activate.__doc__.strip())
raise NotImplementedError
def on_rename_activate(self, menu_item):
"""
Rename disk or directory
"""
LOG.debug(self.on_rename_activate.__doc__.strip())
treeview = self.view['discs']
selection = treeview.get_selection()
path = selection.get_selected_rows()[1][0]
treeview.set_cursor(path, treeview.get_column(0), start_editing=True)
def on_delete_activate(self, menu_item):
"""
Delete disk or directory from catalog
"""
LOG.debug(self.on_delete_activate.__doc__.strip())
raise NotImplementedError
def on_statistics_activate(self, menu_item):
"""
Show statistics for selected item
"""
LOG.debug(self.on_statistics_activate.__doc__.strip())
raise NotImplementedError
# observable properties
def property_currentdir_value_change(self, model, old, new):
"""
Change of a current dir signalized by other controllers/models
"""
LOG.debug(self.property_currentdir_value_change.__doc__.strip())
self._set_cursor_to_obj_position(new)
# private methods
def _popup_menu(self, selection, event, button):
"""
Popup menu for discs treeview. Gather information from discs model,
and trigger menu popup.
"""
LOG.debug(self._popup_menu.__doc__.strip())
if selection is None:
self.view.menu.set_menu_items_sensitivity(False)
else:
model, list_of_paths = selection.get_selected_rows()
for path in list_of_paths:
self.view.menu.set_menu_items_sensitivity(True)
self.view.menu.set_update_sensitivity(model.get_value(\
model.get_iter(path), 0).parent_id == 1)
self.view.menu['discs_popup'].popup(None, None, None,
button, event.time)
def _set_cursor_to_obj_position(self, obj):
"""
Set cursor/focus to specified object postion in Discs treeview.
"""
path = self.model.discs.find_path(obj)
self.view['discs'].expand_to_path(path)
self.view['discs'].set_cursor(path)

View File

@@ -1,282 +0,0 @@
"""
Project: pyGTKtalog
Description: Controller for Files TreeView
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-08-30
"""
import gtk
from gtkmvc import Controller
from pygtktalog.pygtkutils import get_tv_item_under_cursor
from pygtktalog.logger import get_logger
LOG = get_logger("files ctrl")
class FilesController(Controller):
"""
Controller for files TreeView list.
"""
def __init__(self, model, view):
"""
FilesController initialization
"""
Controller.__init__(self, model, view)
self.DND_TARGETS = [('files_tags', 0, 69)]
self.files_model = self.model.files
def register_view(self, view):
"""
Register view, and setup columns for files treeview
"""
view['files'].set_model(self.files_model.files)
sigs = {"add_tag": ("activate", self.on_add_tag1_activate),
"delete_tag": ("activate", self.on_delete_tag_activate),
"add_thumb": ("activate", self.on_add_thumb1_activate),
"remove_thumb": ("activate", self.on_remove_thumb1_activate),
"add_image": ("activate", self.on_add_image1_activate),
"remove_image": ("activate", self.on_remove_image1_activate),
"edit": ("activate", self.on_edit2_activate),
"delete": ("activate", self.on_delete3_activate),
"rename": ("activate", self.on_rename2_activate)}
for signal in sigs:
view.menu[signal].connect(sigs[signal][0], sigs[signal][1])
view['files'].get_selection().set_mode(gtk.SELECTION_MULTIPLE)
col = gtk.TreeViewColumn(_('Disc'), gtk.CellRendererText(), text=1)
col.set_sort_column_id(1)
col.set_resizable(True)
col.set_visible(False)
view['files'].append_column(col)
col = gtk.TreeViewColumn(_('Filename'))
cellpb = gtk.CellRendererPixbuf()
cell = gtk.CellRendererText()
col.pack_start(cellpb, False)
col.pack_start(cell, True)
col.set_attributes(cellpb, stock_id=7)
col.set_attributes(cell, text=2)
col.set_sort_column_id(2)
col.set_resizable(True)
self.view['files'].append_column(col)
col = gtk.TreeViewColumn(_('Path'), gtk.CellRendererText(), text=3)
col.set_sort_column_id(3)
col.set_resizable(True)
col.set_visible(False)
self.view['files'].append_column(col)
col = gtk.TreeViewColumn(_('Size'), gtk.CellRendererText(), text=4)
col.set_sort_column_id(4)
col.set_resizable(True)
self.view['files'].append_column(col)
col = gtk.TreeViewColumn(_('Date'), gtk.CellRendererText(), text=5)
col.set_sort_column_id(5)
col.set_resizable(True)
self.view['files'].append_column(col)
self.view['files'].set_search_column(2)
# setup d'n'd support
self.view['files'].drag_source_set(gtk.gdk.BUTTON1_MASK,
self.DND_TARGETS,
gtk.gdk.ACTION_COPY)
# signals
def on_files_drag_data_get(self, treeview, context, selection,
targetType, eventTime):
"""responce to "data get" DnD signal"""
# get selection, and send it to the client
if targetType == self.DND_TARGETS[0][2]:
# get selection
treesrl = treeview.get_selection()
model, list_of_paths = treesrl.get_selected_rows()
ids = []
for path in list_of_paths:
fid = model.get_value(model.get_iter(path), 0)
ids.append(fid)
string = str(tuple(ids)).replace(",)", ")")
selection.set(selection.target, 8, string)
def on_files_button_press_event(self, treeview, event):
"""
Handle right click on files treeview - show popup menu.
"""
LOG.debug("Mouse button pressed")
pathinfo = treeview.get_path_at_pos(int(event.x), int(event.y))
if event.button == 3: # Right mouse button. Show context menu.
LOG.debug("It's a right button")
if pathinfo:
path = pathinfo[0]
# Make sure, that there is selected row
sel = treeview.get_selection()
sel.unselect_all()
sel.select_path(path)
self._popup_menu(sel, event, event.button)
else:
self._popup_menu(None, event, event.button)
return True
#try:
# selection = tree.get_selection()
# model, list_of_paths = selection.get_selected_rows()
#except TypeError:
# list_of_paths = []
#if len(list_of_paths) == 0:
# # try to select item under cursor
# try:
# path, column, x, y = tree.get_path_at_pos(int(event.x),
# int(event.y))
# except TypeError:
# # failed, do not show any popup and return
# tree.get_selection().unselect_all()
# return False
# selection.select_path(path[0])
#if len(list_of_paths) > 1:
# self.view['add_image'].set_sensitive(False)
# self.view['rename'].set_sensitive(False)
# self.view['edit'].set_sensitive(False)
#else:
# self.view['add_image'].set_sensitive(True)
# self.view['rename'].set_sensitive(True)
# self.view['edit'].set_sensitive(True)
#self.__popup_menu(event, 'files_popup')
#return True
LOG.debug("It's other button")
def on_files_cursor_changed(self, treeview):
"""Show details of selected file/directory"""
file_id = get_tv_item_under_cursor(treeview)
LOG.debug("found item: %s" % file_id)
return
def on_files_key_release_event(self, treeview, event):
"""do something with pressed keys"""
if gtk.gdk.keyval_name(event.keyval) == 'Menu':
try:
selection = treeview.get_selection()
model, list_of_paths = selection.get_selected_rows()
if not list_of_paths:
return
except TypeError:
return
self._popup_menu(selection, event, 0)
if gtk.gdk.keyval_name(event.keyval) == 'BackSpace':
row, gtk_column = self.view['files'].get_cursor()
if row and gtk_column:
fileob = self.files_model.get_value(row)
if fileob.parent.parent.id != 1:
#self.files_model.refresh(fileob.parent.parent)
# TODO: synchronize with disks
self.model.discs.currentdir = fileob.parent.parent
self.view['files'].grab_focus()
def on_files_row_activated(self, files_obj, row, column):
"""
On directory doubleclick in files listview dive into desired branch.
"""
fileob = self.files_model.get_value(row=row)
if not fileob.children:
# ONLY directories. files are omitted.
return
self.files_model.refresh(fileob)
self.model.discs.currentdir = fileob
self.view['files'].grab_focus()
# TODO: synchronize with disks
return
def on_add_tag1_activate(self, menu_item):
"""
TODO
"""
LOG.debug(self.on_add_tag1_activate.__doc__.strip())
raise NotImplementedError
def on_delete_tag_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_delete_tag_activate.__doc__.strip())
raise NotImplementedError
def on_add_thumb1_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_add_thumb1_activate.__doc__.strip())
raise NotImplementedError
def on_remove_thumb1_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_remove_thumb1_activate.__doc__.strip())
raise NotImplementedError
def on_add_image1_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_add_image1_activate.__doc__.strip())
raise NotImplementedError
def on_remove_image1_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_remove_image1_activate.__doc__.strip())
raise NotImplementedError
def on_edit2_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_edit2_activate.__doc__.strip())
raise NotImplementedError
def on_delete3_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_delete3_activate.__doc__.strip())
raise NotImplementedError
def on_rename2_activate(self, menuitem):
"""
TODO
"""
LOG.debug(self.on_rename2_activate.__doc__.strip())
raise NotImplementedError
# private methods
def _popup_menu(self, selection, event, button):
"""
Popup menu for files treeview. Gather information from discs model,
and trigger menu popup.
"""
LOG.debug(self._popup_menu.__doc__.strip())
if selection is None:
self.view.menu.set_menu_items_sensitivity(False)
else:
model, list_of_paths = selection.get_selected_rows()
for path in list_of_paths:
self.view.menu.set_menu_items_sensitivity(True)
self.view.menu['files_popup'].popup(None, None, None,
button, event.time)

View File

@@ -1,201 +0,0 @@
"""
Project: pyGTKtalog
Description: Controller for main window
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-02
"""
import gtk
from gtkmvc import Controller
from pygtktalog.controllers.discs import DiscsController
from pygtktalog.controllers.files import FilesController
#from pygtktalog.controllers.details import DetailsController
#from pygtktalog.controllers.tags import TagcloudController
#from pygtktalog.dialogs import yesno, okcancel, info, warn, error
from pygtktalog.dialogs import open_catalog, save_catalog, error, yesno, about
from pygtktalog.logger import get_logger
LOG = get_logger("main controller")
class MainController(Controller):
"""
Controller for main application window
"""
TITLE = "pyGTKtalog"
UNTITLED = _("untitled")
def __init__(self, model, view):
"""
Initialize MainController, add controllers for trees and details.
"""
LOG.debug(self.__init__.__doc__.strip())
Controller.__init__(self, model, view)
# add controllers for files/tags/details components
self.discs = DiscsController(model, view.discs)
self.files = FilesController(model, view.files)
#self.details = DetailsController(model, view.details)
#self.tags = TagcloudController(model, view.tags)
def register_view(self, view):
"""
Registration view for MainController class
"""
LOG.debug(self.register_view.__doc__.strip())
LOG.debug("replace hardcoded defaults with configured!")
view['main'].set_default_size(800, 600)
view['hpaned1'].set_position(200)
if not self.model.tmp_filename:
view.set_widgets_app_sensitivity(False)
view['main'].show()
# status bar
LOG.debug("register statusbar")
self.context_id = self.view['mainStatus'].get_context_id('status')
self.statusbar_id = \
self.view['mainStatus'].push(self.context_id,
self.model.status_bar_message)
# signals
def on_main_destroy_event(self, widget, event):
"""
Window destroyed. Cleanup before quit.
"""
LOG.debug(self.on_main_destroy_event.__doc__.strip())
self.on_quit_activate(widget)
return True
def on_quit_activate(self, widget):
"""
Quit and save window parameters to config file
"""
LOG.debug(self.on_quit_activate.__doc__.strip())
#if yesno(_("Do you really want to quit?"),
# _("Current database is not saved, any changes will be "
# "lost."), _("Quit application") + " - pyGTKtalog", 0):
self.model.cleanup()
LOG.debug("quit application")
gtk.main_quit()
return False
def on_new_activate(self, widget):
"""
Create new catalog file
"""
LOG.debug(self.on_new_activate.__doc__.strip())
self.model.new()
self._set_title()
self.view.set_widgets_app_sensitivity(True)
def on_open_activate(self, widget):
"""
Open catalog file
"""
LOG.debug(self.on_open_activate.__doc__.strip())
if self.model.db_unsaved and 'confirm' in self.model.config and \
self.model.config['confirm']:
if not yesno(_("Current database is not saved"),
_("Do you really want to open new file and abandon "
"current one?"),
_("Unsaved data")):
LOG.debug("Cancel opening catalog file - unsaved data remain")
return
initial_path = None
#if self.model.config.recent and self.model.config.recent[0]:
# initial_path = os.path.dirname(self.model.config.recent[0])
#if not path:
path = open_catalog(path=initial_path)
if not path:
return
# cleanup files and details
try:
self.model.files_list.clear()
except:
pass
#self.__hide_details()
#self.view['tag_path_box'].hide()
#buf = self.view['tag_cloud_textview'].get_buffer()
#buf.set_text('')
#self.view['tag_cloud_textview'].set_buffer(buf)
if not self.model.open(path):
error(_("Cannot open file."),
_("File %s cannot be open") % path,
_("Error opening file"))
else:
#self.__generate_recent_menu()
#self.__activate_ui(path)
#self.__tag_cloud()
self.view.set_widgets_app_sensitivity()
self._set_title()
return
def on_about1_activate(self, widget):
"""Show about dialog"""
about()
def on_save_activate(self, widget):
"""
Save current catalog
"""
LOG.debug(self.on_save_activate.__doc__.strip())
if not self.model.cat_fname:
self.on_save_as_activate(widget)
else:
self.model.save()
self._set_title()
def on_save_as_activate(self, widget):
"""
Save current catalog under differnet file
"""
LOG.debug(self.on_save_as_activate.__doc__.strip())
initial_path = None
#if self.model.config.recent[0]:
# initial_path = os.path.dirname(self.model.config.recent[0])
path = save_catalog(path=initial_path)
if path:
ret, err = self.model.save(path)
if ret:
#self.model.config.add_recent(path)
self._set_title()
pass
else:
error(_("Cannot write file %s.") % path,
"%s" % err,
_("Error writing file"))
# helpers
def _set_title(self):
"""
Get title of the main window, to reflect state of the catalog file
Returns:
String with apropriate title for main form
"""
LOG.debug("change the title")
if not self.model.tmp_filename:
LOG.debug("application has been initialized, title should"
" be empty")
fname = ""
elif not self.model.cat_fname:
fname = self.UNTITLED
else:
fname = self.model.cat_fname
modified = self.model.db_unsaved and "*" or ""
self.view['main'].set_title("%s%s - %s" % (fname,
modified, self.TITLE))

View File

@@ -1,23 +0,0 @@
"""
Project: pyGTKtalog
Description: Controller for Tagcloud TextView
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-08-30
"""
from gtkmvc import Controller
class TagcloudController(Controller):
"""
Controller for Tagcloud TextView
"""
#def __init__(self, model, view):
# """Initialize main controller"""
# Controller.__init__(self, model, view)
# return
def register_view(self, view):
"""Default view registration stuff"""
pass

View File

@@ -1,31 +0,0 @@
"""
Project: pyGTKtalog
Description: Model(s) for details part of the application
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2010-11-09
"""
import gtk
import gobject
from gtkmvc import Model
class DetailsModel(Model):
"""
Main model for application.
It is responsible for communicate with database objects and I/O
operations.
"""
exif = gtk.ListStore(gobject.TYPE_PYOBJECT,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_UINT64,
gobject.TYPE_STRING,
gobject.TYPE_INT,
str)
__observables__ = ['exif']

View File

@@ -1,107 +0,0 @@
"""
Project: pyGTKtalog
Description: Model for discs representation
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-02
"""
import gtk
import gobject
from gtkmvc import Model
from pygtktalog.dbobjects import File
from pygtktalog.dbcommon import Session
from pygtktalog.logger import get_logger
LOG = get_logger("discs model")
class DiscsModel(Model):
"""
Model for discs representation.
"""
currentdir = None
__observables__ = ("currentdir",)
def __init__(self):
"""
Initialization. Make some nice defaults.
"""
Model.__init__(self)
self.discs = gtk.TreeStore(gobject.TYPE_PYOBJECT,
gobject.TYPE_STRING,
str)
self.files_model = None
def clear(self):
"""
Make TreeStore empty
"""
self.discs.clear()
def refresh(self, session=Session()):
"""
Read objects from database, fill TreeStore model with discs
information
Arguments:
@session current sqlalchemy.orm.session.Session object
"""
LOG.debug("session obj: %s" % str(session))
dirs = session.query(File).filter(File.type == 1)
dirs = dirs.order_by(File.filename).all()
def get_children(parent_id=1, iterator=None):
"""
Get all children of the selected parent.
Arguments:
@parent_id - integer with id of the parent (from db)
@iterator - gtk.TreeIter, which points to a path inside model
"""
for fileob in dirs:
if fileob.parent_id == parent_id:
myiter = self.discs.insert_before(iterator, None)
self.discs.set_value(myiter, 0, fileob)
self.discs.set_value(myiter, 1, fileob.filename)
if iterator is None:
self.discs.set_value(myiter, 2, gtk.STOCK_CDROM)
else:
self.discs.set_value(myiter, 2, gtk.STOCK_DIRECTORY)
get_children(fileob.id, myiter)
return
get_children()
return True
def find_path(self, obj):
"""
Return path of specified File object (which should be the first one)
"""
path = None
gtkiter = self.discs.get_iter_first()
def get_children(iterator):
"""
Iterate through entire TreeModel, and return path for specified in
outter scope File object
"""
if self.discs.get_value(iterator, 0) == obj:
return self.discs.get_path(iterator)
if self.discs.iter_has_child(iterator):
path = get_children(self.discs.iter_children(iterator))
if path:
return path
iterator = self.discs.iter_next(iterator)
if iterator is None:
return None
return get_children(iterator)
path = get_children(gtkiter)
LOG.debug("found path for object '%s': %s" % (str(obj), str(path)))
return path

View File

@@ -1,79 +0,0 @@
"""
Project: pyGTKtalog
Description: Model for files representation
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2010-11-12
"""
import gtk
import gobject
from gtkmvc import Model
from pygtktalog.dbcommon import Session
from pygtktalog.logger import get_logger
LOG = get_logger("files model")
class FilesModel(Model):
"""
Model for files representation
"""
def __init__(self):
"""
Initialization. Make some nice defaults.
"""
Model.__init__(self)
self.files = gtk.ListStore(gobject.TYPE_PYOBJECT,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_UINT64,
gobject.TYPE_STRING,
gobject.TYPE_INT,
str)
self.discs_model = None
def clear(self):
"""
Cleanup ListStore model
"""
self.files.clear()
def refresh(self, fileob):
"""
Update files ListStore
Arguments:
fileob - File object
"""
LOG.info("found %d files for File object: %s" % (len(fileob.children),
str(fileob)))
self.files.clear()
for child in fileob.children:
myiter = self.files.insert_before(None, None)
self.files.set_value(myiter, 0, child)
self.files.set_value(myiter, 1, child.parent_id \
if child.parent_id != 1 else None)
self.files.set_value(myiter, 2, child.filename)
self.files.set_value(myiter, 3, child.filepath)
self.files.set_value(myiter, 4, child.size)
self.files.set_value(myiter, 5, child.date)
self.files.set_value(myiter, 6, 1)
self.files.set_value(myiter, 7, gtk.STOCK_DIRECTORY \
if child.type == 1 else gtk.STOCK_FILE)
def get_value(self, row=None, fiter=None, column=0):
"""
TODO:
"""
if row:
fiter = self.files.get_iter(row)
if not fiter:
LOG.error("ERROR: there is no way to determine gtk_iter object!"
" Please specify valid row or gtk_iter!")
return None
return self.files.get_value(fiter, column)

View File

@@ -1,289 +0,0 @@
"""
Project: pyGTKtalog
Description: Model for main application
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-02
"""
import os
import bz2
from string import printable
from tempfile import mkstemp
import gtk
import gobject
from gtkmvc import ModelMT
from sqlalchemy import create_engine
from pygtktalog.dbobjects import File, Exif, Group, Gthumb
from pygtktalog.dbobjects import Image, Tag, Thumbnail
from pygtktalog.dbcommon import connect, Meta, Session
from pygtktalog.logger import get_logger
from pygtktalog.models.details import DetailsModel
from pygtktalog.models.discs import DiscsModel
from pygtktalog.models.files import FilesModel
LOG = get_logger("main model")
class MainModel(ModelMT):
"""
Main model for application.
It is responsible for communicate with database objects and I/O
operations.
"""
status_bar_message = _("Idle")
current_disc = None
__observables__ = ("status_bar_message", "current_disc")
def __init__(self, filename=None):
"""
Initialization. Make some nice defaults.
Arguments:
filename - String that indicates optionally compressed with bzip2
file containing sqlite3 database.
"""
ModelMT.__init__(self)
# Opened/saved database location in filesystem. Could be compressed.
self.cat_fname = filename
# Temporary (usually in /tmp) working database.
self.tmp_filename = None
self.config = {}
# SQLAlchemy session object for internal use
self._session = None
# Flag indicates, that db was compressed
# TODO: make it depend on configuration
self.compressed = False
self.db_unsaved = None
self.discs = DiscsModel()
self.files = FilesModel()
if self.cat_fname:
self.open(self.cat_fname)
def open(self, filename):
"""
Open catalog file and read db
Arguments:
@filename - see MainModel __init__ docstring.
Returns: Bool - true for success, false otherwise.
"""
LOG.debug("filename: '%s'", filename)
self.unsaved_project = False
if not os.path.exists(filename):
LOG.warn("db file '%s' doesn't exist.", filename)
return False
if not os.path.isfile(filename):
LOG.warn("db file '%s' is not a regular file.", filename)
return False
self.cat_fname = filename
if self._open_or_decompress():
return self.discs.refresh(self._session)
else:
return False
def save(self, filename=None):
"""
Save tared directory at given catalog fielname
Arguments:
@filename - see MainModel __init__ docstring.
Returns: tuple:
Bool - true for success, false otherwise.
String or None - error message
"""
if not filename and not self.cat_fname:
LOG.debug("no filename detected!")
return False, None
if filename:
if not '.sqlite' in filename:
filename += '.sqlite'
else:
filename = filename[:filename.rindex('.sqlite')] + '.sqlite'
if 'compress' in self.config and self.config['compress']:
filename += '.bz2'
self.cat_fname = filename
val, err = self._compress_and_save()
if not val:
self.cat_fname = None
return val, err
def new(self):
"""
Create new catalog
"""
self.cleanup()
self._create_temp_db_file()
self._create_schema()
self.discs.clear()
self.files.clear()
self.db_unsaved = False
def cleanup(self):
"""
Remove temporary directory tree from filesystem
"""
if self._session:
self._session.close()
self._session = None
if self.tmp_filename is None:
return
try:
os.unlink(self.tmp_filename)
except OSError:
LOG.error("temporary db file doesn't exists!")
except TypeError:
# TODO: file does not exist - create? print error message?
LOG.error("temporary db file doesn't exists!")
else:
LOG.debug("file %s succesfully deleted", self.tmp_filename)
def _examine_file(self, filename):
"""
Try to recognize file.
Arguments:
@filename - String with full path to file to examine
Returns: 'sql', 'bz2' or None for file sqlite, compressed with bzip2
or other respectively
"""
try:
head_of_file = open(filename).read(15)
LOG.debug("head of file: %s", filter(lambda x: x in printable,
head_of_file))
except IOError:
LOG.exception("Error opening file '%s'!", filename)
self.cleanup()
self.cat_fname = None
self.tmp_filename = None
return None
if head_of_file == "SQLite format 3":
LOG.debug("File format: uncompressed sqlite")
return 'sql'
elif head_of_file[0:10] == "BZh91AY&SY":
LOG.debug("File format: bzip2")
return 'bz2'
else:
return None
def _open_or_decompress(self):
"""
Try to open file, which user thinks is our catalog file.
Returns: Bool - true for success, false otherwise
"""
filename = os.path.abspath(self.cat_fname)
LOG.info("catalog file: %s", filename)
if self._session:
self.cleanup()
self._create_temp_db_file()
LOG.debug("tmp database file: %s", str(self.tmp_filename))
examine = self._examine_file(filename)
if examine == "sql":
db_tmp = open(self.tmp_filename, "wb")
db_tmp.write(open(filename).read())
db_tmp.close()
elif examine == "bz2":
open_file = bz2.BZ2File(filename)
try:
db_tmp = open(self.tmp_filename, "w")
db_tmp.write(open_file.read())
db_tmp.close()
open_file.close()
except IOError:
self.cleanup()
self.cat_fname = None
self.internal_dirname = None
LOG.exception("File is probably not a bz2!")
return False
if self._examine_file(self.tmp_filename) is not 'sql':
LOG.error("Error opening file '%s' - not a catalog file!",
self.tmp_filename)
self.cleanup()
self.cat_fname = None
self.tmp_filename = None
return False
else:
LOG.error("Error opening file '%s' - not a catalog file!",
self.tmp_filename)
self.cleanup()
self.cat_fname = None
self.internal_dirname = None
return False
connect(os.path.abspath(self.tmp_filename))
self._session = Session()
LOG.debug("session obj: %s" % str(self._session))
return True
def _create_temp_db_file(self):
"""
Create new DB file, populate schema.
"""
fd, self.tmp_filename = mkstemp()
LOG.debug("new db filename: %s" % self.tmp_filename)
# close file descriptor, otherwise it can be source of app crash!
# http://www.logilab.org/blogentry/17873
os.close(fd)
def _create_schema(self):
"""
"""
self._session = Session()
LOG.debug("session obj: %s" % str(self._session))
connect(os.path.abspath(self.tmp_filename))
root = File()
root.id = 1
root.filename = 'root'
root.size = 0
root.source = 0
root.type = 0
root.parent_id = 1
self._session.add(root)
self._session.commit()
def _compress_and_save(self):
"""
Create (and optionaly compress) tar archive from working directory and
write it to specified file.
"""
# flush all changes
self._session.commit()
try:
if 'compress' in self.config and self.config['compress']:
output_file = bz2.BZ2File(self.cat_fname, "w")
else:
output_file = open(self.cat_fname, "w")
LOG.debug("save (and optionally compress) successed")
except IOError, (errno, strerror):
LOG.error("error saving or compressing file", errno, strerror)
return False, strerror
dbpath = open(self.tmp_filename)
output_file.write(dbpath.read())
dbpath.close()
output_file.close()
self.db_unsaved = False
return True, None

View File

@@ -1,138 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Sat Aug 29 15:13:44 2009 -->
<glade-interface>
<widget class="GtkWindow" id="top_details">
<child>
<widget class="GtkNotebook" id="notebook_details">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkHBox" id="fileinfo">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTextView" id="description">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="left_margin">2</property>
<property name="right_margin">2</property>
<property name="cursor_visible">False</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkViewport" id="thumb_box">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="resize_mode">GTK_RESIZE_QUEUE</property>
<child>
<widget class="GtkImage" id="thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xpad">3</property>
<property name="ypad">3</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">6</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="nb_fileinfo">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">File info</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="img_container">
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkIconView" id="images">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip" translatable="yes">Double click to open image</property>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Images</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="exifinfo">
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="exif_tree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="rules_hint">True</property>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">EXIF</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,76 +0,0 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="top_discs">
<child>
<widget class="GtkTreeView" id="discs">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">True</property>
<signal name="button_press_event" handler="on_discs_button_press_event"/>
<signal name="cursor_changed" handler="on_discs_cursor_changed"/>
<signal name="row_activated" handler="on_discs_row_activated"/>
<signal name="key_release_event" handler="on_discs_key_release_event"/>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="discs_popup">
<child>
<widget class="GtkMenuItem" id="expand_all">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Expand all nodes</property>
<property name="label" translatable="yes">_Expand all</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_expand_all_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="collapse_all">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Collapse all nodes</property>
<property name="label" translatable="yes">_Collapse all</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_collapse_all_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="update">
<property name="visible">True</property>
<property name="label" translatable="yes">_Update</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_update_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="rename">
<property name="visible">True</property>
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_rename_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="delete">
<property name="visible">True</property>
<property name="label" translatable="yes">_Delete</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="statistics">
<property name="visible">True</property>
<property name="label" translatable="yes">_Statistics</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_statistics_activate"/>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,122 +0,0 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="top_files">
<child>
<widget class="GtkTreeView" id="files">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="rules_hint">True</property>
<signal name="button_press_event" handler="on_files_button_press_event"/>
<signal name="cursor_changed" handler="on_files_cursor_changed"/>
<signal name="row_activated" handler="on_files_row_activated"/>
<signal name="drag_data_get" handler="on_files_drag_data_get"/>
<signal name="key_release_event" handler="on_files_key_release_event"/>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="files_popup">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="add_tag">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Add tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_tag1_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="delete_tag">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Remo_ve tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_tag_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="add_thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Add _Thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_thumb1_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="remove_thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Re_move Thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_remove_thumb1_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="add_image">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="has_tooltip">True</property>
<property name="tooltip" translatable="yes">Add images to file. If file have no thumbnail,
thumbnail from first image will be generated.</property>
<property name="label" translatable="yes">Add _Images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_image1_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="remove_image">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Rem_ove All Images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_remove_image1_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator2">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_edit2_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="delete">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Delete</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete3_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="rename">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_rename2_activate"/>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,729 +0,0 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="main">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="title" translatable="yes">pyGTKtalog</property>
<signal name="destroy" handler="on_main_destroy_event"/>
<signal name="delete_event" handler="on_main_destroy_event"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkMenuBar" id="mainMenubar">
<property name="visible">True</property>
<child>
<widget class="GtkMenuItem" id="file1">
<property name="visible">True</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="file1_menu">
<child>
<widget class="GtkImageMenuItem" id="new1">
<property name="label">gtk-new</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_new_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="open1">
<property name="label">gtk-open</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_open_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="save1">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_save_activate"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="save_as1">
<property name="label">gtk-save-as</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_save_as_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="import">
<property name="visible">True</property>
<property name="label" translatable="yes">Import</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_import_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="export">
<property name="visible">True</property>
<property name="label" translatable="yes">Export</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_export_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator13">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="recent_files1">
<property name="visible">True</property>
<property name="label" translatable="yes">Recent files</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_recent_files1_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatorseparator">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="quit1">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_quit_activate"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_edit1_activate"/>
<child>
<widget class="GtkMenu" id="edit1_menu">
<child>
<widget class="GtkImageMenuItem" id="delete1">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_delete1_activate"/>
<accelerator key="Delete" signal="activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator5">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="find1">
<property name="label">gtk-find</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_find_activate"/>
<accelerator key="f" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator2">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="properties1">
<property name="label">gtk-preferences</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_preferences_activate"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="catalog1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Catalog</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="catalog1_menu">
<child>
<widget class="GtkMenuItem" id="add_cd">
<property name="visible">True</property>
<property name="label" translatable="yes">Add _CD/DVD</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_cd_activate"/>
<accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="add_directory1">
<property name="visible">True</property>
<property name="label" translatable="yes">Add _Directory</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_directory_activate"/>
<accelerator key="d" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator3">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="del_all_images">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip" translatable="yes">Deletes all images from files in current colection</property>
<property name="label" translatable="yes">Delete all images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_del_all_images_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="del_all_thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Delete all thumbnals</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_del_all_thumb_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="save_all_img">
<property name="visible">True</property>
<property name="label" translatable="yes">Save all images...</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_save_all_img_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator12">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="stat1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Catalog _statistics</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_stat1_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator6">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="cancel1">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_cancel_clicked"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="view1">
<property name="visible">True</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="view1_menu">
<child>
<widget class="GtkCheckMenuItem" id="toolbar1">
<property name="visible">True</property>
<property name="label" translatable="yes">Toolbar</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_toolbar1_activate"/>
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="status_bar1">
<property name="visible">True</property>
<property name="label" translatable="yes">Status bar</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_status_bar1_activate"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="help1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="help1_menu">
<child>
<widget class="GtkImageMenuItem" id="about1">
<property name="label">gtk-about</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="on_about1_activate"/>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkToolbar" id="maintoolbar">
<property name="visible">True</property>
<property name="toolbar_style">both</property>
<child>
<widget class="GtkToolButton" id="tb_new">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Create new catalog</property>
<property name="stock_id">gtk-new</property>
<signal name="clicked" handler="on_new_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_open">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Open catalog file</property>
<property name="stock_id">gtk-open</property>
<signal name="clicked" handler="on_open_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_save">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Save catalog</property>
<property name="stock_id">gtk-save</property>
<signal name="clicked" handler="on_save_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolItem" id="toolitem2">
<property name="visible">True</property>
<child>
<widget class="GtkVSeparator" id="vseparator1">
<property name="visible">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_addcd">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Add CD/DVD to catalog</property>
<property name="label" translatable="yes">Add CD</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-cdrom</property>
<signal name="clicked" handler="on_add_cd_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_adddir">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Add Dir</property>
<property name="stock_id">gtk-directory</property>
<signal name="clicked" handler="on_add_directory_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_find">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Find file</property>
<property name="stock_id">gtk-find</property>
<signal name="clicked" handler="on_find_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolItem" id="toolitem3">
<property name="visible">True</property>
<child>
<widget class="GtkVSeparator" id="vseparator2">
<property name="visible">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="cancel">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="label" translatable="yes">Cancel</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-cancel</property>
<signal name="clicked" handler="on_cancel_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="tb_quit">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Quit pyGTKtalog</property>
<property name="stock_id">gtk-quit</property>
<signal name="clicked" handler="on_quit_activate"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="tag_path_box">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">1</property>
<property name="spacing">2</property>
<child>
<widget class="GtkEntry" id="tag_path">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="shadow_type">etched-in</property>
<property name="caps_lock_warning">False</property>
<property name="secondary_icon_stock">gtk-clear</property>
<property name="secondary_icon_activatable">True</property>
<property name="secondary_icon_sensitive">True</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="clear">
<property name="label">gtk-clear</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_clear_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkNotebook" id="nb_dirs">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow_discs">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<placeholder/>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Discs</property>
</widget>
<packing>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Tags</property>
</widget>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
<property name="type">tab</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkVPaned" id="vpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow_files">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">False</property>
</packing>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">1</property>
<property name="position">4</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="statusprogress">
<property name="visible">True</property>
<child>
<widget class="GtkStatusbar" id="mainStatus">
<property name="visible">True</property>
<property name="has_resize_grip">False</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkProgressBar" id="progressbar1">
<property name="visible">True</property>
<property name="pulse_step">0.10000000149</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">5</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="win_exifinfo">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkHBox" id="exif_hbox">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
<widget class="GtkWindow" id="image_show">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="title" translatable="yes">pyGTKtalog - Image</property>
<property name="destroy_with_parent">True</property>
<child>
<widget class="GtkImage" id="img">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-missing-image</property>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="img_popup">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="img_add">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Add images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_img_add_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator9">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="img_delete">
<property name="visible">True</property>
<property name="label" translatable="yes">_Delete images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_img_delete_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="img_thumbset">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Set as _thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_img_thumbset_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator10">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="img_save">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Save images to...</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_img_save_activate"/>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="th_popup">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="th_delete">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Remove Thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_th_delete_activate"/>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="tag_popup">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="delete_tag2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Delete tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_tag2_activate"/>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="top_tags">
<child>
<widget class="GtkTextView" id="tag_cloud_textview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="wrap_mode">word</property>
<property name="cursor_visible">False</property>
<signal name="visibility_notify_event" handler="on_tag_cloud_textview_visibility_notify_event"/>
<signal name="drag_motion" handler="on_tag_cloud_textview_drag_motion"/>
<signal name="event_after" handler="on_tag_cloud_textview_event_after"/>
<signal name="drag_drop" handler="on_tag_cloud_textview_drag_drop"/>
<signal name="drag_leave" handler="on_tag_cloud_textview_drag_leave"/>
<signal name="drag_data_received" handler="on_tag_cloud_textview_drag_data_received"/>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,210 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Fri Oct 23 18:28:53 2009 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<widget class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
<widget class="GtkMenuItem" id="menuitem1">
<property name="visible">True</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu1">
<property name="visible">True</property>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem1">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-new</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem2">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-open</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem3">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-save</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem4">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-save-as</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem5">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-quit</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem2">
<property name="visible">True</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu2">
<property name="visible">True</property>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem6">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-cut</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem7">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-copy</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem8">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-paste</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem9">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-delete</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem3">
<property name="visible">True</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menuitem4">
<property name="visible">True</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menu3">
<property name="visible">True</property>
<child>
<widget class="GtkImageMenuItem" id="imagemenuitem10">
<property name="visible">True</property>
<property name="label" translatable="yes">gtk-about</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkHPaned" id="hpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<widget class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="resize_mode">GTK_RESIZE_QUEUE</property>
<child>
<widget class="GtkTreeView" id="treeview1">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkTextView" id="textview1">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<child>
<widget class="GtkStatusbar" id="statusbar1">
<property name="visible">True</property>
<property name="spacing">2</property>
<property name="has_resize_grip">False</property>
</widget>
</child>
<child>
<widget class="GtkProgressBar" id="progressbar1">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,189 +0,0 @@
"""
Project: pyGTKtalog
Description: View for main window
Type: core
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-02
"""
import os.path
from gtkmvc import View
def get_glade(glade_filename):
"""
Return full path to specified glade file
"""
return os.path.join(os.path.dirname(__file__), "glade", glade_filename)
class MainView(View):
"""
Create window from glade file. Note, that glade file is placed in view
module under glade subdirectory.
"""
glade = get_glade("main.glade")
top = "main"
def __init__(self, top="main"):
"""
Initialize view
"""
View.__init__(self)
self.app_sensitive = None
self['tag_path_box'].hide()
self.discs = DiscsView()
#self['scrolledwindow_discs'].add_with_viewport(\
# self.discs.get_top_widget())
self['scrolledwindow_discs'].add(self.discs.get_top_widget())
self.files = FilesView()
self['scrolledwindow_files'].add_with_viewport(\
self.files.get_top_widget())
self.details = DetailsView()
self['vpaned1'].add2(self.details.get_top_widget())
def set_widgets_scan_sensitivity(self, sensitive=True):
"""
Activate/deactivate selected widgets while scanning is active
"""
pass
def set_widgets_app_sensitivity(self, sensitive=True):
"""
Enable/disable widgets for empty application. Usefull for first run
of an application (without any db filename as an argument).
"""
if self.app_sensitive is sensitive:
return
for widget in ['scrolledwindow_discs', 'scrolledwindow_files',
'tb_save', 'tb_addcd', 'tb_adddir', 'tb_find',
'edit1', 'catalog1', 'save1', 'save_as1', 'import',
'export']:
self[widget].set_sensitive(sensitive)
# widgets from subclasses
self.details['notebook_details'].set_sensitive(sensitive)
class DiscsView(View):
"""
Separate Discs TreeView subview.
"""
glade = get_glade("discs.glade")
top = 'discs'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
self.menu = DiscsPopupView()
class DiscsPopupView(View):
"""
Separate Discs PopUp subview.
"""
glade = get_glade("discs.glade")
top = 'discs_popup'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
def set_update_sensitivity(self, state):
"""
Set sensitivity for 'update' popup menu item
Arguments:
@state - Bool, if True update menu item will be sensitive,
otherwise not
"""
self['update'].set_sensitive(state)
def set_menu_items_sensitivity(self, state):
"""
Set sensitivity for couple of popup menu items, which should be
disabled if user right-clicks on no item in treeview.
Arguments:
@state - Bool, if True update menu item will be sensitive,
otherwise not
"""
for item in ['update', 'rename', 'delete', 'statistics']:
self[item].set_sensitive(state)
class FilesView(View):
"""
Separate subview of Files TreeView as a table.
"""
glade = get_glade("files.glade")
top = 'files'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
self.menu = FilesPopupView()
class FilesPopupView(View):
"""
Separate Files PopUp subview.
"""
glade = get_glade("files.glade")
top = 'files_popup'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
def set_menu_items_sensitivity(self, state):
"""
Set sensitivity for couple of popup menu items, which should be
disabled if user right-clicks on no item in treeview.
Arguments:
@state - Bool, if True update menu item will be sensitive,
otherwise not
"""
for item in ["add_tag", "delete_tag", "add_thumb", "remove_thumb",
"add_image", "remove_image", "edit", "delete", "rename"]:
self[item].set_sensitive(state)
class TagcloudView(View):
"""
Textview subview with clickable tags.
"""
glade = get_glade("tagcloud.glade")
top = 'tag_cloud_textview'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
class DetailsView(View):
"""
Notebook subview containing tabs with details and possibly Exif, images
assocated with object and alternatively thumbnail.
"""
glade = get_glade("details.glade")
top = 'notebook_details'
def __init__(self):
"""
Initialize view
"""
View.__init__(self)

View File

@@ -1,36 +0,0 @@
"""
Project: pyGTKtalog
Description: Menu for the main window
Type: interface
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2010-03-14 21:31:57
"""
import gtk
from gtkmvc import View
class MainMenu(View):
def __init__(self):
View.__init__(self)
self['mainMenu'] = gtk.MenuBar()
self['file_menu'] = gtk.MenuItem(_("_File"))
accel_group = gtk.AccelGroup()
menu_items = (("/_File", None, None, 0, "<Branch>"),
("/File/_New", "<control>N", None, 0, None),
("/File/_Open", "<control>O", None, 0, None),
("/File/_Save", "<control>S", None, 0, None),
("/File/Save _As", None, None, 0, None),
("/File/sep1", None, None, 0, "<Separator>"),
("/File/Quit", "<control>Q", gtk.main_quit, 0, None),
("/_Options", None, None, 0, "<Branch>"),
("/Options/Test", None, None, 0, None),
("/_Help", None, None, 0, "<LastBranch>"),
("/_Help/About", None, None, 0, None),)
item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>", accel_group)
item_factory.create_items(menu_items)

View File

@@ -1,18 +0,0 @@
"""
Project: pyGTKtalog
Description: Toolbar for the main window
Type: interface
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2010-04-20 18:47:49
"""
import gtk
from gtkmvc import View
class ToolBar(View):
def __init__(self):
View.__init__(self)
self['maintoolbar'] = gtk.Toolbar()

View File

@@ -1,803 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkDialog" id="config">
<property name="width_request">550</property>
<property name="height_request">400</property>
<property name="title" translatable="yes">Preferences - pyGTKtalog</property>
<property name="modal">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkHPaned" id="hpaned1">
<property name="width_request">168</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">140</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="category_tree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="rules_hint">True</property>
<signal name="cursor_changed" handler="on_category_tree_cursor_changed"/>
</widget>
</child>
</widget>
<packing>
<property name="resize">False</property>
<property name="shrink">True</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="desc">
<property name="visible">True</property>
<property name="xpad">3</property>
<property name="ypad">3</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkViewport" id="pref_group">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="main_group_holder">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="disk_group">
<property name="border_width">1</property>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="n_rows">2</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Mount point:</property>
<property name="mnemonic_widget">mnt_entry</property>
</widget>
<packing>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="mnt_entry">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_mnt">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Browse...</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_mnt_clicked"/>
<signal name="activate" handler="on_button_mnt_activate"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="ejt_entry">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_ejt">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Browse...</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_ejt_clicked"/>
<signal name="activate" handler="on_button_ejt_activate"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Eject program:</property>
<property name="mnemonic_widget">ejt_entry</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;CD/DVD drive options&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkVBox" id="general_group">
<property name="border_width">1</property>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="ch_win">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Save main window size</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_pan">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Save paned window sizes</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_eject">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Eject CD/DVD after scan</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_compress">
<property name="can_focus">True</property>
<property name="label" translatable="yes">Compress collection</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_imageviewer">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Use external image viewer</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_ch_imageviewer_toggled"/>
</widget>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table4">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="n_rows">1</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<widget class="GtkLabel" id="label_imv">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Image viewer:</property>
<property name="mnemonic_widget">mnt_entry</property>
</widget>
<packing>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry_imv">
<property name="width_request">100</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_imv">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Browse...</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_imv_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
</widget>
<packing>
<property name="position">5</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;General options&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkFrame" id="frame5">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment5">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="vbox7">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="ch_xls">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Export to XLS</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Misc&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame6">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment6">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="vbox8">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="ch_quit">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Confirm quit if there are unsaved data</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_warnnew">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Confirm "new" if there are unsaved data</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_wrnmount">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Warn about mount/umount errors</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_wrndel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Warn on delete</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Confirmations&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="scan_group">
<property name="border_width">1</property>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="vbox6">
<property name="visible">True</property>
<child>
<widget class="GtkCheckButton" id="ch_thumb">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Create thumbnails for images</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_exif">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Scan EXIF data</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="ch_gthumb">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Include gThumb image description</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkCheckButton" id="ch_retrive">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Retrive extra information</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_ch_retrive_toggled"/>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="ft_group">
<property name="border_width">1</property>
<child>
<widget class="GtkFrame" id="frame7">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment7">
<property name="visible">True</property>
<property name="has_focus">True</property>
<property name="border_width">5</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkVBox" id="ext_ed">
<property name="visible">True</property>
<property name="border_width">5</property>
<child>
<widget class="GtkTable" id="table3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="row_spacing">3</property>
<child>
<widget class="GtkEntry" id="ext_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Extension:</property>
<property name="justify">GTK_JUSTIFY_RIGHT</property>
<property name="mnemonic_widget">ext_entry</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Command:</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="com_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">3</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="ext_add">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Add/Change</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_ext_add_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="ext_del">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Delete</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_ext_del_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="extension_tree">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="rules_hint">True</property>
<signal name="cursor_changed" handler="on_extension_tree_cursor_changed"/>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Files extensions&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">3</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="resize">True</property>
<property name="shrink">True</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_cancelbutton_clicked"/>
</widget>
</child>
<child>
<widget class="GtkButton" id="okbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-save</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
<signal name="clicked" handler="on_okbutton_clicked"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,838 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkDialog" id="inputDialog">
<property name="visible">True</property>
<property name="title" translatable="yes">Disk label - pyGTKtalog</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="xpad">3</property>
<property name="label" translatable="yes">Disk label</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="volname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="activates_default">True</property>
<signal name="activate" handler="on_volname_activate"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="okbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="addDirDialog">
<property name="visible">True</property>
<property name="title" translatable="yes">dialog1</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox2">
<property name="visible">True</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="border_width">3</property>
<child>
<widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="border_width">3</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="border_width">3</property>
<property name="left_padding">12</property>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">2</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="browse">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Browse...</property>
<property name="use_underline">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_browse_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Select directory:</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Disk Label:</property>
</widget>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="directory">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="dirvolname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">New</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Select directory and enter label&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area2">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="okbutton2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-ok</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="statDialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">pyGTKtalog - stats</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="n_rows">4</property>
<property name="n_columns">2</property>
<property name="column_spacing">2</property>
<child>
<widget class="GtkLabel" id="size_label">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Total size:</property>
<property name="justify">GTK_JUSTIFY_RIGHT</property>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="files_label">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Number of files:</property>
<property name="justify">GTK_JUSTIFY_RIGHT</property>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="dirs_label">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Number of directories:</property>
<property name="justify">GTK_JUSTIFY_RIGHT</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="discs_label">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Number of discs:</property>
<property name="justify">GTK_JUSTIFY_RIGHT</property>
</widget>
<packing>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="size_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="files_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="dirs_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="discs_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="editable">False</property>
<property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="renameDialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">pyGTKtalog - rename</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xpad">3</property>
<property name="label" translatable="yes">Rename</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="name">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="button2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="button3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-ok</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="file_editDialog">
<property name="width_request">500</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="top_padding">3</property>
<property name="bottom_padding">3</property>
<property name="left_padding">12</property>
<property name="right_padding">3</property>
<child>
<widget class="GtkEntry" id="filename_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">&lt;b&gt;Filename&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="top_padding">3</property>
<property name="bottom_padding">3</property>
<property name="left_padding">12</property>
<property name="right_padding">3</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTextView" id="description_text">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">&lt;b&gt;Description&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="top_padding">3</property>
<property name="bottom_padding">3</property>
<property name="left_padding">12</property>
<property name="right_padding">3</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTextView" id="note_text">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">&lt;b&gt;Note&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-save</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="tagsDialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Fill field below with comma separated keywords you will desired to tag selected files.</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">3</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="tag_entry1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="is_focus">True</property>
<property name="can_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="activates_default">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="button5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-ok</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="tagRemove">
<property name="width_request">600</property>
<property name="height_request">400</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">pyGTKtalog - remove tags</property>
<property name="modal">True</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox7">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="treeview1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property>
<property name="rules_hint">True</property>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area7">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancel5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="ok2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-remove</property>
<property name="use_stock">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,276 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.0 on Tue May 13 10:23:51 2008 -->
<glade-interface>
<widget class="GtkWindow" id="search_window">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">3</property>
<signal name="delete_event" handler="on_search_window_delete_event"/>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">3</property>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Search for:</property>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBoxEntry" id="comboboxentry">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child internal-child="entry">
<widget class="GtkEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="activate" handler="on_search_activate"/>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkButton" id="search_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-find</property>
<property name="use_stock">True</property>
<property name="response_id">1</property>
<signal name="clicked" handler="on_search_activate"/>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
</packing>
</child>
<child>
<widget class="GtkFrame" id="frame3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label_xalign">0</property>
<child>
<widget class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="top_padding">3</property>
<property name="bottom_padding">3</property>
<property name="left_padding">12</property>
<property name="right_padding">3</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkTreeView" id="result">
<property name="width_request">600</property>
<property name="height_request">300</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="headers_clickable">True</property>
<property name="rules_hint">True</property>
<signal name="row_activated" handler="on_result_row_activated"/>
<signal name="button_release_event" handler="on_result_button_release_event"/>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="spacing">2</property>
<property name="has_resize_grip">False</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">&lt;b&gt;Search results&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="type">label_item</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">3</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="close">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
<signal name="clicked" handler="on_close_clicked"/>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkMenu" id="files_popup">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkMenuItem" id="add_tag">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Add tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_tag_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="delete_tag">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Remo_ve tag</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_tag_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator16">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="add_thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Add _Thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_thumb_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="remove_thumb">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Re_move Thumbnail</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_remove_thumb_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator17">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="add_image">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip" translatable="yes">Add images to file. If file have no thumbnail,
thumbnail from first image will be generated.</property>
<property name="label" translatable="yes">Add _Images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_add_image_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="remove_image">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Rem_ove All Images</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_remove_image_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator18">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="edit">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_edit_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="delete">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Delete</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_delete_activate"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="rename">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Rename</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_rename_activate"/>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -1,25 +0,0 @@
# 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
# -------------------------------------------------------------------------

View File

@@ -1,25 +0,0 @@
# 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
# -------------------------------------------------------------------------

View File

@@ -1,282 +0,0 @@
# 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 Controller
import views.v_dialogs as Dialogs
import gtk
class ConfigController(Controller):
category_dict = {
'Disk options':'disk_group',
'General':'general_group',
'Scan options':'scan_group',
'Files extensions':'ft_group',
}
category_order = ['General', 'Disk options', 'Scan options',
'Files extensions',]
def __init__(self, model):
Controller.__init__(self, model)
return
def register_view(self, view):
Controller.register_view(self, view)
# get data from Config object and put it into view
self.view['mnt_entry'].set_text(self.model.confd['cd'])
self.view['ejt_entry'].set_text(self.model.confd['ejectapp'])
self.view['ch_win'].set_active(self.model.confd['savewin'])
self.view['ch_pan'].set_active(self.model.confd['savepan'])
self.view['ch_eject'].set_active(self.model.confd['eject'])
self.view['ch_xls'].set_active(self.model.confd['exportxls'])
self.view['ch_quit'].set_active(self.model.confd['confirmquit'])
self.view['ch_wrnmount'].set_active(self.model.confd['mntwarn'])
self.view['ch_wrndel'].set_active(self.model.confd['delwarn'])
self.view['ch_warnnew'].set_active(self.model.confd['confirmabandon'])
self.view['ch_thumb'].set_active(self.model.confd['thumbs'])
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'])
self.view['ch_retrive'].set_active(self.model.confd['retrive'])
self.view['ch_imageviewer'].set_active(self.model.confd['imgview'])
self.view['entry_imv'].set_text(self.model.confd['imgprog'])
self.__toggle_scan_group()
# initialize tree view
self.__setup_category_tree()
# initialize models for files extensions
vi = self.view['extension_tree']
vi.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
#self.view['extension_tree'].set_model(self.model.ext_tree)
self.__setup_extension_tree()
self.view['config'].show()
return
#################
# connect signals
def on_extension_tree_cursor_changed(self, tree):
model = tree.get_model()
selected = model.get_value(model.get_iter(tree.get_cursor()[0]), 0)
ext = self.model.confd['extensions']
self.view['ext_entry'].set_text(selected)
self.view['com_entry'].set_text(ext[selected])
def on_category_tree_cursor_changed(self, tree):
"""change view to selected row corresponding to group of properties"""
model = tree.get_model()
selected = model.get_value(model.get_iter(tree.get_cursor()[0]), 0)
iterator = tree.get_model().get_iter_first();
while iterator != None:
if model.get_value(iterator, 0) == selected:
self.view[self.category_dict[model.get_value(iterator,
0)]].show()
self.view['desc'].set_markup("<b>%s</b>" % selected)
else:
self.view[self.category_dict[model.get_value(iterator,
0)]].hide()
iterator = tree.get_model().iter_next(iterator);
return
def on_cancelbutton_clicked(self, button):
self.view['config'].destroy()
return
def on_okbutton_clicked(self, button):
# get data from view and put it into Config object
self.model.confd['cd'] = self.view['mnt_entry'].get_text()
self.model.confd['ejectapp'] = self.view['ejt_entry'].get_text()
self.model.confd['savewin'] = self.view['ch_win'].get_active()
self.model.confd['savepan'] = self.view['ch_pan'].get_active()
self.model.confd['eject'] = self.view['ch_eject'].get_active()
self.model.confd['exportxls'] = self.view['ch_xls'].get_active()
self.model.confd['confirmquit'] = self.view['ch_quit'].get_active()
self.model.confd['mntwarn'] = self.view['ch_wrnmount'].get_active()
self.model.confd['delwarn'] = self.view['ch_wrndel'].get_active()
v = self.view['ch_warnnew']
self.model.confd['confirmabandon'] = v.get_active()
self.model.confd['thumbs'] = 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.confd['retrive'] = self.view['ch_retrive'].get_active()
self.model.confd['imgview'] = self.view['ch_imageviewer'].get_active()
self.model.confd['imgprog'] = self.view['entry_imv'].get_text()
self.model.save()
self.view['config'].destroy()
def on_button_ejt_clicked(self, button):
fn = self.__show_filechooser("Choose eject program")
self.view['ejt_entryentry_imv'].set_text(fn)
def on_button_mnt_clicked(self, button):
fn = self.__show_filechooser("Choose mount point")
self.view['mnt_entry'].set_text(fn)
def on_ch_retrive_toggled(self, widget):
self.__toggle_scan_group()
def on_ch_imageviewer_toggled(self, checkbox):
state = self.view['ch_imageviewer'].get_active()
for i in ['label_imv', 'entry_imv', 'button_imv']:
self.view[i].set_sensitive(state)
def on_button_imv_clicked(self, widget):
fn = self.__show_filechooser("Choose image viewer")
self.view['entry_imv'].set_text(fn)
def on_ext_add_clicked(self, widget):
ext = self.view['ext_entry'].get_text().lower()
com = self.view['com_entry'].get_text()
if len(ext) == 0 and len(com) == 0:
Dialogs.Err("Config - pyGTKtalog", "Error",
"Extension and command required")
return
if len(com) == 0:
Dialogs.Err("Config - pyGTKtalog", "Error", "Command is empty")
return
if len(ext) == 0:
Dialogs.Err("Config - pyGTKtalog", "Error", "Extension is empty")
return
if ext in self.model.confd['extensions'].keys():
obj = Dialogs.Qst('Alter extension',
'Alter extension?',
'Extension "%s" will be altered.' % ext)
if not obj.run():
return
self.model.confd['extensions'][ext] = com
self.__setup_extension_tree()
return
def on_ext_del_clicked(self, widget):
v = self.view['extension_tree']
model, selection = v.get_selection().get_selected_rows()
if len(selection) == 0:
Dialogs.Err("Config - pyGTKtalog", "Error", "No item selected")
return
elif len(selection) == 1:
sufix = ''
else:
sufix = "s"
if self.model.confd['delwarn']:
obj = Dialogs.Qst('Delete extension%s' % sufix,
'Delete extension%s?' % sufix,
'Object%s will be permanently removed.' % sufix)
if not obj.run():
return
for i in selection:
m = self.model.confd['extensions']
m.pop(model.get_value(model.get_iter(i), 0))
self.__setup_extension_tree()
return
############################
# private controller methods
def __setup_extension_tree(self):
self.model.refresh_ext()
self.view['extension_tree'].set_model(self.model.ext_tree)
for i in self.view['extension_tree'].get_columns():
self.view['extension_tree'].remove_column(i)
cell = gtk.CellRendererText()
column = gtk.TreeViewColumn("Extension", cell, text=0)
column.set_resizable(True)
self.view['extension_tree'].append_column(column)
column = gtk.TreeViewColumn("Command", cell, text=1)
column.set_resizable(True)
self.view['extension_tree'].append_column(column)
def __toggle_scan_group(self):
for i in ('ch_thumb','ch_exif','ch_gthumb'):
self.view[i].set_sensitive(self.view['ch_retrive'].get_active())
return
def __setup_category_tree(self):
category_tree = self.view['category_tree']
category_tree.set_model(self.model.category_tree)
self.model.category_tree.clear()
for i in self.category_order:
myiter = self.model.category_tree.insert_before(None,None)
self.model.category_tree.set_value(myiter,0,i)
cell = gtk.CellRendererText()
column = gtk.TreeViewColumn("Name",cell,text=0)
column.set_resizable(True)
category_tree.append_column(column)
def __show_filechooser(self, title):
"""dialog for choose eject"""
fn = None
dialog = gtk.FileChooserDialog(
title=title,
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:
if __debug__:
print "c_config.py: __show_filechooser()",
print dialog.get_filename()
fn = dialog.get_filename()
dialog.destroy()
return fn
def __show_dirchooser(self):
"""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.view['mnt_entry'].get_text())
dialog.set_default_response(gtk.RESPONSE_OK)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.view['mnt_entry'].set_text(dialog.get_filename())
dialog.destroy()
pass # end of class

File diff suppressed because it is too large Load Diff

View File

@@ -1,379 +0,0 @@
# 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 gtk
from gtkmvc import Controller
import views.v_dialogs as Dialogs
class SearchController(Controller):
"""Controller for main application window"""
def __init__(self, model):
"""Initialize controller"""
Controller.__init__(self, model)
self.search_string = ""
return
def register_view(self, view):
Controller.register_view(self, view)
# Setup TreeView result widget, as columned list
v = self.view['result']
v.set_model(self.model.search_list)
v.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
c = gtk.TreeViewColumn('Disc', gtk.CellRendererText(), text=1)
c.set_sort_column_id(1)
c.set_resizable(True)
v.append_column(c)
c = gtk.TreeViewColumn('Filename')
cellpb = gtk.CellRendererPixbuf()
cell = gtk.CellRendererText()
c.pack_start(cellpb, False)
c.pack_start(cell, True)
c.set_attributes(cellpb, stock_id=7)
c.set_attributes(cell, text=2)
c.set_sort_column_id(2)
c.set_resizable(True)
v.append_column(c)
c = gtk.TreeViewColumn('Path', gtk.CellRendererText(), text=3)
c.set_sort_column_id(3)
c.set_resizable(True)
v.append_column(c)
c = gtk.TreeViewColumn('Size', gtk.CellRendererText(), text=4)
c.set_sort_column_id(4)
c.set_resizable(True)
v.append_column(c)
c = gtk.TreeViewColumn('Date', gtk.CellRendererText(), text=5)
c.set_sort_column_id(5)
c.set_resizable(True)
v.append_column(c)
v.set_search_column(2)
# combobox
self.view['comboboxentry'].set_model(self.model.search_history)
self.view['comboboxentry'].set_text_column(0)
# statusbar
self.context_id = self.view['statusbar'].get_context_id('search')
self.view['statusbar'].pop(self.context_id)
self.view['search_window'].show()
self.model.search_created = True;
return
#########################################################################
# Connect signals from GUI, like menu objects, toolbar buttons and so on.
def on_search_window_delete_event(self, window, event):
"""if window was closed, reset attributes"""
self.model.point = None
self.model.search_created = False;
return False
def on_close_clicked(self, button):
"""close search window"""
self.model.point = None
self.model.search_created = False;
self.view['search_window'].destroy()
def on_search_activate(self, entry):
"""find button or enter pressed on entry search. Do the search"""
search_txt = self.view['search_entry'].get_text()
self.search_string = search_txt
found = self.model.search(search_txt)
self.model.add_search_history(search_txt)
self.__set_status_bar(found)
def on_result_row_activated(self, treeview, path, treecolumn):
"""result treeview row activated, change models 'point' observable
variable to id of elected item. rest is all in main controler hands."""
model = treeview.get_model()
s_iter = model.get_iter(path)
self.model.point = model.get_value(s_iter, 0)
def on_result_button_release_event(self, tree, event):
if event.button == 3: # Right mouse button. Show context menu.
try:
selection = tree.get_selection()
model, list_of_paths = selection.get_selected_rows()
except TypeError:
list_of_paths = []
if len(list_of_paths) == 0:
# try to select item under cursor
try:
path, column, x, y = tree.get_path_at_pos(int(event.x),
int(event.y))
except TypeError:
# failed, do not show any popup and return
tree.get_selection().unselect_all()
return False
selection.select_path(path[0])
if len(list_of_paths) > 1:
self.view['add_image'].set_sensitive(False)
self.view['rename'].set_sensitive(False)
self.view['edit'].set_sensitive(False)
else:
self.view['add_image'].set_sensitive(True)
self.view['rename'].set_sensitive(True)
self.view['edit'].set_sensitive(True)
self.view['files_popup'].popup(None, None, None, 0, 0)
self.view['files_popup'].show_all()
return True
def on_add_tag_activate(self, menu_item):
"""Add tags to selected files"""
tags = Dialogs.TagsDialog().run()
if not tags:
return
ids = self.__get_tv_selection_ids(self.view['result'])
for item_id in ids:
self.model.add_tags(item_id, tags)
self.model.unsaved_project = True
return
def on_delete_tag_activate(self, menu_item):
ids = self.__get_tv_selection_ids(self.view['result'])
if not ids:
Dialogs.Inf("Remove tags", "No files selected",
"You have to select some files first.")
return
tags = self.model.get_tags_by_file_id(ids)
if tags:
d = Dialogs.TagsRemoveDialog(tags)
retcode, retval = d.run()
if retcode=="ok" and not retval:
Dialogs.Inf("Remove tags", "No tags selected",
"You have to select any tag to remove from files.")
return
elif retcode == "ok" and retval:
self.model.delete_tags(ids, retval)
found = self.model.search(self.search_string)
self.__set_status_bar(found)
def on_add_thumb_activate(self, menu_item):
image, only_thumbs = Dialogs.LoadImageFile().run()
if not image:
return
ids = self.__get_tv_selection_ids(self.view['result'])
for item_id in ids:
self.model.add_thumbnail(image, item_id)
self.model.unsaved_project = True
return
def on_remove_thumb_activate(self, menu_item):
if self.model.config.confd['delwarn']:
title = 'Delete thumbnails'
question = 'Delete thumbnails?'
description = "Thumbnails for selected items will be permanently"
description += " removed from catalog."
obj = Dialogs.Qst(title, question, description)
if not obj.run():
return
try:
ids = self.__get_tv_selection_ids(self.view['result'])
for item_id in ids:
self.model.del_thumbnail(item_id)
except:
if __debug__:
print "c_search.py: on_remove_thumb_activate(): error on",
print "getting selected items or removing thumbnails"
return
self.model.unsaved_project = True
return
def on_add_image_activate(self, menu_item):
dialog = Dialogs.LoadImageFile(True)
msg = "Don't copy images. Generate only thumbnails."
toggle = gtk.CheckButton(msg)
toggle.show()
dialog.dialog.set_extra_widget(toggle)
images, only_thumbs = dialog.run()
if not images:
return
for image in images:
try:
selection = self.view['result'].get_selection()
model, list_of_paths = selection.get_selected_rows()
fid = model.get_value(model.get_iter(list_of_paths[0]), 0)
except:
try:
path, column = self.view['result'].get_cursor()
model = self.view['result'].get_model()
fiter = model.get_iter(path)
fid = model.get_value(fiter, 0)
except:
return
self.model.add_image(image, fid, only_thumbs)
self.model.unsaved_project = True
return
def on_remove_image_activate(self, menu_item):
if self.model.config.confd['delwarn']:
title = 'Delete images'
question = 'Delete all images?'
description = 'All images for selected items will be permanently'
description += ' removed from catalog.'
obj = Dialogs.Qst(title, question, description)
if not obj.run():
return
try:
ids = self.__get_tv_selection_ids(self.view['result'])
for item_id in ids:
self.model.del_images(item_id)
except:
if __debug__:
print "c_search.py: on_remove_thumb_activate(): error on",
print "getting selected items or removing thumbnails"
return
self.model.unsaved_project = True
return
def on_edit_activate(self, menu_item):
try:
selection = self.view['result'].get_selection()
model, list_of_paths = selection.get_selected_rows()
fid = model.get_value(model.get_iter(list_of_paths[0]), 0)
except TypeError:
if __debug__:
print "c_main.py: on_edit2_activate(): 0",
print "zaznaczonych wierszy"
return
val = self.model.get_file_info(fid)
ret = Dialogs.EditDialog(val).run()
if ret:
self.model.rename(fid, ret['filename'])
self.model.update_desc_and_note(fid,
ret['description'], ret['note'])
self.model.unsaved_project = True
def on_delete_activate(self, menu_item):
dmodel = self.model.discs_tree
try:
selection = self.view['result'].get_selection()
model, list_of_paths = selection.get_selected_rows()
except TypeError:
return
if not list_of_paths:
Dialogs.Inf("Delete files", "No files selected",
"You have to select at least one file to delete.")
return
if self.model.config.confd['delwarn']:
description = "Selected files and directories will be "
description += "permanently\n removed from catalog."
obj = Dialogs.Qst("Delete files", "Delete files?", description)
if not obj.run():
return
def foreach_searchtree(zmodel, zpath, ziter, d):
if d[0] == zmodel.get_value(ziter, 0):
d[1].append(zpath)
return False
ids = []
for p in list_of_paths:
ids.append(model.get_value(model.get_iter(p), 0))
for fid in ids:
# delete from db
self.model.delete(fid)
self.model.unsaved_project = True
found = self.model.search(self.search_string)
self.__set_status_bar(found)
return
def on_rename_activate(self, menu_item):
try:
selection = self.view['result'].get_selection()
model, list_of_paths = selection.get_selected_rows()
fid = model.get_value(model.get_iter(list_of_paths[0]), 0)
except TypeError:
if __debug__:
print "c_main.py: on_edit2_activate(): 0",
print "zaznaczonych wierszy"
return
fid = model.get_value(model.get_iter(list_of_paths[0]), 0)
name = model.get_value(model.get_iter(list_of_paths[0]),2)
new_name = Dialogs.InputNewName(name).run()
if __debug__:
print "c_search.py: on_rename_activate(): label:", new_name
if new_name and new_name != name:
self.model.rename(fid, new_name)
self.model.unsaved_project = True
return
#####################
# observed properetis
#########################
# private class functions
def __set_status_bar(self, found):
"""sets number of founded items in statusbar"""
if found == 0:
msg = "No files found."
elif found == 1:
msg = "Found 1 file."
else:
msg = "Found %d files." % found
self.view['statusbar'].push(self.context_id, "%s" % msg)
def __get_tv_selection_ids(self, treeview):
"""get selection from treeview and return coresponding ids' from
connected model or None"""
ids = []
try:
selection = treeview.get_selection()
model, list_of_paths = selection.get_selected_rows()
for path in list_of_paths:
ids.append(model.get_value(model.get_iter(path), 0))
return ids
except:
# DEBUG: treeview have no selection or smth is broken
if __debug__:
print "c_search.py: __get_tv_selection_ids(): error on",
print "getting selected items"
return
return None
pass # end of class

View File

@@ -1,49 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
__all__ = ["model", "view", "controller", "observable", "observer", "support"]
__version = (1,2,2)
from model import Model, TreeStoreModel, ListStoreModel, TextBufferModel
from model_mt import ModelMT
from controller import Controller
from view import View
from observer import Observer
import observable
import adapters
def get_version(): return __version
def require(ver):
if isinstance(ver, str): ver = ver.split(".")
ver = tuple(map(int, ver))
if get_version() < ver:
raise AssertionError("gtkmvc required version '%s', found '%s'"\
% (ver, get_version()))
pass
return

View File

@@ -1,25 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2007 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
from gtkmvc.adapters.basic import Adapter, UserClassAdapter, RoUserClassAdapter
from gtkmvc.adapters.containers import StaticContainerAdapter

View File

@@ -1,428 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2007 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
import types
import gtk
import time
from gtkmvc.adapters.default import *
from gtkmvc.observer import Observer
# ----------------------------------------------------------------------
class Adapter (Observer):
def __init__(self, model, prop_name,
prop_read=None, prop_write=None,
value_error=None):
"""
Creates a new adapter that handles setting of value of a
model single model property when a corresponding widgets set
is changed and viceversa when the property is also
observable.
This class handles only assignments to properties. For other
kinds of setting (e.g. user-defined classes used as
observable properties, containers, etc.) use other types of
Adapters derived from this class.
prop_name is the model's property name (as a string). It is
possible to use a dotted notation to identify a property
contained into a hierarchy of models. For example 'a.b.c'
identifies property 'c' into model 'b' inside model 'a',
where model 'a' is an attribute of given top level model.
Last name must be an observable or non-observable attribute,
and previous names (if specified) must all refer to
instances of class Model. First name from the left must be
the name of a model instance inside the given model.
prop_{write,read} are two optional functions that apply
custom modifications to the value of the property before
setting and reading it. Both take a value and must return a
transformed value whose type must be compatible with the
type of the property.
value_error can be a function (or a method) to be called
when a ValueError exception occurs while trying to set a
wrong value for the property inside the model. The function
will receive: the adapter, the property name and the value
coming from the widget that offended the model.
"""
# registration is delayed, as we need to create possible
# listener before:
Observer.__init__(self)
self._prop_name = prop_name
self._prop_read = prop_read
self._prop_write = prop_write
self._value_error = value_error
self._wid = None
self._wid_info = {}
# this flag is set when self is changing the property or the
# widget, in order to avoid infinite looping.
self._itsme = False
self._connect_model(model)
return
def connect_widget(self, wid,
getter=None, setter=None,
signal=None, arg=None, update=True):
"""
Called when the widget is instantiated, and the adapter is
ready to connect the widget and the property inside the
observed model. arg is the (optional) argument that will be
passed when connecting the signal.
getter and setter are the (optional) methods used
for reading and writing the widget's value. When not
specified, default getter and setter will be guessed by
looking at the widget type the adapter will be connected
with. Guessing is carried out by querying information
specified into module 'adapters.default'.
Finally, if update is false, the widget will not be updated
"""
if self._wid_info.has_key(wid):
raise ValueError("Widget " + str(wid) + " was already connected")
wid_type = None
if None in (getter, setter, signal):
w = search_adapter_info(wid)
if getter is None: getter = w[GETTER]
if setter is None:
setter = w[SETTER]
wid_type = w[WIDTYPE]
pass
if signal is None: signal = w[SIGNAL]
pass
# saves information about the widget
self._wid_info[wid] = (getter, setter, wid_type)
# connects the widget
if signal:
if arg: wid.connect(signal, self._on_wid_changed, arg)
else: wid.connect(signal, self._on_wid_changed)
pass
self._wid = wid
# updates the widget:
if update: self.update_widget()
return
def update_model(self):
"""Forces the property to be updated from the value hold by
the widget. This method should be called directly by the
user in very unusual conditions."""
self._write_property(self._read_widget())
return
def update_widget(self):
"""Forces the widget to be updated from the property
value. This method should be called directly by the user
when the property is not observable, or in very unusual
conditions."""
self._write_widget(self._read_property())
return
# ----------------------------------------------------------------------
# Private methods
# ----------------------------------------------------------------------
def _connect_model(self, model):
"""
Used internally to connect the property into the model, and
register self as a value observer for that property"""
parts = self._prop_name.split(".")
if len(parts) > 1:
# identifies the model
models = parts[:-1]
for name in models:
model = getattr(model, name)
if not isinstance(model, gtkmvc.Model):
raise TypeError("Attribute '" + name +
"' was expected to be a Model, but found: " +
str(model))
pass
prop = parts[-1]
else: prop = parts[0]
# prop is inside model?
if not hasattr(model, prop):
raise ValueError("Attribute '" + prop +
"' not found in model " + str(model))
# is it observable?
if model.has_property(prop):
# we need to create an observing method before registering
self._add_method(self._get_observer_src(prop))
pass
self._prop = getattr(model, prop)
self._prop_name = prop
# registration of model:
self.register_model(model)
return
def _get_observer_src(self, prop_name):
"""This is the code for an value change observer"""
return """def property_%s_value_change(self, model, old, new):
if self._itsme or old == new: return
self._on_prop_changed()""" % prop_name
def _add_method(self, src):
"""Private service to add a new method to the instance,
given method code"""
from gtkmvc.support.utils import get_function_from_source
import new
func = get_function_from_source(src)
meth = new.instancemethod(func, self, self.__class__)
setattr(self, func.__name__, meth)
return
def _get_property(self):
"""Private method that returns the value currently stored
into the property"""
return getattr(self.get_model(), self._prop_name)
#return self._prop # bug fix reported by A. Dentella
def _set_property(self, val):
"""Private method that sets the value currently of the property."""
return setattr(self.get_model(), self._prop_name, val)
def _read_property(self, *args):
"""Returns the (possibly transformed) value that is stored
into the property"""
if self._prop_read: return self._prop_read(self._get_property(*args))
return self._get_property(*args)
def _write_property(self, val, *args):
"""Sets the value of property. Given val is transformed
accodingly to prop_write function when specified at
construction-time. A try to cast the value to the property
type is given."""
# 'finally' would be better here, but not supported in 2.4 :(
try:
totype = type(self._get_property(*args))
val_prop = self._cast_value(val, totype)
if self._prop_write: val_prop = self._prop_write(val_prop)
self._itsme = True
self._set_property(val_prop, *args)
except ValueError:
self._itsme = False
if self._value_error: self._value_error(self, self._prop_name, val)
else: raise
pass
except: self._itsme = False; raise
self._itsme = False
return
def _read_widget(self):
"""Returns the value currently stored into the widget, after
transforming it accordingly to possibly specified function."""
getter = self._wid_info[self._wid][0]
return getter(self._wid)
def _write_widget(self, val):
"""Writes value into the widget. If specified, user setter
is invoked."""
self._itsme = True
try:
setter = self._wid_info[self._wid][1]
wtype = self._wid_info[self._wid][2]
if wtype is not None: setter(self._wid, self._cast_value(val, wtype))
else: setter(self._wid, val)
finally:
self._itsme = False
pass
return
def _cast_value(self, val, totype):
"""Casts given val to given totype. Raises TypeError if not able to cast."""
t = type(val)
if issubclass(t, totype): return val
if issubclass(totype, types.StringType): return str(val)
if issubclass(t, types.StringType):
if issubclass(totype, types.IntType):
if val: return int(float(val))
return 0
if issubclass(totype, types.FloatType):
if val: return float(val)
return 0.0
pass
raise TypeError("Not able to cast " + str(t) + " to " + str(totype))
# ----------------------------------------------------------------------
# Callbacks and observation
# ----------------------------------------------------------------------
def _on_wid_changed(self, wid):
"""Called when the widget is changed"""
if self._itsme: return
self.update_model()
return
def _on_prop_changed(self):
"""Called by the observation code, when the value in the
observed property is changed"""
if not self._itsme: self.update_widget()
return
pass # end of class Adapter
#----------------------------------------------------------------------
class UserClassAdapter (Adapter):
"""
This class handles the communication between a widget and a
class instance (possibly observable) that is a property inside
the model. The value to be shown is taken and stored by using a
getter and a setter. getter and setter can be: names of user
class methods, bound or unbound methods of the user class, or a
function that will receive the user class instance and possible
arguments whose number depends on whether it is a getter or a
setter."""
def __init__(self, model, prop_name,
getter, setter,
prop_read=None, prop_write=None,
value_error=None):
Adapter.__init__(self, model, prop_name,
prop_read, prop_write, value_error)
self._getter = self._resolve_to_func(getter)
self._setter = self._resolve_to_func(setter)
return
# ----------------------------------------------------------------------
# Private methods
# ----------------------------------------------------------------------
def _resolve_to_func(self, what):
"""This method resolves whatever is passed: a string, a
bound or unbound method, a function, to make it a
function. This makes internal handling of setter and getter
uniform and easier."""
if isinstance(what, types.StringType):
what = getattr(Adapter._get_property(self), what)
pass
# makes it an unbounded function if needed
if type(what) == types.MethodType: what = what.im_func
if not type(what) == types.FunctionType: raise TypeError("Expected a method name, a method or a function")
return what
def _get_observer_src(self, prop_name):
"""This is the code for a method after_change observer"""
return """def property_%s_after_change(self, model, \
instance, meth_name, res, args, kwargs):
if self._itsme: return
self._on_prop_changed(instance, meth_name, res, args, kwargs)""" % prop_name
def _on_prop_changed(self, instance, meth_name, res, args, kwargs):
"""Called by the observation code, when a modifying method
is called"""
Adapter._on_prop_changed(self)
return
def _get_property(self, *args):
"""Private method that returns the value currently stored
into the property"""
val = self._getter(Adapter._get_property(self), *args)
if self._prop_read: return self._prop_read(val, *args)
return val
def _set_property(self, val, *args):
"""Private method that sets the value currently of the property"""
if self._prop_write: val = self._prop_write(val)
return self._setter(Adapter._get_property(self), val, *args)
pass # end of class UserClassAdapter
# ----------------------------------------------------------------------
#----------------------------------------------------------------------
class RoUserClassAdapter (UserClassAdapter):
"""
This class is for Read-Only user classes. RO classes are those
whose setting methods do not change the instance, but return a
new instance that has been changed accordingly to the setters
semantics. An example is python datetime class, whose replace
method does not change the instance it is invoked on, but
returns a new datetime instance.
This class is likely to be used very rarely.
"""
def __init__(self, model, prop_name,
getter, setter,
prop_read=None, prop_write=None,
value_error=None):
UserClassAdapter.__init__(self, model, prop_name,
getter, setter,
prop_read, prop_write, value_error)
return
# ----------------------------------------------------------------------
# Private methods
# ----------------------------------------------------------------------
def _set_property(self, val, *args):
"""Private method that sets the value currently of the property"""
val = UserClassAdapter._set_property(self, val, *args)
if val: Adapter._set_property(self, val, *args)
return val
pass # end of class RoUserClassAdapter
# ----------------------------------------------------------------------

View File

@@ -1,217 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2007 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
import types
import gtk
from gtkmvc.adapters.basic import UserClassAdapter, Adapter
from gtkmvc.adapters.default import *
from gtkmvc.observer import Observer
from gtkmvc.support.wrappers import ObsMapWrapper
# ----------------------------------------------------------------------
class StaticContainerAdapter (UserClassAdapter):
"""
This class can be used to bound a set of widgets to a property
that is a container, like a tuple, a list or a map, or in
general a class that implements __getitem__ and __setitem__
methods.
From the other hand, the set of widgets can be a list provided
by the user, or a container widget like a Box, a notebook, etc.
Widgets will be linked by their position when the property is
list-like, or by their name when the property is map-like.
This class supports only properties that are static containers,
i.e. those containers that do not change their length
dynamically. If the container grows up in length, no change will
occur in the view-side.
"""
def __init__(self, model, prop_name,
prop_read=None, prop_write=None, value_error=None):
UserClassAdapter.__init__(self, model, prop_name,
lambda c,i: c.__getitem__(i),
lambda c,v,i: c.__setitem__(i,v),
prop_read, prop_write,
value_error)
prop = Adapter._get_property(self)
#prop = self._get_property() # bug fix reported by A. Dentella
if not (hasattr(prop, "__getitem__") and
hasattr(prop, "__setitem__")):
raise TypeError("Property " + self._prop_name +
" is not a valid container")
self._prop_is_map = isinstance(prop, types.DictType) or \
isinstance(prop, ObsMapWrapper)
# contained widgets
self._idx2wid = {}
self._wid2idx = {}
self._widgets = None
return
def connect_widget(self, wid, getters=None, setters=None,
signals=None, arg=None):
"""
Called when the widget is instantiated, and the adapter is
ready to connect the widgets inside it (if a container) or
each widget if wid is a list of widgets. getters and setters
can be None, a function or a list or a map of
functions. signals can be None, a signal name, or a list or
a map of signal names. When maps are used, keys can be
widgets or widget names. The length of the possible lists or
maps must be lesser or equal to the number of widgets that
will be connected.
"""
if isinstance(wid, gtk.Container): self._widgets = wid.get_children()
elif isinstance(wid, types.ListType) or isinstance(wid, types.TupleType): self._widgets = wid
else: raise TypeError("widget must be either a gtk.Container or a list or tuple")
# prepares the mappings:
for idx, w in enumerate(self._widgets):
if self._prop_is_map: idx=w.get_name()
self._idx2wid[idx] = w
self._wid2idx[w] = idx
pass
# prepares the lists for signals
getters = self.__handle_par("getters", getters)
setters = self.__handle_par("setters", setters)
signals = self.__handle_par("signals", signals)
for wi,ge,se,si in zip(self._widgets, getters, setters, signals):
if type(ge) == types.MethodType: ge = ge.im_func
if type(se) == types.MethodType: se = se.im_func
UserClassAdapter.connect_widget(self, wi, ge, se, si, arg, False)
pass
self.update_widget()
self._wid = wid
return
def update_model(self, idx=None):
"""Updates the value of property at given index. If idx is
None, all controlled indices will be updated. This method
should be called directly by the user in very unusual
conditions."""
if idx is None:
for w in self._widgets:
idx = self._get_idx_from_widget(w)
self._write_property(self._read_widget(idx), idx)
pass
pass
else: self._write_property(self._read_widget(idx), idx)
return
def update_widget(self, idx=None):
"""Forces the widget at given index to be updated from the
property value. If index is not given, all controlled
widgets will be updated. This method should be called
directly by the user when the property is not observable, or
in very unusual conditions."""
if idx is None:
for w in self._widgets:
idx = self._get_idx_from_widget(w)
self._write_widget(self._read_property(idx), idx)
pass
else: self._write_widget(self._read_property(idx), idx)
return
# ----------------------------------------------------------------------
# Private methods
# ----------------------------------------------------------------------
def _get_idx_from_widget(self, wid):
"""Given a widget, returns the corresponding index for the
model. Returned value can be either an integer or a string"""
return self._wid2idx[wid]
def _get_widget_from_idx(self, idx):
"""Given an index, returns the corresponding widget for the view.
Given index can be either an integer or a string"""
return self._idx2wid[idx]
def _read_widget(self, idx):
sav = self._wid
self._wid = self._get_widget_from_idx(idx)
val = UserClassAdapter._read_widget(self)
self._wid = sav
return val
def _write_widget(self, val, idx):
sav = self._wid
self._wid = self._get_widget_from_idx(idx)
UserClassAdapter._write_widget(self, val)
self._wid = sav
return
# This is a private service to factorize code of connect_widget
def __handle_par(self, name, par):
if par is None or type(par) in (types.FunctionType,
types.MethodType, types.StringType):
par = [par] * len(self._widgets)
pass
elif isinstance(par, types.DictType):
val = []
for w in self._widgets:
if par.has_key(w): val.append(par[w])
elif par.has_key(w.get_name()): val.append(par[w.get_name()])
else: val.append(None)
pass
par = val
pass
elif isinstance(par, types.ListType) or isinstance(par, types.TupleType):
par = list(par)
par.extend([None]*(len(self._widgets)-len(par)))
pass
else: raise TypeError("Parameter %s has an invalid type (should be None, a sequence or a string)" % name)
return par
# Callbacks:
def _on_wid_changed(self, wid):
"""Called when the widget is changed"""
if self._itsme: return
self.update_model(self._get_idx_from_widget(wid))
return
def _on_prop_changed(self, instance, meth_name, res, args, kwargs):
"""Called by the observation code, we are interested in
__setitem__"""
if not self._itsme and meth_name == "__setitem__": self.update_widget(args[0])
return
pass # end of class StaticContainerAdapter

View File

@@ -1,55 +0,0 @@
__all__ = ("search_adapter_info",
"SIGNAL", "GETTER", "SETTER", "WIDTYPE")
import types
import gtk
# ----------------------------------------------------------------------
# This list defines a default behavior for widgets.
# If no particular behaviour has been specified, adapters will
# use information contained into this list to create themself.
# This list is ordered: the earlier a widget occurs, the better it
# will be matched by the search function.
# ----------------------------------------------------------------------
__def_adapter = [ # class, default signal, getter, setter, value type
(gtk.Entry, "changed", gtk.Entry.get_text, gtk.Entry.set_text, types.StringType),
(gtk.Label, None, gtk.Label.get_text, gtk.Label.set_text, types.StringType),
(gtk.Arrow, None, lambda a: a.get_property("arrow-type"),
lambda a,v: a.set(v,a.get_property("shadow-type")), gtk.ArrowType),
(gtk.ToggleButton, "toggled", gtk.ToggleButton.get_active, gtk.ToggleButton.set_active, types.BooleanType),
(gtk.CheckMenuItem, "toggled", gtk.CheckMenuItem.get_active, gtk.CheckMenuItem.set_active, types.BooleanType),
(gtk.Expander, "activate", lambda w: not w.get_expanded(), gtk.Expander.set_expanded, types.BooleanType),
(gtk.ColorButton, "color-set", gtk.ColorButton.get_color, gtk.ColorButton.set_color, gtk.gdk.Color),
(gtk.ColorSelection, "color-changed", gtk.ColorSelection.get_current_color, gtk.ColorSelection.set_current_color, gtk.gdk.Color),
]
if gtk.pygtk_version >= (2,10,0):
# conditionally added support
__def_adapter.append(
(gtk.LinkButton, "clicked", gtk.LinkButton.get_uri, gtk.LinkButton.set_uri, types.StringType))
pass
# constants to access values:
SIGNAL =1
GETTER =2
SETTER =3
WIDTYPE =4
# ----------------------------------------------------------------------
# To optimize the search
__memoize__ = {}
def search_adapter_info(wid):
"""Given a widget returns the default tuple found in __def_adapter"""
t = type(wid)
if __memoize__.has_key(t): return __memoize__[t]
for w in __def_adapter:
if isinstance(wid, w[0]):
__memoize__[t] = w
return w
pass
raise TypeError("Adapter type " + str(t) + " not found among supported adapters")

View File

@@ -1,218 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
from gtkmvc.observer import Observer
import types
class Controller (Observer):
"""
We put all of our gtk signal handlers into a class. This lets
us bind all of them at once, because their names are in the
class dict. This class automatically register its instances as
observers into the corresponding model. Also, when a view is
created, the view calls method register_view, which can be
oveloaded in order to connect signals and perform other specific
operation. A controller possibly handles and contains also a set
of adapters that makes easier to connect widgets and observable
properties into the model.
parameter spurious controls the way spurious value change
notifications are handled. If True, assignments to observable
properties that do not actually change the value are
notified anyway."""
def __init__(self, model, spurious=False):
Observer.__init__(self, model, spurious)
self.view = None
self.__adapters = []
return
def register_view(self, view):
"""
This method is called by the framework when registering a
view. Derived classes can exploit this call to connect
signals manually, create and connect treeview, textview,
etc.
"""
assert(self.view is None)
self.view = view
self.register_adapters()
return
def register_adapters(self):
"""
This method is called by register_view during view
registration process, when it is time to possibly create all
adapters. model and view can safely be taken from self.model
and self.view. Derived classes can specilize this method. In
this implementation the method does nothing.
"""
assert(self.model is not None)
assert(self.view is not None)
return
def adapt(self, *args):
"""
Adapts a set of (observable) properties and a set of
widgets, by connecting them.
This method can be used to simplify the creation of one or
more adapters, by letting the framework do most of the work
needed to connect ('adapt') properties from one hand, and
widgets on the other.
This method is provided in three flavours:
1. An instance of an Adapter class can be created by the
caller and passed as a unique argument. The adapter must
be already fully configured.
2. The name of a model's property is passed as a unique
argument. A correponding widget name is searched and if
found, an adapter is created. Name matching is performed
by searching into view's widget names for words that end
with the given property name. Matching is case
insensitive and words can be separated by spaces,
underscore (_) and CapitalizedWords. For example property
'prop' will match widget 'cb_prop'. If no matches or
multiple matches are found, a ValueError will be raised.
The way names are matched can be customized by deriving
method match_prop_name.
3. Two strings can be passed, respectively containing the
name of an observable property in the model, and the name
of a widget in the view.
Flavour 1 allows for a full control, but flavour 2 and 3
make easier to create adpaters with basic (default)
behaviour.
This method can be called into the method register_adapters
which derived classes can specialise.
"""
# checks arguments
n = len(args)
if n not in (1,2): raise TypeError("adapt() takes 1 or 2 arguments (%d given)" % n)
if n == 1: #one argument
from gtkmvc.adapters.basic import Adapter
if isinstance(args[0], Adapter): adapters = (args[0],)
elif isinstance(args[0], types.StringType):
prop_name = args[0]
names = []
for k in self.view:
if self.__match_prop_name(prop_name, k): names.append(k)
pass
if len(names) != 1:
raise ValueError("%d widget candidates match property '%s': %s" % \
(len(names), prop_name, names))
wid_name = names[0]
adapters = self.__create_adapters__(prop_name, wid_name)
pass
else: raise TypeError("Argument of adapt() must be an Adapter or a string")
else: # two arguments
if not (isinstance(args[0], types.StringType) and
isinstance(args[1], types.StringType)):
raise TypeError("Arguments of adapt() must be two strings")
# retrieves both property and widget, and creates an adapter
prop_name, wid_name = args
adapters = self.__create_adapters__(prop_name, wid_name)
pass
for ad in adapters: self.__adapters.append(ad)
return
def __match_prop_name(self, prop_name, wid_name):
"""
Used internally when searching for a suitable widget. To customize
its behaviour, re-implement this method into derived classes
"""
return wid_name.lower().endswith(prop_name.lower())
def __create_adapters__(self, prop_name, wid_name):
"""
Private service that looks at property and widgets types,
and possibly creates one or more (best) fitting adapters
that are returned as a list.
"""
from gtkmvc.adapters.basic import Adapter, RoUserClassAdapter
from gtkmvc.adapters.containers import StaticContainerAdapter
import gtk
res = []
wid = self.view[wid_name]
if wid is None: raise ValueError("Widget '%s' not found" % wid_name)
# Decides the type of adapters to be created.
if isinstance(wid, gtk.Calendar):
# calendar creates three adapter for year, month and day
ad = RoUserClassAdapter(self.model, prop_name,
lambda d: d.year, lambda d,y: d.replace(year=y))
ad.connect_widget(wid, lambda c: c.get_date()[0],
lambda c,y: c.select_month(c.get_date()[1], y),
"day-selected")
res.append(ad) # year
ad = RoUserClassAdapter(self.model, prop_name,
lambda d: d.month, lambda d,m: d.replace(month=m))
ad.connect_widget(wid, lambda c: c.get_date()[1]+1,
lambda c,m: c.select_month(m-1, c.get_date()[0]),
"day-selected")
res.append(ad) # month
ad = RoUserClassAdapter(self.model, prop_name,
lambda d: d.day, lambda d,v: d.replace(day=v))
ad.connect_widget(wid, lambda c: c.get_date()[2],
lambda c,d: c.select_day(d),
"day-selected")
res.append(ad) # day
return res
try: # tries with StaticContainerAdapter
ad = StaticContainerAdapter(self.model, prop_name)
ad.connect_widget(wid)
res.append(ad)
except TypeError:
# falls back to a simple adapter
ad = Adapter(self.model, prop_name)
ad.connect_widget(wid)
res.append(ad)
pass
return res
pass # end of class Controller

View File

@@ -1,336 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
import support.metaclasses
from support.wrappers import ObsWrapperBase
from observable import Signal
class Model (object):
"""
This class is the application model base class. It handles a set
of observable properties which you are interested in showing by
one ore more view - via one or more observers of course. The
mechanism is the following:
1. You are interested in showing a set of model property, that
you can declare in the __properties__ member map.
2. You define one or more observers that observe one or more
properties you registered. When someone changes a property
value the model notifies the changing to each observer.
The property-observer[s] association is given by the implicit
rule in observers method names: if you want the model notified
the changing event of the value of the property 'p' you have to
define the method called 'property_p_value_change' in each
listening observer class.
Notice that tipically 'controllers' implement the observer
pattern. The notification method gets the emitting model, the
old value for the property and the new one. Properties
functionalities are automatically provided by the
ObservablePropertyMeta meta-class."""
__metaclass__ = support.metaclasses.ObservablePropertyMeta
__properties__ = {} # override this
def __init__(self):
object.__init__(self)
self.__observers = []
# keys are properties names, values are methods inside the observer:
self.__value_notifications = {}
self.__instance_notif_before = {}
self.__instance_notif_after = {}
self.__signal_notif = {}
for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
self.register_property(key)
pass
return
def register_property(self, name):
"""Registers an existing property to be monitored, and sets
up notifiers for notifications"""
if not self.__value_notifications.has_key(name):
self.__value_notifications[name] = []
pass
# registers observable wrappers
prop = getattr(self, "_prop_%s" % name)
if isinstance(prop, ObsWrapperBase):
prop.__set_model__(self, name)
if isinstance(prop, Signal):
if not self.__signal_notif.has_key(name):
self.__signal_notif[name] = []
pass
pass
else:
if not self.__instance_notif_before.has_key(name):
self.__instance_notif_before[name] = []
pass
if not self.__instance_notif_after.has_key(name):
self.__instance_notif_after[name] = []
pass
pass
pass
return
def has_property(self, name):
"""Returns true if given property name refers an observable
property inside self or inside derived classes"""
return self.__properties__.has_key(name) or \
self.__derived_properties__.has_key(name)
def register_observer(self, observer):
if observer in self.__observers: return # not already registered
self.__observers.append(observer)
for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
self.__add_observer_notification(observer, key)
pass
return
def unregister_observer(self, observer):
if observer not in self.__observers: return
for key in (self.__properties__.keys() + self.__derived_properties__.keys()):
self.__remove_observer_notification(observer, key)
pass
self.__observers.remove(observer)
return
def _reset_property_notification(self, prop_name):
"""Called when it has be done an assignment that changes the
type of a property or the instance of the property has been
changed to a different instance. In this case it must be
unregistered and registered again"""
self.register_property(prop_name)
for observer in self.__observers:
self.__remove_observer_notification(observer, prop_name)
self.__add_observer_notification(observer, prop_name)
pass
return
def __add_observer_notification(self, observer, prop_name):
"""Searches in the observer for any possible listener, and
stores the notification methods to be called later"""
method_name = "property_%s_value_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method not in self.__value_notifications[prop_name]:
list.append(self.__value_notifications[prop_name], method)
pass
pass
# is it a signal?
orig_prop = getattr(self, "_prop_%s" % prop_name)
if isinstance(orig_prop, Signal):
method_name = "property_%s_signal_emit" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method not in self.__signal_notif[prop_name]:
list.append(self.__signal_notif[prop_name], method)
pass
pass
pass
# is it an instance change notification type?
elif isinstance(orig_prop, ObsWrapperBase):
method_name = "property_%s_before_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method not in self.__instance_notif_before[prop_name]:
list.append(self.__instance_notif_before[prop_name], method)
pass
pass
method_name = "property_%s_after_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method not in self.__instance_notif_after[prop_name]:
list.append(self.__instance_notif_after[prop_name], method)
pass
pass
pass
return
def __remove_observer_notification(self, observer, prop_name):
if self.__value_notifications.has_key(prop_name):
method_name = "property_%s_value_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method in self.__value_notifications[prop_name]:
self.__value_notifications[prop_name].remove(method)
pass
pass
pass
orig_prop = getattr(self, "_prop_%s" % prop_name)
# is it a signal?
if isinstance(orig_prop, Signal):
method_name = "property_%s_signal_emit" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method in self.__signal_notif[prop_name]:
self.__signal_notif[prop_name].remove(method)
pass
pass
pass
# is it an instance change notification type?
elif isinstance(orig_prop, ObsWrapperBase):
if self.__instance_notif_before.has_key(prop_name):
method_name = "property_%s_before_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method in self.__instance_notif_before[prop_name]:
self.__instance_notif_before[prop_name].remove(method)
pass
pass
pass
if self.__instance_notif_after.has_key(prop_name):
method_name = "property_%s_after_change" % prop_name
if hasattr(observer, method_name):
method = getattr(observer, method_name)
if method in self.__instance_notif_after[prop_name]:
self.__instance_notif_after[prop_name].remove(method)
pass
pass
pass
pass
return
def __notify_observer__(self, observer, method, *args, **kwargs):
"""This can be overridden by derived class in order to call
the method in a different manner (for example, in
multithreading, or a rpc, etc.) This implementation simply
calls the given method with the given arguments"""
return method(*args, **kwargs)
# ---------- Notifiers:
def notify_property_value_change(self, prop_name, old, new):
assert(self.__value_notifications.has_key(prop_name))
for method in self.__value_notifications[prop_name] :
obs = method.im_self
# notification occurs checking spuriousness of the observer
if old != new or obs.accepts_spurious_change():
self.__notify_observer__(obs, method,
self, old, new) # notifies the change
pass
pass
return
def notify_method_before_change(self, prop_name, instance, meth_name,
args, kwargs):
assert(self.__instance_notif_before.has_key(prop_name))
for method in self.__instance_notif_before[prop_name] :
self.__notify_observer__(method.im_self, method, self, instance,
meth_name, args, kwargs) # notifies the change
pass
return
def notify_method_after_change(self, prop_name, instance, meth_name,
res, args, kwargs):
assert(self.__instance_notif_after.has_key(prop_name))
for method in self.__instance_notif_after[prop_name] :
self.__notify_observer__(method.im_self, method, self, instance,
meth_name, res, args, kwargs) # notifies the change
pass
return
def notify_signal_emit(self, prop_name, args, kwargs):
assert(self.__signal_notif.has_key(prop_name))
for method in self.__signal_notif[prop_name] :
self.__notify_observer__(method.im_self, method, self,
args, kwargs) # notifies the signal emit
pass
return
pass # end of class Model
# ----------------------------------------------------------------------
import gtk
# ----------------------------------------------------------------------
class TreeStoreModel (Model, gtk.TreeStore):
"""Use this class as base class for your model derived by
gtk.TreeStore"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
def __init__(self, column_type, *args):
Model.__init__(self)
gtk.TreeStore.__init__(self, column_type, *args)
return
pass
# ----------------------------------------------------------------------
class ListStoreModel (Model, gtk.ListStore):
"""Use this class as base class for your model derived by
gtk.ListStore"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
def __init__(self, column_type, *args):
Model.__init__(self)
gtk.ListStore.__init__(self, column_type, *args)
return
pass
# ----------------------------------------------------------------------
class TextBufferModel (Model, gtk.TextBuffer):
"""Use this class as base class for your model derived by
gtk.TextBuffer"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMeta
def __init__(self, table=None):
Model.__init__(self)
gtk.TextBuffer.__init__(self, table)
return
pass

View File

@@ -1,124 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2006 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
from gtkmvc.model import Model
import support.metaclasses
try: import threading as _threading
except ImportError: import dummy_threading as _threading
import gobject
if hasattr(gobject, "threads_init"): gobject.threads_init()
else: import gtk; gtk.threads_init()
class ModelMT (Model):
"""A base class for models whose observable properties can be
changed by threads different than gtk main thread. Notification is
performed by exploiting the gtk idle loop only if needed,
otherwise the standard notification system (direct method call) is
used. In this model, the observer is expected to run in the gtk
main loop thread."""
__metaclass__ = support.metaclasses.ObservablePropertyMetaMT
def __init__(self):
Model.__init__(self)
self.__observer_threads = {}
self._prop_lock = _threading.Lock()
return
def register_observer(self, observer):
Model.register_observer(self, observer)
self.__observer_threads[observer] = _threading.currentThread()
return
def unregister_observer(self, observer):
Model.unregister_observer(self, observer)
del self.__observer_threads[observer]
return
# ---------- Notifiers:
def __notify_observer__(self, observer, method, *args, **kwargs):
"""This makes a call either through the gtk.idle list or a
direct method call depending whether the caller's thread is
different from the observer's thread"""
assert self.__observer_threads.has_key(observer)
if _threading.currentThread() == self.__observer_threads[observer]:
# standard call
return Model.__notify_observer__(self, observer, method,
*args, **kwargs)
# multi-threading call
gobject.idle_add(self.__idle_callback, observer, method, args, kwargs)
return
def __idle_callback(self, observer, method, args, kwargs):
method(*args, **kwargs)
return False
pass # end of class
import gtk
# ----------------------------------------------------------------------
class TreeStoreModelMT (ModelMT, gtk.TreeStore):
"""Use this class as base class for your model derived by
gtk.TreeStore"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMetaMT
def __init__(self, column_type, *args):
ModelMT.__init__(self)
gtk.TreeStore.__init__(self, column_type, *args)
return
pass
# ----------------------------------------------------------------------
class ListStoreModelMT (ModelMT, gtk.ListStore):
"""Use this class as base class for your model derived by
gtk.ListStore"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMetaMT
def __init__(self, column_type, *args):
ModelMT.__init__(self)
gtk.ListStore.__init__(self, column_type, *args)
return
pass
# ----------------------------------------------------------------------
class TextBufferModelMT (ModelMT, gtk.TextBuffer):
"""Use this class as base class for your model derived by
gtk.TextBuffer"""
__metaclass__ = support.metaclasses.ObservablePropertyGObjectMetaMT
def __init__(self, table=None):
ModelMT.__init__(self)
gtk.TextBuffer.__init__(self, table)
return
pass

View File

@@ -1,69 +0,0 @@
# -------------------------------------------------------------------------
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (C) 2006 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.ridge, MA 02139, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author <cavada@fbk.eu>.
# -------------------------------------------------------------------------
from support import decorators
from support.wrappers import ObsWrapperBase
# ----------------------------------------------------------------------
class Observable (ObsWrapperBase):
def __init__(self):
ObsWrapperBase.__init__(self)
return
pass # end of class
@decorators.good_decorator
def observed(func):
"""Use this decorator to make your class methods observable.
Your observer will receive at most two notifications:
- property_<name>_before_change
- property_<name>_after_change
"""
def wrapper(*args, **kwargs):
self = args[0]
assert(isinstance(self, Observable))
self._notify_method_before(self, func.__name__, args, kwargs)
res = func(*args, **kwargs)
self._notify_method_after(self, func.__name__, res, args, kwargs)
return res
return wrapper
# ----------------------------------------------------------------------
class Signal (Observable):
"""Base class for signals properties"""
def __init__(self):
Observable.__init__(self)
return
def emit(self, *args, **kwargs):
return self.__get_model__().notify_signal_emit(
self.__get_prop_name__(), args, kwargs)
pass # end of class

View File

@@ -1,91 +0,0 @@
# -------------------------------------------------------------------------
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (C) 2006 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
# -------------------------------------------------------------------------
class Observer (object):
"""Use this class as base class of all observers"""
def __init__(self, model=None, spurious=False):
"""
When parameter spurious is set to False
(default value) the observer declares that it is not
interested in receiving value-change notifications when
property's value does not really change. This happens when a
property got assigned to a value that is the same it had
before being assigned.
A notification was used to be sent to the observer even in
this particular condition, because spurious (non-changing)
assignments were used as signals when signals were not
supported by early version of the framework. The observer
was in charge of deciding what to do with spurious
assignments, by checking if the old and new values were
different at the beginning of the notification code. With
latest version providing new notification types like
signals, this requirement seems to be no longer needed, and
delivering a notification is no longer a sensible
behaviour.
This is the reason for providing parameter
spurious that changes the previous behaviour
but keeps availability of a possible backward compatible
feature.
"""
self.model = None
self.__accepts_spurious__ = spurious
self.register_model(model)
return
def register_model(self, model):
self.unregister_model()
self.model = model
if self.model: self.model.register_observer(self)
return
def accepts_spurious_change(self):
"""
Returns True if this observer is interested in receiving
spurious value changes. This is queried by the model when
notifying a value change."""
return self.__accepts_spurious__
def unregister_model(self):
if self.model:
self.model.unregister_observer(self)
self.model = None
pass
return
def __del__(self):
self.unregister_model()
return
def get_model(self): return self.model
pass # end of class

View File

@@ -1,26 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
__all__ = ["metaclass_base", "metaclasses", "wrappers", "decorators",
"factories"]

View File

@@ -1,44 +0,0 @@
# -------------------------------------------------------------------------
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (C) 2006 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
# -------------------------------------------------------------------------
# This file contains decorators to be used (privately) by other parts
# of the framework
def good_decorator(decorator):
"""This decorator makes decorators behave well wrt to decorated
functions names, doc, etc."""
def new_decorator(f):
g = decorator(f)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
g.__dict__.update(f.__dict__)
return g
new_decorator.__name__ = decorator.__name__
new_decorator.__doc__ = decorator.__doc__
new_decorator.__dict__.update(decorator.__dict__)
return new_decorator

View File

@@ -1,24 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.

View File

@@ -1,86 +0,0 @@
# -------------------------------------------------------------------------
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (C) 2008 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
# -------------------------------------------------------------------------
import new
from gtkmvc import Model
from noconflict import get_noconflict_metaclass
class ModelFactory (object):
"""This factory constructs classes for models. Use it to build
the classes to derive your own models"""
__memoized = {}
@staticmethod
def __fix_bases(base_classes, have_mt):
"""This function check whether base_classes contains a Model
instance. If not, choose the best fitting class for
model. Furthermore, it makes the list in a cannonical
ordering form in a way that ic can be used as memoization
key"""
fixed = list(base_classes)
contains_model = False
for b in fixed:
if isinstance(fixed, Model): contains_model = True; break
pass
# adds a model when user is lazy
if not contains_model:
if have_mt:
from gtkmvc.model_mt import ModelMT
fixed.insert(0, ModelMT)
else: fixed.insert(0, Model)
pass
class ModelFactoryWrap (object):
__metaclass__ = get_noconflict_metaclass(tuple(fixed), (), ())
def __init__(self, *args, **kwargs): pass
pass
fixed.append(ModelFactoryWrap)
fixed.sort()
return tuple(fixed)
@staticmethod
def make(base_classes=(), have_mt=False):
"""Use this static method to build a model class that
possibly derives from other classes. If have_mt is True,
then returned class will take into account multi-threading
issues when dealing with observable properties."""
good_bc = ModelFactory.__fix_bases(base_classes, have_mt)
print "Base classes are:", good_bc
key = "".join(map(str, good_bc))
if ModelFactory.__memoized.has_key(key):
return ModelFactory.__memoized[key]
cls = new.classobj('', good_bc, {'__module__': '__main__', '__doc__': None})
ModelFactory.__memoized[key] = cls
return cls
#__
#make = staticmethod(make)
pass # end of class

View File

@@ -1,240 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
import new
import re
import types
import gtkmvc.support.wrappers as wrappers
from gtkmvc.support.utils import get_function_from_source
# ----------------------------------------------------------------------
VERBOSE_LEVEL = 5
class PropertyMeta (type):
"""This is a meta-class that provides auto-property support.
The idea is to allow programmers to define some properties which
will be automatically connected to auto-generated code which handles
access to those properties.
How can you use this meta-class?
First, '__metaclass__ = PropertyMeta' must be class member of the class
you want to make the automatic properties handling.
Second, '__properties__' must be a map containing the properties names
as keys, values will be initial values for properties.
That's all: after the instantiation, your class will contain all properties
you named inside '__properties__'. Each of them will be also associated
to a couple of automatically-generated functions which get and set the
property value inside a generated member variable.
About names: suppose the property is called 'x'. The generated variable
(which keeps the real value of the property x) is called _prop_x.
The getter is called get_prop_x(self), and the setter is called
'set_prop_x(self, value)'.
Customization:
The base implementation of getter is to return the value stored in the
variable associated to the property. The setter simply sets its value.
Programmers can override basic behaviour for getters or setters simply by
defining their getters and setters (see at the names convention above).
The customized function can lie everywhere in the user classes hierarchy.
Every overridden function will not be generated by the metaclass.
To supply your own methods is good for few methods, but can result in a
very unconfortable way for many methods. In this case you can extend
the meta-class, and override methods get_[gs]etter_source with your
implementation (this can be probably made better).
An example is provided in meta-class PropertyMetaVerbose below.
"""
def __init__(cls, name, bases, dict):
"""class constructor"""
properties = {}
type.__init__(cls, name, bases, dict)
props = getattr(cls, '__properties__', {})
setattr(cls, '__derived_properties__', {})
der_props = getattr(cls, '__derived_properties__')
# Calculates derived properties:
for base in bases:
maps = ( getattr(base, '__properties__', {}),
getattr(base, '__derived_properties__', {}) )
for _map in maps:
for p in _map.keys():
if not props.has_key(p) and not der_props.has_key(p):
der_props[p] = _map[p]
pass
pass
pass
pass
# Generates code for all properties (but not for derived props):
props = getattr(cls, '__properties__', {})
for prop in props.keys():
type(cls).__create_prop_accessors__(cls, prop, props[prop])
pass
return
def __msg__(cls, msg, level):
"""if level is less or equal to VERBOSE_LEVEL, ths message will
be printed"""
if level <= VERBOSE_LEVEL: print msg
return
def __create_prop_accessors__(cls, prop_name, default_val):
"""Private method that creates getter and setter, and the
corresponding property"""
getter_name = "get_prop_%s" % prop_name
setter_name = "set_prop_%s" % prop_name
members_names = cls.__dict__.keys()
# checks if accessors are already defined:
if getter_name not in members_names:
src = type(cls).get_getter_source(cls, getter_name, prop_name)
func = get_function_from_source(src)
setattr(cls, getter_name, func)
else:
cls.__msg__("Warning: Custom member '%s' overloads generated accessor of property '%s'" \
% (getter_name, prop_name), 2)
pass
if setter_name not in members_names:
src = type(cls).get_setter_source(cls, setter_name, prop_name)
func = get_function_from_source(src)
setattr(cls, setter_name, func)
else:
cls.__msg__("Warning: Custom member '%s' overloads generated accessor of property '%s'" \
% (setter_name, prop_name), 2)
pass
prop = property(getattr(cls, getter_name), getattr(cls, setter_name))
if prop_name in members_names:
cls.__msg__("Warning: automatic property builder overrids property %s in class %s" \
% (prop_name, cls.__name__), 2)
pass
setattr(cls, prop_name, prop)
varname = "_prop_%s" % prop_name
if not varname in members_names: cls.__create_property(varname, default_val)
else: cls.__msg__("Warning: automatic property builder found a possible clashing for variable %s inside class %s" \
% (varname, cls.__name__), 2)
return
def __create_property(cls, name, default_val):
setattr(cls, name, cls.create_value(name, default_val))
return
def check_value_change(cls, old, new):
"""Checks whether the value of the property changed in type
or if the instance has been changed to a different instance.
If true, a call to model._reset_property_notification should
be called in order to re-register the new property instance
or type"""
return type(old) != type(new) or \
isinstance(old, wrappers.ObsWrapperBase) and (old != new)
def create_value(cls, prop_name, val, model=None):
"""This is used to create a value to be assigned to a
property. Depending on the type of the value, different values
are created and returned. For example, for a list, a
ListWrapper is created to wrap it, and returned for the
assignment. model is different from model when the value is
changed (a model exists). Otherwise, during property creation
model is None"""
if isinstance(val, tuple):
# this might be a class instance to be wrapped
if len(val) == 3 and \
isinstance(val[1], val[0]) and \
(isinstance(val[2], tuple) or isinstance(val[2], list)):
res = wrappers.ObsUserClassWrapper(val[1], val[2])
if model: res.__set_model__(model, prop_name)
return res
pass
elif isinstance(val, list):
res = wrappers.ObsListWrapper(val)
if model: res.__set_model__(model, prop_name)
return res
elif isinstance(val, dict):
res = wrappers.ObsMapWrapper(val)
if model: res.__set_model__(model, prop_name)
return res
return val
# ------------------------------------------------------------
# Services
# ------------------------------------------------------------
# Override these:
def get_getter_source(cls, getter_name, prop_name):
"""This must be overridden if you need a different implementation.
Simply the generated implementation returns the variable name
_prop_name"""
return "def %s(self): return self._prop_%s" % (getter_name, prop_name)
def get_setter_source(cls, setter_name, prop_name):
"""This must be overridden if you need a different implementation.
Simply the generated implementation sets the variable _prop_name"""
return "def %s(self, val): self._prop_%s = val" \
% (setter_name, prop_name)
pass # end of class
# ----------------------------------------------------------------------
# What follows underneath is a set of examples of usage
## class PropertyMetaVerbose (PropertyMeta):
## """An example of customization"""
## def get_getter_source(cls, getter_name, prop_name):
## return "def %s(self): print 'Calling %s!'; return self._prop_%s" \
## % (getter_name, getter_name, prop_name)
## def get_setter_source(cls, setter_name, prop_name):
## return "def %s(self, val): print 'Calling %s!'; self._prop_%s = val;" \
## % (setter_name, setter_name, prop_name)
## pass #end of class
# ----------------------------------------------------------------------
## class User:
## """An example of usage"""
## __metaclass__ = PropertyMetaVerbose
## __properties__ = {'x':10, 'y':20}
## def __init__(self):
## print self.x # x is 10
## self.x = self.y + 10 # x is now 30
## return
## pass
# ----------------------------------------------------------------------

View File

@@ -1,82 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2005 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
from metaclass_base import PropertyMeta
import types
class ObservablePropertyMeta (PropertyMeta):
"""Classes instantiated by this meta-class must provide a method named
notify_property_change(self, prop_name, old, new)"""
def __init__(cls, name, bases, dict):
PropertyMeta.__init__(cls, name, bases, dict)
return
def get_setter_source(cls, setter_name, prop_name):
return """def %(setter)s(self, val):
old = self._prop_%(prop)s
new = type(self).create_value('%(prop)s', val, self)
self._prop_%(prop)s = new
if type(self).check_value_change(old, new): self._reset_property_notification('%(prop)s')
self.notify_property_value_change('%(prop)s', old, val)
return
""" % {'setter':setter_name, 'prop':prop_name}
pass #end of class
class ObservablePropertyMetaMT (ObservablePropertyMeta):
"""This class provides multithreading support for accesing
properties, through a locking mechanism. It is assumed a lock is
owned by the class that uses it. A Lock object called _prop_lock
is assumed to be a member of the using class. see for example class
ModelMT"""
def __init__(cls, name, bases, dict):
ObservablePropertyMeta.__init__(cls, name, bases, dict)
return
def get_setter_source(cls, setter_name, prop_name):
return """def %(setter)s(self, val):
old = self._prop_%(prop)s
new = type(self).create_value('%(prop)s', val, self)
self._prop_lock.acquire()
self._prop_%(prop)s = new
self._prop_lock.release()
if type(self).check_value_change(old, new): self._reset_property_notification('%(prop)s')
self.notify_property_value_change('%(prop)s', old, val)
return
""" % {'setter':setter_name, 'prop':prop_name}
pass #end of class
try:
from gobject import GObjectMeta
class ObservablePropertyGObjectMeta (ObservablePropertyMeta, GObjectMeta): pass
class ObservablePropertyGObjectMetaMT (ObservablePropertyMetaMT, GObjectMeta): pass
except:
class ObservablePropertyGObjectMeta (ObservablePropertyMeta): pass
class ObservablePropertyGObjectMetaMT (ObservablePropertyMetaMT): pass
pass

View File

@@ -1,65 +0,0 @@
# Author: Michele Simionato <michelesimionato@libero.it>
# Copyright (C) 2004 by Michele Simionato
# License: Python License (version not specified)
# Last Updated: 2nd of March 2007, 10:23 GMT
#
# Any serious user of metaclasses has been bitten at least once by
# the infamous metaclass/metatype conflict. This script contains a
# general recipe to solve the problem, as well as some theory and
# some examples.
import inspect, types, __builtin__
############## preliminary: two utility functions #####################
def skip_redundant(iterable, skipset=None):
"Redundant items are repeated items or items in the original skipset."
if skipset is None: skipset = set()
for item in iterable:
if item not in skipset:
skipset.add(item)
yield item
def remove_redundant(metaclasses):
skipset = set([types.ClassType])
for meta in metaclasses: # determines the metaclasses to be skipped
skipset.update(inspect.getmro(meta)[1:])
return tuple(skip_redundant(metaclasses, skipset))
##################################################################
## now the core of the module: two mutually recursive functions ##
##################################################################
memoized_metaclasses_map = {}
def get_noconflict_metaclass(bases, left_metas, right_metas):
"""Not intended to be used outside of this module, unless you know
what you are doing."""
# make tuple of needed metaclasses in specified priority order
metas = left_metas + tuple(map(type, bases)) + right_metas
needed_metas = remove_redundant(metas)
# return existing confict-solving meta, if any
if needed_metas in memoized_metaclasses_map:
return memoized_metaclasses_map[needed_metas]
# nope: compute, memoize and return needed conflict-solving meta
elif not needed_metas: # wee, a trivial case, happy us
meta = type
elif len(needed_metas) == 1: # another trivial case
meta = needed_metas[0]
# check for recursion, can happen i.e. for Zope ExtensionClasses
elif needed_metas == bases:
raise TypeError("Incompatible root metatypes", needed_metas)
else: # gotta work ...
metaname = '_' + ''.join([m.__name__ for m in needed_metas])
meta = classmaker()(metaname, needed_metas, {})
memoized_metaclasses_map[needed_metas] = meta
return meta
def classmaker(left_metas=(), right_metas=()):
def make_class(name, bases, adict):
metaclass = get_noconflict_metaclass(bases, left_metas, right_metas)
return metaclass(name, bases, adict)
return make_class

View File

@@ -1,4 +0,0 @@
class A (object):
a = 10
pass

View File

@@ -1,39 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (c) 2007 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
def get_function_from_source(source):
"""Given source code of a function, a function object is
returned"""
import re
m = re.compile("def\s+(\w+)\s*\(.*\):").match(source)
if m is None: raise ValueError("Given source is not a valid function:\n"+
source)
name = m.group(1)
exec source
code = eval("%s.func_code" % name)
import new
return new.function(code, globals(), name)

View File

@@ -1,162 +0,0 @@
# -------------------------------------------------------------------------
# Author: Roberto Cavada <cavada@fbk.eu>
#
# Copyright (C) 2006 by Roberto Cavada
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
# -------------------------------------------------------------------------
import new
# ----------------------------------------------------------------------
class ObsWrapperBase (object):
"""
This class is a base class wrapper for user-defined classes and
containers like lists and maps.
"""
def __init__(self):
self.__prop_name = None
self.__gtkmvc_model = None
return
def __set_model__(self, model, prop_name):
self.__prop_name = prop_name
self.__gtkmvc_model = model
return
def __get_prop_name__(self): return self.__prop_name
def __get_model__(self): return self.__gtkmvc_model
def _notify_method_before(self, instance, name, args, kwargs):
self.__get_model__().notify_method_before_change(self.__prop_name,
instance,
name, args, kwargs)
return
def _notify_method_after(self, instance, name, res_val, args, kwargs):
self.__get_model__().notify_method_after_change(self.__prop_name,
instance,
name, res_val, args, kwargs)
return
pass
# ----------------------------------------------------------------------
class ObsWrapper (ObsWrapperBase):
"""
Base class for wrappers, like user-classes and sequences.
"""
def __init__(self, obj, method_names):
ObsWrapperBase.__init__(self)
self._obj = obj
self.__doc__ = obj.__doc__
for name in method_names:
if hasattr(self._obj, name):
src = self.__get_wrapper_code(name)
exec src
code = eval("%s.func_code" % name)
func = new.function(code, globals())
meth = new.instancemethod(func, self, type(self).__name__)
setattr(self, name, meth)
pass
pass
return
def __get_wrapper_code(self, name):
return """def %(name)s(self, *args, **kwargs):
self._notify_method_before(self._obj, "%(name)s", args, kwargs)
res = self._obj.%(name)s(*args, **kwargs)
self._notify_method_after(self._obj, "%(name)s", res, args, kwargs)
return res""" % {'name' : name}
# For all fall backs
def __getattr__(self, name): return getattr(self._obj, name)
def __repr__(self): return self._obj.__repr__()
def __str__(self): return self._obj.__str__()
pass #end of class
# ----------------------------------------------------------------------
class ObsSeqWrapper (ObsWrapper):
def __init__(self, obj, method_names):
ObsWrapper.__init__(self, obj, method_names)
return
def __setitem__(self, key, val):
self._notify_method_before(self._obj, "__setitem__", (key,val), {})
res = self._obj.__setitem__(key, val)
self._notify_method_after(self._obj, "__setitem__", res, (key,val), {})
return res
def __delitem__(self, key):
self._notify_method_before(self._obj, "__delitem__", (key,), {})
res = self._obj.__delitem__(key)
self._notify_method_after(self._obj, "__delitem__", res, (key,), {})
return res
def __getitem__(self, key):
return self._obj.__getitem__(key)
pass #end of class
# ----------------------------------------------------------------------
class ObsMapWrapper (ObsSeqWrapper):
def __init__(self, m):
methods = ("clear", "pop", "popitem", "update",
"setdefault")
ObsSeqWrapper.__init__(self, m, methods)
return
pass #end of class
# ----------------------------------------------------------------------
class ObsListWrapper (ObsSeqWrapper):
def __init__(self, l):
methods = ("append", "extend", "insert",
"pop", "remove", "reverse", "sort")
ObsSeqWrapper.__init__(self, l, methods)
return
pass #end of class
# ----------------------------------------------------------------------
class ObsUserClassWrapper (ObsWrapper):
def __init__(self, user_class_instance, obs_method_names):
ObsWrapper.__init__(self, user_class_instance, obs_method_names)
return
pass #end of class

View File

@@ -1,215 +0,0 @@
# Author: Roberto Cavada <cavada@fbk.eu>
# Modified by: Guillaume Libersat <glibersat AT linux62.org>
#
# Copyright (c) 2005 by Roberto Cavada
# Copyright (c) 2007 by Guillaume Libersat
#
# pygtkmvc is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# pygtkmvc 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110, USA.
#
# For more information on pygtkmvc see <http://pygtkmvc.sourceforge.net>
# or email to the author Roberto Cavada <cavada@fbk.eu>.
# Please report bugs to <cavada@fbk.eu>.
import gtk.glade
from controller import Controller
import types
class View (object):
def __init__(self, controller, glade_filename=None,
glade_top_widget_name=None, parent_view=None, register=True):
"""If register is False you *must* call 'controller.register_view(self)'
from the derived class constructor (i.e. registration is delayed)
If filename is not given (or None) all following parameters must be
not given (or None). In that case widgets must be connected manually.
glade_top_widget_name can be either a string name or list of names."""
self.manualWidgets = {}
self.autoWidgets = None
self.xmlWidgets = []
# Sets a callback for custom widgets
gtk.glade.set_custom_handler(self._custom_widget_create)
if (( type(glade_top_widget_name) == types.StringType)
or (glade_top_widget_name is None) ):
wids = (glade_top_widget_name,)
else: wids = glade_top_widget_name # Already a list or tuple
# retrieves XML objects from glade
if (glade_filename is not None):
for i in range(0,len(wids)):
self.xmlWidgets.append(gtk.glade.XML(glade_filename, wids[i]))
pass
pass
# top widget list or singleton:
if (glade_top_widget_name is not None):
if len(wids) > 1:
self.m_topWidget = []
for i in range(0, len(wids)):
self.m_topWidget.append(self[wids[i]])
pass
else: self.m_topWidget = self[wids[0]]
else: self.m_topWidget = None
if (glade_filename is not None): self.__autoconnect_signals(controller)
if (register): controller.register_view(self)
if (not parent_view is None): self.set_parent_view(parent_view)
return
# Gives us the ability to do: view['widget_name'].action()
# Returns None if no widget name has been found.
def __getitem__(self, key):
wid = None
if self.autoWidgets:
if self.autoWidgets.has_key(key): wid = self.autoWidgets[key]
pass
else:
for xml in self.xmlWidgets:
wid = xml.get_widget(key)
if wid is not None: break
pass
pass
if wid is None:
# try with manually-added widgets:
if self.manualWidgets.has_key(key):
wid = self.manualWidgets[key]
pass
pass
return wid
# You can also add a single widget:
def __setitem__(self, key, wid):
self.manualWidgets[key] = wid
if (self.m_topWidget is None): self.m_topWidget = wid
return
def show(self):
ret = True
top = self.get_top_widget()
if type(top) in (types.ListType, types.TupleType):
for t in top:
if t is not None: ret = ret and t.show()
pass
elif (top is not None): ret = top.show_all()
else: ret = False
return ret
def hide(self):
top = self.get_top_widget()
if type(top) in (types.ListType, types.TupleType):
for t in top:
if t is not None: t.hide_all()
pass
elif top is not None: top.hide_all()
return
# Returns the top-level widget, or a list of top widgets
def get_top_widget(self):
return self.m_topWidget
# Set parent view:
def set_parent_view(self, parent_view):
top = self.get_top_widget()
if type(top) in (types.ListType, types.TupleType):
for t in top:
if t is not None:
t.set_transient_for(parent_view.get_top_widget())
pass
pass
elif (top is not None):
top.set_transient_for(parent_view.get_top_widget())
pass
return
# Set the transient for the view:
def set_transient(self, transient_view):
top = self.get_top_widget()
if type(top) in (types.ListType, types.TupleType):
for t in top:
if t is not None:
transient_view.get_top_widget().set_transient_for(t)
pass
pass
elif (top is not None):
transient_view.get_top_widget().set_transient_for(top)
pass
return
# Finds the right callback for custom widget creation and calls it
# Returns None if an undefined or invalid handler is found
def _custom_widget_create(self, glade, function_name, widget_name,
str1, str2, int1, int2):
# This code was kindly provided by Allan Douglas <zalguod at
# users.sourceforge.net>
if function_name is not None:
handler = getattr(self, function_name, None)
if handler is not None: return handler(str1, str2, int1, int2)
pass
return None
# implements the iteration protocol
def __iter__(self):
# pre-calculates the auto widgets if needed:
if self.autoWidgets is None:
self.autoWidgets = {}
for xml in self.xmlWidgets:
for wid in xml.get_widget_prefix(""):
wname = gtk.glade.get_widget_name(wid)
assert not self.autoWidgets.has_key(wname)
self.autoWidgets[wname] = wid
pass
pass
pass
self.__idx = 0
self.__max1 = len(self.autoWidgets)
self.__max2 = self.__max1 + len(self.manualWidgets)
return self
# implements the iteration protocol
def next(self):
if self.__idx >= self.__max2: raise StopIteration()
if self.__idx >= self.__max1: m = self.manualWidgets
else: m = self.autoWidgets
self.__idx += 1
return m.keys()[self.__idx-1]
# performs Controller's signals auto-connection:
def __autoconnect_signals(self, controller):
dic = {}
for name in dir(controller):
method = getattr(controller, name)
if (not callable(method)): continue
assert(not dic.has_key(name)) # not already connected!
dic[name] = method
pass
for xml in self.xmlWidgets: xml.signal_autoconnect(dic)
return
pass # end of class View

View File

@@ -1,25 +0,0 @@
# 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
# -------------------------------------------------------------------------

View File

@@ -1,86 +0,0 @@
"""
Project: pyGTKtalog
Description: Simple functions for device management.
Type: lib
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2008-12-15
"""
import os
import locale
import gettext
from src.lib.globs import APPL_SHORT_NAME
locale.setlocale(locale.LC_ALL, '')
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
def volname(mntp):
"""read volume name from cd/dvd"""
dev = mountpoint_to_dev(mntp)
label = None
if dev != None:
try:
disk = open(dev, "rb")
disk.seek(32808)
label = disk.read(32).strip()
disk.close()
except IOError:
return None
return label
def volmount(mntp):
"""
Mount device.
@param mountpoint
@returns tuple with bool status of mount, and string with error message
"""
_in, _out, _err = os.popen3("mount %s" % mntp)
inf = _err.readlines()
if len(inf) > 0:
return False, inf[0].strip()
else:
return True, ''
def volumount(mntp):
"""mount device, return 'ok' or error message"""
_in, _out, _err = os.popen3("umount %s" % mntp)
inf = _err.readlines()
if len(inf) > 0:
return inf[0].strip()
return 'ok'
def check_mount(dev):
"""Refresh the entries from fstab or mount."""
mounts = os.popen('mount')
for line in mounts.readlines():
parts = line.split()
device = parts
if device[0] == dev:
return True
return False
def mountpoint_to_dev(mntp):
"""guess device name by mountpoint from fstab"""
fstab = open("/etc/fstab")
device = None
for line in fstab.readlines():
output = line.split()
# lengtht of single valid fstab line is at least 5
if len(output) > 5 and output[1] == mntp and output[0][0] != '#':
device = output[0]
fstab.close()
return device
def eject_cd(eject_app, cdrom):
"""mount device, return 'ok' or error message"""
if len(eject_app) > 0:
_in, _out, _err = os.popen3("%s %s" % (eject_app, cdrom))
inf = _err.readlines()
if len(inf) > 0 and inf[0].strip() != '':
return inf[0].strip()
return 'ok'
return _("Eject program not specified")

View File

@@ -1,41 +0,0 @@
# 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
import sys
if sys.argv[0]:
top_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
else:
top_dir = "."
# ----------------------------------------------------------------------
TOPDIR = top_dir
RESOURCES_DIR = os.path.join(TOPDIR, "resources")
GLADE_DIR = os.path.join(RESOURCES_DIR, "glade")
STYLES_DIR = os.path.join(RESOURCES_DIR, "styles")
APPL_SHORT_NAME = "pygtktalog"
APPL_VERSION = (1, 0, 2)
# ----------------------------------------------------------------------

View File

@@ -1,82 +0,0 @@
# 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 xml.dom import minidom
import gzip
import os
from datetime import date
class GthumbCommentParser(object):
"""Read and return comments created eith gThumb program"""
def __init__(self, image_path, image_filename):
self.path = image_path
self.filename = image_filename
def parse(self):
"""Return dictionary with apropriate fields, or None if no comment
available"""
try:
gzf = gzip.open(os.path.join(self.path, '.comments',
self.filename + '.xml'))
except:
return None
try:
xml = gzf.read()
gzf.close()
except:
gzf.close()
return None
if not xml:
return None
retval = {}
doc = minidom.parseString(xml)
try:
retval['note'] = doc.getElementsByTagName('Note').item(0)
retval['note'] = retval['note'].childNodes.item(0).data
except: retval['note'] = None
try:
retval['place'] = doc.getElementsByTagName('Place').item(0)
retval['place'] = retval['place'].childNodes.item(0).data
except: retval['place'] = None
try:
d = doc.getElementsByTagName('Time').item(0).childNodes
d = d.item(0).data
if int(d) > 0: retval['date'] = date.fromtimestamp(int(d))
else: retval['date'] = None
except: retval['date'] = None
try:
retval['keywords'] = doc.getElementsByTagName('Keywords').item(0)
retval['keywords'] = retval['keywords'].childNodes.item(0)
retval['keywords'] = retval['keywords'].data.split(',')
except: pass
if len(retval) > 0: return retval
else: return None

View File

@@ -1,78 +0,0 @@
# 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 shutil import copy
from os import path
from hashlib import sha512
from PIL import Image
class Img(object):
def __init__(self, filename, base=''):
self.root = 'images'
self.x = 160
self.y = 160
self.filename = filename
self.base = base
f = open(filename, "r")
self.sha512 = sha512(f.read()).hexdigest()
f.close()
def save(self):
"""Save image and asociated thumbnail into specific directory structure
returns filename for image"""
image_filename = path.join(self.base, self.sha512)
thumbnail = path.join(self.base, self.sha512 + "_t")
# check wheter image already exists
if path.exists(image_filename) and path.exists(thumbnail):
if __debug__:
print "image", self.filename, "with hash",
print self.sha512, "already exist"
return self.sha512
if not path.exists(thumbnail):
im = self.__scale_image()
im.save(thumbnail, "JPEG")
# copy image
if not path.exists(image_filename):
copy(self.filename, image_filename)
return self.sha512
# private class functions
def __scale_image(self):
"""create thumbnail. returns image object or None"""
try:
im = Image.open(self.filename).convert('RGB')
except:
return None
x, y = im.size
if x > self.x or y > self.y:
im.thumbnail((self.x, self.y), Image.ANTIALIAS)
return im

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +0,0 @@
import re
import EXIF
class ParseExif(object):
def __init__(self, exif_dict=None, exif_file=None):
self.camera = None
self.date = None
self.aperture = None
self.exposure_program = None
self.exposure_bias = None
self.iso = None
self.focal_length = None
self.subject_distance = None
self.metering_mode = None
self.flash = None
self.light_source = None
self.resolution = None
self.orientation = None
self.exif_dict = exif_dict
if not self.exif_dict:
try:
f = open(exif_file, 'rb')
e = EXIF.process_file(f)
if len(e.keys()) >0:
self.exif_dict = e
f.close()
except:
pass
def parse(self):
try:
self.camera = "%s" % self.exif_dict['Image Make']
self.camera = self.camera.strip()
except: pass
try:
model = "%s" % self.exif_dict['Image Model']
self.camera += ", " + model.strip()
except: pass
try:
self.date = "%s" % self.exif_dict['EXIF DateTimeOriginal']
p = re.compile('[\d,:]+')
if not p.match(self.date):
self.date = None
except: pass
try:
self.aperture = "%s" % self.exif_dict['EXIF FNumber']
if len(self.aperture.split("/")) == 2:
self.aperture += '.'
self.aperture = "%.1f" % eval(self.aperture)
self.aperture = "f/%.1f" % float(self.aperture)
self.aperture = self.aperture.replace('.',',')
except: pass
try: self.exposure_program = "%s" % \
self.exif_dict['EXIF ExposureProgram']
except: pass
try:
self.exposure_bias = "%s" % \
self.exif_dict['EXIF ExposureBiasValue']
if len(self.exposure_bias.split("/")) == 2:
self.exposure_bias += '.'
self.exposure_bias = "%.1f" % eval(self.exposure_bias)
self.exposure_bias = "%.1f" % float(self.exposure_bias)
self.exposure_bias = self.exposure_bias.replace('.',',')
except: pass
try: self.iso = "%s" % self.exif_dict['EXIF ISOSpeedRatings']
except: pass
try:
self.focal_length = "%s" % self.exif_dict['EXIF FocalLength']
if len(self.focal_length.split("/")) == 2:
self.focal_length += '.'
self.focal_length = "%.2f" % eval(self.focal_length)
self.focal_length = "%.2f mm" % float(self.focal_length)
self.focal_length = self.focal_length.replace('.',',')
except: pass
try:
self.subject_distance = "%s" % \
self.exif_dict['EXIF SubjectDistance']
if len(self.subject_distance.split("/")) == 2:
self.subject_distance += '.'
self.subject_distance = "%.3f" % eval(self.subject_distance)
self.subject_distance = "%.3f m" % float(self.subject_distance)
self.subject_distance = self.subject_distance.replace('.',',')
except: pass
try: self.metering_mode = "%s" % self.exif_dict['EXIF MeteringMode']
except: pass
try: self.flash = "%s" % self.exif_dict['EXIF Flash']
except: pass
try: self.light_source = "%s" % self.exif_dict['EXIF LightSource']
except: pass
try: self.resolution = "%s" % self.exif_dict['Image XResolution']
except: pass
try: self.resolution = self.resolution + " x %s" % \
self.exif_dict['Image YResolution']
except: pass
try: self.resolution = self.resolution + " (%s)" % \
self.exif_dict['Image ResolutionUnit']
except: pass
try: self.orientation = "%s" % self.exif_dict['Image Orientation']
except: pass
return (self.camera, self.date, self.aperture, self.exposure_program,
self.exposure_bias, self.iso, self.focal_length,
self.subject_distance, self.metering_mode, self.flash,
self.light_source, self.resolution, self.orientation)

View File

@@ -1,119 +0,0 @@
# 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 tempfile import mkstemp
from hashlib import sha512
from shutil import move
from os import path
import sys
from PIL import Image
from lib import EXIF
class Thumbnail(object):
"""Class for generate/extract thumbnail from image file"""
def __init__(self, filename=None, base=''):
self.thumb_x = 160
self.thumb_y = 160
self.filename = filename
self.base = base
self.sha512 = sha512(open(filename).read()).hexdigest()
self.thumbnail_path = path.join(self.base, self.sha512 + "_t")
def save(self):
"""Save thumbnail into specific directory structure
return filename base and exif object or None"""
exif = {}
orientations = {2: Image.FLIP_LEFT_RIGHT, # Mirrored horizontal
3: Image.ROTATE_180, # Rotated 180
4: Image.FLIP_TOP_BOTTOM, # Mirrored vertical
5: Image.ROTATE_90, # Mirrored horizontal then
# rotated 90 CCW
6: Image.ROTATE_270, # Rotated 90 CW
7: Image.ROTATE_270, # Mirrored horizontal then
# rotated 90 CW
8: Image.ROTATE_90} # Rotated 90 CCW
flips = {7: Image.FLIP_LEFT_RIGHT, 5: Image.FLIP_LEFT_RIGHT}
image_file = open(self.filename, 'rb')
try:
exif = EXIF.process_file(image_file)
except:
if __debug__:
print "exception", sys.exc_info()[0], "raised with file:"
print self.filename
finally:
image_file.close()
if path.exists(self.thumbnail_path):
if __debug__:
print "file", self.filename, "with hash", self.sha512, "exists"
return self.sha512, exif
if 'JPEGThumbnail' in exif:
if __debug__:
print self.filename, "exif thumb"
exif_thumbnail = exif['JPEGThumbnail']
thumb_file = open(self.thumbnail_path, 'wb')
thumb_file.write(exif_thumbnail)
thumb_file.close()
if 'Image Orientation' in exif:
orient = exif['Image Orientation'].values[0]
if orient > 1 and orient in orientations:
fd, temp_image_path = mkstemp()
os.close(fd)
thumb_image = Image.open(self.thumbnail_path)
tmp_thumb_img = thumb_image.transpose(orientations[orient])
if orient in flips:
tmp_thumb_img = tmp_thumb_img.transpose(flips[orient])
if tmp_thumb_img:
tmp_thumb_img.save(temp_image_path, 'JPEG')
move(temp_image_path, self.thumbnail_path)
return self.sha512, exif
else:
if __debug__:
print self.filename, "no exif thumb"
thumb = self.__scale_image()
if thumb:
thumb.save(self.thumbnail_path, "JPEG")
return self.sha512, exif
return None, exif
def __scale_image(self):
"""create thumbnail. returns image object or None"""
try:
image_thumb = Image.open(self.filename).convert('RGB')
except:
return None
it_x, it_y = image_thumb.size
if it_x > self.thumb_x or it_y > self.thumb_y:
image_thumb.thumbnail((self.thumb_x, self.thumb_y), Image.ANTIALIAS)
return image_thumb

View File

@@ -1,25 +0,0 @@
# 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
# -------------------------------------------------------------------------

View File

@@ -1,340 +0,0 @@
# 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
import os
import gtk
import gobject
from ConfigParser import ConfigParser
class Ini(object):
def __init__(self):
self.ini = []
def add_section(self, section):
self.ini.append("[%s]" % section)
def add_key(self, key, value):
self.ini.append("%s=%s" % (key, value))
def add_comment(self, comment):
self.ini.append(";%s" % comment)
def add_verb(self, verb):
self.ini.append(verb)
def show(self):
return "\n".join(self.ini)
class ConfigModel(Model):
ini = Ini()
__properties__ = {}
filetype_list = ['Images', 'Movies']
confd = {
'savewin': True,
'savepan': True,
'wx': 800,
'wy': 600,
'h': 200,
'v': 300,
'eject': True,
'compress': True,
'exportxls': False,
'confirmquit': True,
'confirmabandon': True,
'mntwarn': True,
'delwarn': True,
'cd': '/mnt/cdrom',
'ejectapp': 'eject -r',
'imgview': False,
'imgprog': 'gqview',
'retrive': False,
'thumbs': True,
'exif': True,
'gthumb': False,
'extensions': {'bmp':'identify %s',
'gif':'identify "%s"',
'jpg':'identify "%s"',
'jpeg':'identify "%s"',
'png':'identify "%s"',
'avi':'midentify "%s"',
'mkv':'midentify "%s"',
'mpg':'midentify "%s"',
'mpeg':'midentify "%s"',
'wmv':'midentify "%s"',
},
'showtoolbar':True,
'showstatusbar':True,
}
dictconf = {
"save main window size": "savewin",
"save panes size": "savepan",
"main window width": "wx",
"main window height": "wy",
"horizontal panes": "h",
"vertical panes":"v",
"export xls":"exportxls",
"cd drive":"cd",
"eject command":"ejectapp",
"eject":"eject",
"image support":"thumbs",
'confirm quit':'confirmquit',
'warn mount/umount errors':'mntwarn',
'warn on delete':'delwarn',
'confirm abandon current catalog':'confirmabandon',
'show toolbar':'showtoolbar',
'show statusbar and progress bar':'showstatusbar',
'compress catalog':'compress',
'retrive extra informatin':'retrive',
'scan exif data':'exif',
'include gthumb image description':'gthumb',
'use external image viewer':'imgview',
'external image viewer program':'imgprog',
}
dbool = (
'exportxls',
'thumbs',
'savewin',
'savepan',
'eject',
'gthumb',
'exif',
'confirmquit',
'mntwarn',
'delwarn',
'confirmabandon',
'showtoolbar',
'showstatusbar',
'delwarn',
'compress',
'retrive',
'imgview',
)
recent = []
search_history = []
RECENT_MAX = 10
HISTORY_MAX = 20
dstring = ('cd','ejectapp','imgprog')
try:
path = os.path.join(os.environ['HOME'], ".pygtktalog")
except KeyError:
raise KeyError, "Cannot stat path for current user home!"
path = os.path.join(path, "config.ini")
def __init__(self):
Model.__init__(self)
self.category_tree = gtk.ListStore(gobject.TYPE_STRING)
self.refresh_ext()
return
def refresh_ext(self):
self.ext_tree = gtk.ListStore(gobject.TYPE_STRING,
gobject.TYPE_STRING)
keys = sorted(self.confd['extensions'].keys())
for i in keys:
myiter = self.ext_tree.insert_before(None,None)
self.ext_tree.set_value(myiter, 0, i)
self.ext_tree.set_value(myiter, 1, self.confd['extensions'][i])
def save(self):
try:
os.lstat(self.path)
except:
print "Saving preferences to %s." % self.path
if __debug__:
print "m_config.py: save() Saving preferences to",
print "%s" % self.path
newIni = Ini()
# main section
newIni.add_section("pyGTKtalog conf")
for opt in self.dictconf:
newIni.add_key(opt,self.confd[self.dictconf[opt]])
# recent section
newIni.add_section("pyGTKtalog recent")
count = 1
max_count = self.RECENT_MAX + 1
for opt in self.recent:
if count < max_count:
newIni.add_key(count, opt)
else:
break
count+=1
# search history section
newIni.add_section("search history")
count = 1
max_count = self.HISTORY_MAX + 1
for opt in self.search_history:
if count < max_count:
newIni.add_key(count, opt)
else:
break
count+=1
# extensions sections
newIni.add_section("extensions")
count = 1
for i in self.confd['extensions']:
newIni.add_key(i, self.confd['extensions'][i])
count+=1
# write config
try:
f = open(self.path, "w")
success = True
except:
if __debug__:
print "m_config.py: save() Cannot open config file",
print "%s for writing." % self.path
success = False
f.write(newIni.show())
f.close()
return success
def load(self):
try:
# try to read config file
parser = ConfigParser()
parser.read(self.path)
r = {}
h = {}
self.recent = []
self.search_history = []
for sec in parser.sections():
if sec == 'pyGTKtalog conf':
for opt in parser.options(sec):
i = self.dictconf[opt]
try:
if self.dictconf[opt] in self.dbool:
self.confd[i] = parser.getboolean(sec, opt)
elif self.dictconf[opt] in self.dstring:
self.confd[i] = parser.get(sec, opt)
else:
self.confd[i] = parser.getint(sec, opt)
except:
if __debug__:
print "m_config.py: load() failed to parse",
print "option:", opt
pass
elif sec == 'pyGTKtalog recent':
for opt in parser.options(sec):
try:
r[int(opt)] = parser.get(sec, opt)
except:
if __debug__:
print "m_config.py: load() failed to parse",
print "option:", opt
pass
elif sec == 'search history':
for opt in parser.options(sec):
try:
h[int(opt)] = parser.get(sec, opt)
except:
if __debug__:
print "m_config.py: load() failed to parse",
print "option:", opt
pass
elif sec == 'extensions':
self.confd['extensions'] = {}
for opt in parser.options(sec):
try:
self.confd['extensions'][opt] = parser.get(sec,
opt)
except:
if __debug__:
print "m_config.py: load() failed to parse",
print "option:", opt
pass
for i in range(1, self.RECENT_MAX + 1):
if i in r:
self.recent.append(r[i])
for i in range(1, self.HISTORY_MAX + 1):
if i in h:
self.search_history.append(h[i])
except:
if __debug__:
print "m_config.py: load() load config file failed"
pass
def add_recent(self, path):
if not path:
return
if path in self.recent:
self.recent.remove(path)
self.recent.insert(0,path)
return
self.recent.insert(0,path)
if len(self.recent) > self.RECENT_MAX:
self.recent = self.recent[:self.RECENT_MAX]
return
def add_search_history(self, text):
if not text:
return
if text in self.search_history:
self.search_history.remove(text)
self.search_history.insert(0, text)
return
self.search_history.insert(0, text)
if len(self.search_history) > self.HISTORY_MAX:
self.search_history = self.search_history[:self.HISTORY_MAX]
return
def __str__(self):
"""show prefs in string way"""
string = "[varname]\tvalue\n"
for i in self.confd:
string+="%s\t%s\n" % (i,self.confd[i])
return string

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +0,0 @@
# 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
# -------------------------------------------------------------------------

View File

@@ -1,36 +0,0 @@
# 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 lib.globs
class ConfigView(View):
"""Preferences window from glade file """
GLADE = os.path.join(lib.globs.GLADE_DIR, "config.glade")
def __init__(self, ctrl):
View.__init__(self, ctrl, self.GLADE)
return
pass # end of class

View File

@@ -1,653 +0,0 @@
# 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 gtk
import gobject
import os
import lib.globs
class Qst(object):
"""Show simple dialog for questions
if "OK" button pressed, return "True"
"Cancel" button return "False"
"""
def __init__(self, title="", message="", secondarymsg=""):
self.dialog = gtk.MessageDialog(
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_QUESTION,
buttons = gtk.BUTTONS_OK_CANCEL,
message_format = message,
)
self.dialog.set_title(title)
self.dialog.format_secondary_text(secondarymsg)
def run(self):
retval = self.dialog.run()
self.dialog.destroy()
if retval == gtk.RESPONSE_OK:
return True
return False
class Inf(object):
"""Show simple dialog for notices"""
def __init__(self, title="", message="", secondarymsg=""):
self.dialog = gtk.MessageDialog(
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_INFO,
buttons = gtk.BUTTONS_OK,
message_format = message,
)
self.dialog.set_title(title)
self.dialog.format_secondary_text(secondarymsg)
self.dialog.connect('response',
lambda dialog, response: self.ret(response))
self.dialog.show()
def ret(self,result):
self.dialog.destroy()
return True
class Wrn(object):
"""Show simple dialog for warnings"""
def __init__(self, title="", message="", secondarymsg=""):
self.dialog = gtk.MessageDialog(
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_WARNING,
buttons = gtk.BUTTONS_CLOSE,
message_format = message,
)
self.dialog.set_title(title)
self.dialog.format_secondary_text(secondarymsg)
self.dialog.connect('response',
lambda dialog, response: self.ret(response))
self.dialog.show()
def ret(self,result):
self.dialog.destroy()
return True
class Err(object):
"""Show simple dialog for errors"""
def __init__(self, title="", message="", secondarymsg=""):
self.dialog = gtk.MessageDialog(
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_ERROR,
buttons = gtk.BUTTONS_CLOSE,
message_format = message)
self.dialog.set_title(title)
self.dialog.format_secondary_text(secondarymsg)
self.dialog.connect('response',
lambda dialog, response: self.ret(response))
self.dialog.run()
def ret(self,result):
self.dialog.destroy()
return True
class Abt(object):
"""Show simple about dialog"""
def __init__(self, name=None, ver="", title="", authors=[],licence=""):
self.dialog = gtk.AboutDialog()
self.dialog.set_title(title)
self.dialog.set_version(ver)
self.dialog.set_license(licence)
self.dialog.set_name(name)
self.dialog.set_authors(authors)
self.dialog.connect('response',
lambda dialog, response: self.dialog.destroy())
self.dialog.show()
class InputDiskLabel(object):
"""Sepcific dialog for quering user for a disc label"""
def __init__(self, label=""):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.label = ""
if label!= None:
self.label = label
def run(self):
gladexml = gtk.glade.XML(self.gladefile, "inputDialog")
dialog = gladexml.get_widget("inputDialog")
entry = gladexml.get_widget("volname")
entry.set_text(self.label)
result = dialog.run()
dialog.destroy()
if result == gtk.RESPONSE_OK:
return entry.get_text()
return None
class InputNewName(object):
"""Sepcific dialog for quering user for a disc label"""
def __init__(self, name=""):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.label = ""
self.name = name
def run(self):
gladexml = gtk.glade.XML(self.gladefile, "renameDialog")
dialog = gladexml.get_widget("renameDialog")
entry = gladexml.get_widget("name")
entry.set_text(self.name)
result = dialog.run()
dialog.destroy()
if result == gtk.RESPONSE_OK:
return entry.get_text()
return None
class PointDirectoryToAdd(object):
"""Sepcific dialog for quering user for selecting directory to add"""
URI="file://"+os.path.abspath(os.path.curdir)
def __init__(self,volname='',dirname=''):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.gladexml = gtk.glade.XML(self.gladefile, "addDirDialog")
self.volname = self.gladexml.get_widget("dirvolname")
self.volname.set_text(volname)
self.directory = self.gladexml.get_widget("directory")
self.directory.set_text(dirname)
sigs = {"on_browse_activate":self.show_dirchooser,
"on_browse_clicked":self.show_dirchooser}
self.gladexml.signal_autoconnect(sigs)
def show_dirchooser(self,widget):
"""dialog for point the mountpoint"""
dialog = gtk.FileChooserDialog(
title="Choose directory to add",
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_default_response(gtk.RESPONSE_OK)
if self.URI:
dialog.set_current_folder_uri(self.URI)
response = dialog.run()
if response == gtk.RESPONSE_OK:
self.directory.set_text(dialog.get_filename())
self.__class__.URI = dialog.get_current_folder_uri()
dialog.destroy()
def run(self):
dialog = self.gladexml.get_widget("addDirDialog")
ch = True
result = dialog.run()
while ch:
if result == gtk.RESPONSE_OK and (self.volname.get_text()=='' or \
self.directory.get_text() == ''):
a = Err("Error - pyGTKtalog",
"There are fields needed to be filled.",
"Cannot add directory without path and disc label.")
ch = True
result = dialog.run()
else:
ch = False
dialog.destroy()
if result == gtk.RESPONSE_OK:
return self.volname.get_text(),self.directory.get_text()
else:
return None,None
class SelectDirectory(object):
"""Sepcific dialog for quering user for selecting directory to add"""
URI="file://"+os.path.abspath(os.path.curdir)
def __init__(self, title=None):
if title:
self.title = title
else:
self.title = "Choose directory"
def run(self):
"""dialog for point the mountpoint"""
dialog = gtk.FileChooserDialog(
title = self.title,
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_default_response(gtk.RESPONSE_OK)
retval = None
if self.URI:
dialog.set_current_folder_uri(self.URI)
response = dialog.run()
if response == gtk.RESPONSE_OK:
retval = dialog.get_filename()
self.__class__.URI = dialog.get_current_folder_uri()
dialog.destroy()
return retval
class ChooseFilename(object):
"""Dialog for quering user for selecting filename"""
URI=None
def __init__(self, path=None, title=''):
self.path = path
self.dialog = gtk.FileChooserDialog(
title="",
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE,
gtk.RESPONSE_OK))
self.dialog.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
self.dialog.set_default_response(gtk.RESPONSE_OK)
self.dialog.set_do_overwrite_confirmation(True)
self.dialog.set_title(title)
f = gtk.FileFilter()
f.set_name("Catalog files")
f.add_pattern("*.sqlite")
f.add_pattern("*.sqlite.bz2")
self.dialog.add_filter(f)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*.*")
self.dialog.add_filter(f)
def run(self):
if self.URI:
self.dialog.set_current_folder_uri(self.URI)
elif self.path and os.path.exists(self.path):
self.path = "file://"+os.path.abspath(self.path)
self.dialog.set_current_folder_uri(self.path)
response = self.dialog.run()
if response == gtk.RESPONSE_OK:
filename = self.dialog.get_filename()
self.__class__.URI = self.dialog.get_current_folder_uri()
self.dialog.destroy()
return filename
else:
self.dialog.destroy()
return None
pass
class ChooseDBFilename(ChooseFilename):
"""Sepcific dialog for quering user for selecting filename for database"""
URI=None
def __init__(self, path=None):
ChooseFilename.__init__(self)
self.dialog.set_title('Save catalog as...')
f = gtk.FileFilter()
f.set_name("Catalog files")
f.add_pattern("*.sqlite")
f.add_pattern("*.sqlite.bz2")
self.dialog.add_filter(f)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*.*")
self.dialog.add_filter(f)
def run(self):
if self.URI:
self.dialog.set_current_folder_uri(self.URI)
elif self.path and os.path.exists(self.path):
self.path = "file://"+os.path.abspath(self.path)
self.dialog.set_current_folder_uri(self.path)
response = self.dialog.run()
if response == gtk.RESPONSE_OK:
filename = self.dialog.get_filename()
self.__class__.URI = self.dialog.get_current_folder_uri()
self.dialog.destroy()
return filename
else:
self.dialog.destroy()
return None
pass
class LoadDBFile(object):
"""Specific class for displaying openFile dialog. It has veryfication
for file existence."""
URI = None
def __init__(self, path=None):
self.path = path
self.dialog = gtk.FileChooserDialog(
title="Open catalog",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN,
gtk.RESPONSE_OK))
self.dialog.set_default_response(gtk.RESPONSE_OK)
f = gtk.FileFilter()
f.set_name("Catalog files")
f.add_pattern("*.sqlite")
f.add_pattern("*.sqlite.bz2")
self.dialog.add_filter(f)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*.*")
self.dialog.add_filter(f)
def show_dialog(self):
response = self.dialog.run()
filename = None
if response == gtk.RESPONSE_OK:
try:
filename = self.dialog.get_filename()
except:
pass
#self.dialog.destroy()
return 'ok',filename
else:
return 'cancel',None
def run(self):
if self.URI:
self.dialog.set_current_folder_uri(self.URI)
elif self.path and os.path.exists(self.path):
self.path = "file://"+os.path.abspath(self.path)
self.dialog.set_current_folder_uri(self.path)
res,filename = self.show_dialog()
ch = True
while ch:
if res == 'cancel':
self.dialog.destroy()
return None
try:
os.stat(filename)
self.__class__.URI = self.dialog.get_current_folder_uri()
self.dialog.destroy()
return filename
except:
a = Err("Error - pyGTKtalog","File doesn't exist.",
"The file that you choose does not exist." + \
" Choose another one, or cancel operation.")
ch = True
res, filename = self.show_dialog()
class LoadImageFile(object):
"""class for displaying openFile dialog. It have possibility of multiple
selection."""
URI="file://"+os.path.abspath(os.path.curdir)
def __init__(self, multiple=False):
self.dialog = gtk.FileChooserDialog(
title="Select image",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN,
gtk.RESPONSE_OK))
self.dialog.set_select_multiple(multiple)
self.dialog.set_default_response(gtk.RESPONSE_OK)
f = gtk.FileFilter()
f.set_name("All Images")
for i in ['*.jpg', '*.jpeg', '*.gif', '*.png', '*.tif', '*.tiff',
'*.tga', '*.pcx', '*.bmp', '*.xbm', '*.xpm', '*.jp2',
'*.jpx', '*.pnm', '*.JPG', '*.JPEG', '*.GIF', '*.PNG',
'*.TIF', '*.TIFF', '*.TGA', '*.PCX', '*.BMP', '*.XBM',
'*.XPM', '*.JP2', '*.JPX', '*.PNM']:
f.add_pattern(i)
self.dialog.add_filter(f)
f = gtk.FileFilter()
f.set_name("All files")
f.add_pattern("*.*")
self.dialog.add_filter(f)
self.preview = gtk.Image()
self.dialog.set_preview_widget(self.preview)
self.dialog.connect("update-preview", self.update_preview_cb)
def run(self):
if self.URI:
self.dialog.set_current_folder_uri(self.URI)
response = self.dialog.run()
filenames = None
only_thumbs = False
if response == gtk.RESPONSE_OK:
try:
if self.dialog.get_select_multiple():
filenames = self.dialog.get_filenames()
else:
filenames = self.dialog.get_filename()
if self.dialog.get_extra_widget().get_active():
only_thumbs = True
except:
pass
self.__class__.URI = self.dialog.get_current_folder_uri()
self.dialog.destroy()
return filenames, only_thumbs
def update_preview_cb(self, widget):
filename = self.dialog.get_preview_filename()
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 128, 128)
self.preview.set_from_pixbuf(pixbuf)
have_preview = True
except:
have_preview = False
self.dialog.set_preview_widget_active(have_preview)
return
class StatsDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self, values={}):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.values = values
def run(self):
gladexml = gtk.glade.XML(self.gladefile, "statDialog")
dialog = gladexml.get_widget("statDialog")
if 'discs' in self.values:
entry = gladexml.get_widget("discs_entry")
entry.set_text(str(self.values['discs']))
else:
label = gladexml.get_widget("discs_label")
entry = gladexml.get_widget("discs_entry")
label.hide()
entry.hide()
if 'dirs' in self.values:
entry = gladexml.get_widget("dirs_entry")
entry.set_text(str(self.values['dirs']))
else:
label = gladexml.get_widget("dirs_label")
entry = gladexml.get_widget("dirs_entry")
label.hide()
entry.hide()
if 'files' in self.values:
entry = gladexml.get_widget("files_entry")
entry.set_text(str(self.values['files']))
if 'size' in self.values:
entry = gladexml.get_widget("size_entry")
entry.set_text(str(self.values['size']))
result = dialog.run()
dialog.destroy()
if result == gtk.RESPONSE_OK:
return entry.get_text()
return None
class TagsDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
def run(self):
gladexml = gtk.glade.XML(self.gladefile, "tagsDialog")
dialog = gladexml.get_widget("tagsDialog")
entry = gladexml.get_widget("tag_entry1")
result = dialog.run()
dialog.destroy()
if result == gtk.RESPONSE_OK:
return entry.get_text()
return None
class TagsRemoveDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self, tag_dict=None):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.tag_dict = tag_dict
def run(self):
if not self.tag_dict:
return None
gladexml = gtk.glade.XML(self.gladefile, "tagRemove")
dialog = gladexml.get_widget("tagRemove")
# declare model
model = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
# sort dict
values = self.tag_dict.values()
values.sort()
keys = []
for val in values:
for d_key, d_value in self.tag_dict.items():
if d_value == val:
keys.append(d_key)
# fill model with dict
for count in range(len(keys)):
myiter = model.insert_before(None, None)
model.set_value(myiter, 0, keys[count])
model.set_value(myiter, 1, values[count])
model.set_value(myiter, 2, None)
def toggle(cell, path, model):
model[path][2] = not model[path][2]
def toggle_all(column, model):
for row in model:
row[2] = not row[2]
treeview = gladexml.get_widget("treeview1")
treeview.set_model(model)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Tag", renderer, text=1)
column.set_property('expand', True)
treeview.append_column(column)
renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True)
renderer.connect('toggled', toggle, model)
column = gtk.TreeViewColumn("Toggle", renderer)
column.add_attribute(renderer, "active", 2)
column.set_property('expand', False)
column.set_property("clickable", True)
column.connect("clicked", toggle_all, model)
treeview.append_column(column)
result = dialog.run()
dialog.destroy()
if result == gtk.RESPONSE_OK:
ids = []
for i in model:
if i[2]:
ids.append(i[0])
return "ok", ids
return None, None
class EditDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self, values={}):
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
self.values = values
def run(self):
gladexml = gtk.glade.XML(self.gladefile, "file_editDialog")
dialog = gladexml.get_widget("file_editDialog")
filename = gladexml.get_widget("filename_entry")
filename.set_text(str(self.values['filename']))
description = gladexml.get_widget("description_text")
note = gladexml.get_widget("note_text")
if 'description' in self.values:
buff = gtk.TextBuffer()
buff.set_text(str(self.values['description']))
description.set_buffer(buff)
if 'note' in self.values:
buff = gtk.TextBuffer()
buff.set_text(str(self.values['note']))
note.set_buffer(buff)
result = dialog.run()
if result == gtk.RESPONSE_OK:
d = description.get_buffer()
n = note.get_buffer()
retval = {'filename': filename.get_text(),
'description': d.get_text(d.get_start_iter(),
d.get_end_iter()),
'note': n.get_text(n.get_start_iter(), n.get_end_iter())}
dialog.destroy()
return retval
dialog.destroy()
return None

View File

@@ -1,68 +0,0 @@
# 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 gtk
class ImageView(object):
"""simple image viewer. no scaling, no zooming, no rotating.
simply show stupid image"""
def __init__(self, image_filename):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
image = gtk.Image()
image.set_from_file(image_filename)
pixbuf = image.get_pixbuf()
pic_width = pixbuf.get_width() + 23
pic_height = pixbuf.get_height() + 23
screen_width = gtk.gdk.screen_width()
screen_height = gtk.gdk.screen_height()
need_vieport = False
if pic_height > (screen_height - 128):
height = screen_height - 128
need_vieport = True
else:
height = screen_height - 128
if pic_width > (screen_width - 128):
width = screen_width - 128
need_vieport = True
else:
width = pic_width
if need_vieport:
window.resize(width, height)
viewport = gtk.ScrolledWindow()
viewport.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
viewport.add_with_viewport(image)
window.add(viewport)
else:
window.add(image)
window.show_all()
return
pass # end of class

View File

@@ -1,44 +0,0 @@
# 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
import lib.globs
from gtkmvc import View
class MainView(View):
"""This handles only the graphical representation of the
application. The widgets set is loaded from glade file"""
GLADE = os.path.join(lib.globs.GLADE_DIR, "main.glade")
def __init__(self, ctrl):
View.__init__(self, ctrl, self.GLADE)
# hide v2.0 features
self['separatormenuitem4'].hide()
self['list1'].hide()
self['thumbnails1'].hide()
return
pass # end of class

View File

@@ -1,38 +0,0 @@
# 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 lib.globs
class SearchView(View):
"""Search window from glade file """
GLADE = os.path.join(lib.globs.GLADE_DIR, "search.glade")
def __init__(self, ctrl):
View.__init__(self, ctrl, self.GLADE)
return
pass # end of class

View File

View File

@@ -1,8 +0,0 @@
"""
Project: pyGTKtalog
Description: Tests for main view class.
Type: test
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2010-03-14
"""

View File

@@ -1,124 +0,0 @@
"""
Project: pyGTKtalog
Description: Tests for scan files.
Type: test
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2011-03-26
"""
import os
import unittest
from pygtktalog import scan
from pygtktalog.dbobjects import File
from pygtktalog.dbcommon import connect, Session
TEST_DIR = "/home/share/_test_/test_dir"
TEST_DIR_PERMS = "/home/share/_test_/test_dir_permissions/"
class TestScan(unittest.TestCase):
"""
Test cases for scan functionality
1. execution scan function:
1.1 simple case - should pass
1.2 non-existent directory passed
1.3 file passed
1.4 directory has permission that forbids file listing
2. rescan directory; looking for changes
2.0 don't touch records for changed files (same directories, same
filename, same type and size)
2.1 search for files of the same type, same size.
2.2 change parent node for moved files (don't insert new)
3. adding new directory tree which contains same files like already stored
in the database
"""
def setUp(self):
connect()
root = File()
root.id = 1
root.filename = 'root'
root.size = 0
root.source = 0
root.type = 0
root.parent_id = 1
sess = Session()
sess.add(root)
sess.commit()
def test_happy_scenario(self):
"""
make scan, count items
"""
scanob = scan.Scan(os.path.abspath(os.path.join(__file__,
"../../../mocks")))
scanob = scan.Scan(TEST_DIR)
result_list = scanob.add_files()
self.assertEqual(len(result_list), 143)
self.assertEqual(len(result_list[0].children), 8)
# check soft links
self.assertEqual(len([x for x in result_list if x.type == 3]), 2)
def test_wrong_and_nonexistent(self):
"""
Check for accessing non existent directory, regular file instead of
the directory, or file.directory with no access to it.
"""
scanobj = scan.Scan('/nonexistent_directory_')
self.assertRaises(OSError, scanobj.add_files)
scanobj.path = '/root'
self.assertRaises(scan.NoAccessError, scanobj.add_files)
scanobj.path = '/bin/sh'
self.assertRaises(scan.NoAccessError, scanobj.add_files)
# dir contains some non accessable items. Should just pass, and on
# logs should be messages about it
scanobj.path = TEST_DIR_PERMS
scanobj.add_files()
def test_abort_functionality(self):
scanobj = scan.Scan(TEST_DIR)
scanobj.abort = True
self.assertEqual(None, scanobj.add_files())
def test_double_scan(self):
"""
Do the scan twice.
"""
ses = Session()
self.assertEqual(len(ses.query(File).all()), 1)
scanob = scan.Scan(TEST_DIR)
scanob.add_files()
# note: we have 144 elements in db, because of root element
self.assertEqual(len(ses.query(File).all()), 144)
scanob2 = scan.Scan(TEST_DIR)
scanob2.add_files()
# it is perfectly ok, since we don't update collection, but just added
# same directory twice.
self.assertEqual(len(ses.query(File).all()), 287)
file_ob = scanob._files[2]
file2_ob = scanob2._files[2]
# File objects are different
self.assertTrue(file_ob is not file2_ob)
# While Image objects points to the same file
self.assertTrue(file_ob.images[0].filename == \
file2_ob.images[0].filename)
# they are different objects
self.assertTrue(file_ob.images[0] is not file2_ob.images[0])
ses.close()
if __name__ == "__main__":
os.chdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), "../"))
unittest.main()

View File

@@ -1,133 +0,0 @@
"""
Project: pyGTKtalog
Description: Tests for Video class.
Type: test
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2008-12-15
"""
import unittest
import os
from pygtktalog.video import Video
class TestVideo(unittest.TestCase):
"""Class for retrive midentify script output and put it in dict.
Usually there is no need for such a detailed movie/clip information.
Video script belongs to mplayer package.
"""
def test_avi(self):
"""test mock avi file, should return dict with expected values"""
avi = Video("mocks/m.avi")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '85')
self.assertEqual(avi.tags['width'], 128)
self.assertEqual(avi.tags['audio_no_channels'], 2)
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], 'xvid')
self.assertEqual(avi.tags['length'], 4)
self.assertEqual(avi.tags['audio_codec'], 'mp3')
self.assertEqual(avi.tags['video_codec'], 'ffodivx')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertEqual(avi.tags['container'], 'avi')
def test_avi2(self):
"""test another mock avi file, should return dict with expected
values"""
avi = Video("mocks/m1.avi")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '85')
self.assertEqual(avi.tags['width'], 128)
self.assertEqual(avi.tags['audio_no_channels'], 2)
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], 'h264')
self.assertEqual(avi.tags['length'], 4)
self.assertEqual(avi.tags['audio_codec'], 'mp3')
self.assertEqual(avi.tags['video_codec'], 'ffh264')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertEqual(avi.tags['container'], 'avi')
def test_mkv(self):
"""test mock mkv file, should return dict with expected values"""
avi = Video("mocks/m.mkv")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '8192')
self.assertEqual(avi.tags['width'], 128)
self.assertTrue(avi.tags['audio_no_channels'] in (1, 2))
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], 'mp4v')
self.assertEqual(avi.tags['length'], 4)
self.assertTrue(avi.tags['audio_codec'] in ('a52', 'ffac3'))
self.assertEqual(avi.tags['video_codec'], 'ffodivx')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertTrue(avi.tags['container'] in ('mkv', 'lavfpref'))
def test_mpg(self):
"""test mock mpg file, should return dict with expected values"""
avi = Video("mocks/m.mpg")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertFalse(avi.tags.has_key('audio_format'))
self.assertEqual(avi.tags['width'], 128)
self.assertFalse(avi.tags.has_key('audio_no_channels'))
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], '0x10000001')
self.assertFalse(avi.tags.has_key('lenght'))
self.assertFalse(avi.tags.has_key('audio_codec'))
self.assertEqual(avi.tags['video_codec'], 'ffmpeg1')
self.assertFalse(avi.tags.has_key('duration'))
self.assertEqual(avi.tags['container'], 'mpeges')
def test_ogm(self):
"""test mock ogm file, should return dict with expected values"""
avi = Video("mocks/m.ogm")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '8192')
self.assertEqual(avi.tags['width'], 160)
self.assertTrue(avi.tags['audio_no_channels'] in (1, 2))
self.assertEqual(avi.tags['height'], 120)
self.assertEqual(avi.tags['video_format'], 'h264')
self.assertEqual(avi.tags['length'], 4)
self.assertTrue(avi.tags['audio_codec'] in ('a52', 'ffac3'))
self.assertEqual(avi.tags['video_codec'], 'ffh264')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertTrue(avi.tags['container'] in ('ogg', 'lavfpref'))
def test_capture(self):
"""test capture with some small movie and play a little with tags"""
avi = Video("mocks/m.avi")
filename = avi.capture()
self.assertTrue(filename != None)
self.assertTrue(os.path.exists(filename))
file_size = os.stat(filename)[6]
self.assertAlmostEqual(file_size/10000.0, 0.9, 0)
os.unlink(filename)
for length in (480, 380, 4):
avi.tags['length'] = length
filename = avi.capture()
self.assertTrue(filename is not None)
os.unlink(filename)
avi.tags['length'] = 3
self.assertTrue(avi.capture() is None)
avi.tags['length'] = 4
avi.tags['width'] = 0
self.assertTrue(avi.capture() is None)
avi.tags['width'] = 1025
filename = avi.capture()
self.assertTrue(filename is not None)
os.unlink(filename)
del(avi.tags['length'])
self.assertTrue(avi.capture() is None)
self.assertTrue(len(str(avi)) > 0)
if __name__ == "__main__":
os.chdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), "../"))
unittest.main()

192
tests/scan_test.py Normal file
View File

@@ -0,0 +1,192 @@
"""
Project: pyGTKtalog
Description: Tests for scan files.
Type: test
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2011-03-26
"""
import os
import shutil
import tempfile
import unittest
from pygtktalog import scan
from pygtktalog.dbobjects import File, Config, Image
from pygtktalog.dbcommon import connect, Session
def populate_with_mock_files(dir_):
"""Make some files under specified directory, return number of files"""
files1 = ['anim.mkv', 'text.txt', 'image.png', 'photoimage.jpg']
files2 = ['music.mp3', 'loseless.flac']
files_no = 0
for file_ in files1:
with open(os.path.join(dir_, file_), "wb") as fobj:
fobj.write("\xde\xad\xbe\xef" * len(file_))
files_no += 1
os.symlink(os.path.join(dir_, files1[-1]), os.path.join(dir_, 'link.jpg'))
files_no += 1
os.mkdir(os.path.join(dir_, 'directory'))
for file_ in files2:
with open(os.path.join(dir_, 'directory', file_), "wb") as fobj:
fobj.write("\xfe\xad\xfa\xce" * len(file_))
files_no += 1
return files_no
# TODO: exchange this with mock module
def _fake_video(obj, fobj, filepath):
fobj.images.append(Image())
fobj.images[0].filename = filepath + ".jpg"
def _fake_audio(obj, fobj, filepath):
pass
def _fake_image(obj, fobj, filepath):
pass
scan.Scan._video = _fake_video
scan.Scan._audio = _fake_audio
scan.Scan._image = _fake_image
class TestScan(unittest.TestCase):
"""
Test cases for scan functionality
1. execution scan function:
1.1 simple case - should pass
1.2 non-existent directory passed
1.3 file passed
1.4 directory has permission that forbids file listing
2. rescan directory; looking for changes
2.0 don't touch records for changed files (same directories, same
filename, same type and size)
2.1 search for files of the same type, same size.
2.2 change parent node for moved files (don't insert new)
3. adding new directory tree which contains same files like already stored
in the database
"""
def setUp(self):
self.image_path = tempfile.mkdtemp()
self.scan_dir = tempfile.mkdtemp()
self.no_of_files = populate_with_mock_files(self.scan_dir)
connect()
root = File()
root.id = 1
root.filename = 'root'
root.size = 0
root.source = 0
root.type = 0
root.parent_id = 1
config = Config()
config.key = 'image_path'
config.value = self.image_path
sess = Session()
sess.add(root)
sess.add(config)
sess.commit()
def tearDown(self):
shutil.rmtree(self.image_path)
shutil.rmtree(self.scan_dir)
def test_happy_scenario(self):
"""
make scan, count items
"""
scanob = scan.Scan(self.scan_dir)
result_list = scanob.add_files()
# the number of added objects (files/links only) + "directory" +
# topmost directory (self.scan_dir)
self.assertEqual(len(result_list), self.no_of_files + 2)
# all of topmost nide children - including "directory", but excluding
# its contents - so it is all_files + 1 (directory) - 2 files from
# subdir contents
self.assertEqual(len(result_list[0].children), self.no_of_files - 1)
# check soft links
self.assertEqual(len([x for x in result_list if x.type == 3]), 1)
def test_wrong_and_nonexistent(self):
"""
Check for accessing non existent directory, regular file instead of
the directory.
"""
scanobj = scan.Scan('/nonexistent_directory_')
self.assertRaises(OSError, scanobj.add_files)
scanobj.path = '/root'
self.assertRaises(scan.NoAccessError, scanobj.add_files)
scanobj.path = '/bin/sh'
self.assertRaises(scan.NoAccessError, scanobj.add_files)
def test_abort_functionality(self):
scanobj = scan.Scan(self.scan_dir)
scanobj.abort = True
self.assertEqual(None, scanobj.add_files())
def test_double_scan(self):
"""
Do the scan twice.
"""
ses = Session()
self.assertEqual(len(ses.query(File).all()), 1)
scanob = scan.Scan(self.scan_dir)
scanob.add_files()
# dirs: main one + "directory" subdir
self.assertEqual(len(ses.query(File).filter(File.type == 1).all()), 2)
# files: '-1' for existing link there, which have it's own type
self.assertEqual(len(ses.query(File).filter(File.type == 2).all()),
self.no_of_files - 1)
# links
self.assertEqual(len(ses.query(File).filter(File.type == 3).all()), 1)
# all - sum of all of the above + root node
self.assertEqual(len(ses.query(File).all()), self.no_of_files + 2 + 1)
# it is perfectly ok, since we don't update collection, but just added
# same directory twice.
scanob2 = scan.Scan(self.scan_dir)
scanob2.add_files()
# we have twice as much of files (self.no_of_files), plus 2 * of
# topmost dir and subdir "directory" (means 4) + root element
self.assertEqual(len(ses.query(File).all()), self.no_of_files * 2 + 5)
# get some movie files to examine
file_ob = [x for x in scanob._files if x.filename == 'anim.mkv'][0]
file2_ob = [x for x in scanob2._files if x.filename == 'anim.mkv'][0]
# File objects are different
self.assertTrue(file_ob is not file2_ob)
# While Image objects points to the same file
self.assertTrue(file_ob.images[0].filename == \
file2_ob.images[0].filename)
# they are different objects
self.assertTrue(file_ob.images[0] is not file2_ob.images[0])
ses.close()
if __name__ == "__main__":
os.chdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), "../"))
unittest.main()

364
tests/video_test.py Normal file
View File

@@ -0,0 +1,364 @@
"""
Project: pyGTKtalog
Description: Tests for Video class.
Type: test
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2008-12-15
"""
import os
import unittest
import PIL
from pygtktalog.video import Video
DATA = {"m1.avi": """ID_VIDEO_ID=0
ID_AUDIO_ID=1
ID_FILENAME=m1.avi
ID_DEMUXER=avi
ID_VIDEO_FORMAT=H264
ID_VIDEO_BITRATE=46184
ID_VIDEO_WIDTH=128
ID_VIDEO_HEIGHT=96
ID_VIDEO_FPS=30.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=85
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=0
ID_START_TIME=0.00
ID_LENGTH=4.03
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffh264
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=mpg123
ID_EXIT=EOF
""",
"m.avi": """ID_VIDEO_ID=0
ID_AUDIO_ID=1
ID_FILENAME=m.avi
ID_DEMUXER=avi
ID_VIDEO_FORMAT=XVID
ID_VIDEO_BITRATE=313536
ID_VIDEO_WIDTH=128
ID_VIDEO_HEIGHT=96
ID_VIDEO_FPS=30.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=85
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=0
ID_START_TIME=0.00
ID_LENGTH=4.03
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffodivx
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=mpg123
ID_EXIT=EOF""",
"m.mkv": """ID_VIDEO_ID=0
ID_AUDIO_ID=0
ID_CLIP_INFO_NAME0=title
ID_CLIP_INFO_VALUE0=Avidemux
ID_CLIP_INFO_NAME1=encoder
ID_CLIP_INFO_VALUE1=Lavf51.12.1
ID_CLIP_INFO_N=2
ID_FILENAME=m.mkv
ID_DEMUXER=lavfpref
ID_VIDEO_FORMAT=MP4V
ID_VIDEO_BITRATE=0
ID_VIDEO_WIDTH=128
ID_VIDEO_HEIGHT=96
ID_VIDEO_FPS=30.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=8192
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=1
ID_START_TIME=0.00
ID_LENGTH=4.07
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffodivx
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=ffac3
ID_EXIT=EOF""",
"m.mpg": """ID_VIDEO_ID=0
ID_FILENAME=m.mpg
ID_DEMUXER=mpeges
ID_VIDEO_FORMAT=0x10000001
ID_VIDEO_BITRATE=2200000
ID_VIDEO_WIDTH=128
ID_VIDEO_HEIGHT=96
ID_VIDEO_FPS=30.000
ID_VIDEO_ASPECT=0.0000
ID_START_TIME=0.00
ID_LENGTH=0.97
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffmpeg1
ID_EXIT=EOF""",
"m.ogm": """ID_VIDEO_ID=0
ID_AUDIO_ID=0
ID_FILENAME=m.ogm
ID_DEMUXER=lavfpref
ID_VIDEO_FORMAT=H264
ID_VIDEO_BITRATE=0
ID_VIDEO_WIDTH=160
ID_VIDEO_HEIGHT=120
ID_VIDEO_FPS=30.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=8192
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=1
ID_START_TIME=0.00
ID_LENGTH=4.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffh264
ID_AUDIO_BITRATE=128000
ID_AUDIO_RATE=22050
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=ffac3
ID_EXIT=EOF""",
"m.wmv":"""ID_AUDIO_ID=1
ID_VIDEO_ID=2
ID_FILENAME=m.wmv
ID_DEMUXER=asf
ID_VIDEO_FORMAT=WMV3
ID_VIDEO_BITRATE=1177000
ID_VIDEO_WIDTH=852
ID_VIDEO_HEIGHT=480
ID_VIDEO_FPS=1000.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=353
ID_AUDIO_BITRATE=0
ID_AUDIO_RATE=0
ID_AUDIO_NCH=0
ID_START_TIME=4.00
ID_LENGTH=4656.93
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffwmv3
ID_AUDIO_BITRATE=64028
ID_AUDIO_RATE=48000
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=ffwmav2
ID_EXIT=EOF""",
"m.mp4": """ID_VIDEO_ID=0
ID_AUDIO_ID=0
ID_AID_0_LANG=unk
ID_CLIP_INFO_NAME0=major_brand
ID_CLIP_INFO_VALUE0=isom
ID_CLIP_INFO_NAME1=minor_version
ID_CLIP_INFO_VALUE1=512
ID_CLIP_INFO_NAME2=compatible_brands
ID_CLIP_INFO_VALUE2=isomiso2avc1mp41
ID_CLIP_INFO_NAME3=encoder
ID_CLIP_INFO_VALUE3=Lavf56.25.101
ID_CLIP_INFO_N=4
ID_FILENAME=m.mp4
ID_DEMUXER=lavfpref
ID_VIDEO_FORMAT=H264
ID_VIDEO_BITRATE=1263573
ID_VIDEO_WIDTH=720
ID_VIDEO_HEIGHT=404
ID_VIDEO_FPS=25.000
ID_VIDEO_ASPECT=0.0000
ID_AUDIO_FORMAT=MP4A
ID_AUDIO_BITRATE=155088
ID_AUDIO_RATE=44100
ID_AUDIO_NCH=2
ID_START_TIME=0.00
ID_LENGTH=69.18
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_VIDEO_CODEC=ffh264
ID_AUDIO_BITRATE=155082
ID_AUDIO_RATE=44100
ID_AUDIO_NCH=2
ID_AUDIO_CODEC=ffaac
ID_EXIT=EOF"""}
# TODO: exchange this with mock
class Readlines(object):
def __init__(self, key=None):
self.data = DATA.get(key, "")
def readlines(self):
return self.data.split('\n')
def mock_popen(command):
key = None
if 'midentify' in command:
key = command.split('"')[1]
elif 'jpeg:outdir' in command:
# simulate capture for mplayer
img_dir = command.split('"')[-2]
img = PIL.Image.new('RGBA', (320, 200))
with open(os.path.join(img_dir, "00000001.jpg"), "wb") as fobj:
img.save(fobj)
return Readlines(key)
os.popen = mock_popen
class TestVideo(unittest.TestCase):
"""test class for retrive midentify script output"""
def test_avi(self):
"""test mock avi file, should return dict with expected values"""
avi = Video("m.avi")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '85')
self.assertEqual(avi.tags['width'], 128)
self.assertEqual(avi.tags['audio_no_channels'], 2)
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], 'xvid')
self.assertEqual(avi.tags['length'], 4)
self.assertEqual(avi.tags['audio_codec'], 'mpg123')
self.assertEqual(avi.tags['video_codec'], 'ffodivx')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertEqual(avi.tags['container'], 'avi')
def test_avi2(self):
"""test another mock avi file, should return dict with expected
values"""
avi = Video("m1.avi")
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
self.assertEqual(avi.tags['audio_format'], '85')
self.assertEqual(avi.tags['width'], 128)
self.assertEqual(avi.tags['audio_no_channels'], 2)
self.assertEqual(avi.tags['height'], 96)
self.assertEqual(avi.tags['video_format'], 'h264')
self.assertEqual(avi.tags['length'], 4)
self.assertEqual(avi.tags['audio_codec'], 'mpg123')
self.assertEqual(avi.tags['video_codec'], 'ffh264')
self.assertEqual(avi.tags['duration'], '00:00:04')
self.assertEqual(avi.tags['container'], 'avi')
def test_mkv(self):
"""test mock mkv file, should return dict with expected values"""
mkv = Video("m.mkv")
self.assertTrue(len(mkv.tags) != 0, "result should have lenght > 0")
self.assertEqual(mkv.tags['audio_format'], '8192')
self.assertEqual(mkv.tags['width'], 128)
self.assertTrue(mkv.tags['audio_no_channels'] in (1, 2))
self.assertEqual(mkv.tags['height'], 96)
self.assertEqual(mkv.tags['video_format'], 'mp4v')
self.assertEqual(mkv.tags['length'], 4)
self.assertTrue(mkv.tags['audio_codec'] in ('a52', 'ffac3'))
self.assertEqual(mkv.tags['video_codec'], 'ffodivx')
self.assertEqual(mkv.tags['duration'], '00:00:04')
self.assertTrue(mkv.tags['container'] in ('mkv', 'lavfpref'))
def test_mpg(self):
"""test mock mpg file, should return dict with expected values"""
mpg = Video("m.mpg")
self.assertTrue(len(mpg.tags) != 0, "result should have lenght > 0")
self.assertFalse(mpg.tags.has_key('audio_format'))
self.assertEqual(mpg.tags['width'], 128)
self.assertFalse(mpg.tags.has_key('audio_no_channels'))
self.assertEqual(mpg.tags['height'], 96)
self.assertEqual(mpg.tags['video_format'], '0x10000001')
self.assertFalse(mpg.tags.has_key('lenght'))
self.assertFalse(mpg.tags.has_key('audio_codec'))
self.assertEqual(mpg.tags['video_codec'], 'ffmpeg1')
self.assertFalse(mpg.tags.has_key('duration'))
self.assertEqual(mpg.tags['container'], 'mpeges')
def test_ogm(self):
"""test mock ogm file, should return dict with expected values"""
ogm = Video("m.ogm")
self.assertTrue(len(ogm.tags) != 0, "result should have lenght > 0")
self.assertEqual(ogm.tags['audio_format'], '8192')
self.assertEqual(ogm.tags['width'], 160)
self.assertTrue(ogm.tags['audio_no_channels'] in (1, 2))
self.assertEqual(ogm.tags['height'], 120)
self.assertEqual(ogm.tags['video_format'], 'h264')
self.assertEqual(ogm.tags['length'], 4)
self.assertTrue(ogm.tags['audio_codec'] in ('a52', 'ffac3'))
self.assertEqual(ogm.tags['video_codec'], 'ffh264')
self.assertEqual(ogm.tags['duration'], '00:00:04')
self.assertTrue(ogm.tags['container'] in ('ogg', 'lavfpref'))
def test_wmv(self):
"""test mock wmv file, should return dict with expected values"""
wmv = Video("m.wmv")
self.assertTrue(len(wmv.tags) != 0, "result should have lenght > 0")
self.assertEqual(wmv.tags['audio_format'], '353')
self.assertEqual(wmv.tags['width'], 852)
self.assertEqual(wmv.tags['audio_no_channels'], 2)
self.assertEqual(wmv.tags['height'], 480)
self.assertEqual(wmv.tags['video_format'], 'wmv3')
self.assertEqual(wmv.tags['length'], 4656)
self.assertEqual(wmv.tags['audio_codec'], 'ffwmav2')
self.assertEqual(wmv.tags['video_codec'], 'ffwmv3')
self.assertEqual(wmv.tags['duration'], '01:17:32')
self.assertEqual(wmv.tags['container'], 'asf')
def test_mp4(self):
"""test mock mp4 file, should return dict with expected values"""
mp4 = Video("m.mp4")
self.assertTrue(len(mp4.tags) != 0, "result should have lenght > 0")
self.assertEqual(mp4.tags['audio_format'], 'mp4a')
self.assertEqual(mp4.tags['width'], 720)
self.assertEqual(mp4.tags['audio_no_channels'], 2)
self.assertEqual(mp4.tags['height'], 404)
self.assertEqual(mp4.tags['video_format'], 'h264')
self.assertEqual(mp4.tags['length'], 69)
self.assertEqual(mp4.tags['audio_codec'], 'ffaac')
self.assertEqual(mp4.tags['video_codec'], 'ffh264')
self.assertEqual(mp4.tags['duration'], '00:01:09')
self.assertEqual(mp4.tags['container'], 'lavfpref')
def test_capture(self):
"""test capture with some small movie and play a little with tags"""
avi = Video("m.avi")
filename = avi.capture()
self.assertTrue(filename != None)
self.assertTrue(os.path.exists(filename))
file_size = os.stat(filename)[6]
self.assertAlmostEqual(file_size/10000.0, 0.151, 0)
os.unlink(filename)
for length in (480, 380, 4):
avi.tags['length'] = length
filename = avi.capture()
self.assertTrue(filename is not None)
os.unlink(filename)
avi.tags['length'] = 3
self.assertTrue(avi.capture() is None)
avi.tags['length'] = 4
avi.tags['width'] = 0
self.assertTrue(avi.capture() is None)
avi.tags['width'] = 1025
filename = avi.capture()
self.assertTrue(filename is not None)
os.unlink(filename)
del(avi.tags['length'])
self.assertTrue(avi.capture() is None)
self.assertTrue(len(str(avi)) > 0)
if __name__ == "__main__":
os.chdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), "../"))
unittest.main()