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:
@@ -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()
|
||||
375
locale/pl.po
375
locale/pl.po
@@ -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 ""
|
||||
@@ -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 ""
|
||||
@@ -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.
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"><b>CD/DVD drive options</b></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"><b>General options</b></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"><b>Misc</b></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"><b>Confirmations</b></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"><b>Files extensions</b></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>
|
||||
@@ -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"><b>Select directory and enter label</b></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"><b>Filename</b></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"><b>Description</b></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"><b>Note</b></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>
|
||||
@@ -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"><b>Search results</b></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>
|
||||
@@ -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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
1696
src/ctrls/c_main.py
1696
src/ctrls/c_main.py
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -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
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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>.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
class A (object):
|
||||
a = 10
|
||||
pass
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
2397
src/lib/no_thumb.py
2397
src/lib/no_thumb.py
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
2002
src/models/m_main.py
2002
src/models/m_main.py
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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()
|
||||
@@ -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
192
tests/scan_test.py
Normal 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
364
tests/video_test.py
Normal 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()
|
||||
Reference in New Issue
Block a user