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

* Added context menu item for possibility to remove tags form file or

even files.
 * Change of a way to display files after tag click - all files, that
   have clicked tag will be appear in files TreeView.
This commit is contained in:
2008-05-05 21:17:40 +00:00
parent 574767de2a
commit aea871b30e
5 changed files with 332 additions and 46 deletions

View File

@@ -759,4 +759,80 @@
</widget> </widget>
</child> </child>
</widget> </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> </glade-interface>

View File

@@ -861,6 +861,20 @@
<signal name="activate" handler="on_add_tag1_activate"/> <signal name="activate" handler="on_add_tag1_activate"/>
</widget> </widget>
</child> </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="separator11">
<property name="visible">True</property>
</widget>
</child>
<child> <child>
<widget class="GtkMenuItem" id="add_thumb1"> <widget class="GtkMenuItem" id="add_thumb1">
<property name="visible">True</property> <property name="visible">True</property>
@@ -967,7 +981,7 @@
<child> <child>
<widget class="GtkMenuItem" id="img_delete"> <widget class="GtkMenuItem" id="img_delete">
<property name="visible">True</property> <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="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Delete images</property> <property name="label" translatable="yes">_Delete images</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<signal name="activate" handler="on_img_delete_activate"/> <signal name="activate" handler="on_img_delete_activate"/>

View File

@@ -193,7 +193,6 @@ class MainController(Controller):
int(event.x), int(event.y)) int(event.x), int(event.y))
iterator = textview.get_iter_at_location(x, y) iterator = textview.get_iter_at_location(x, y)
# call open_url if an URL is assigned to the iter
tags = iterator.get_tags() tags = iterator.get_tags()
if len(tags) == 1: if len(tags) == 1:
@@ -215,7 +214,9 @@ class MainController(Controller):
else: else:
self.view['tag_path'].set_text(txt + ", " +tag1) self.view['tag_path'].set_text(txt + ", " +tag1)
self.__tag_cloud() self.__tag_cloud()
self.model.get_root_entries()
self.view['files'].set_model(self.model.files_list)
self.__hide_details()
def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y, def on_tag_cloud_textview_drag_data_received(self, widget, context, x, y,
selection, targetType, time): selection, targetType, time):
@@ -401,6 +402,13 @@ class MainController(Controller):
self.view['tag_path_box'].hide() self.view['tag_path_box'].hide()
self.model.selected_tags = [] self.model.selected_tags = []
self.model.refresh_discs_tree() self.model.refresh_discs_tree()
# cleanup files and detiles
try:
self.model.files_list.clear()
except:
pass
self.__hide_details()
self.on_discs_cursor_changed(w) self.on_discs_cursor_changed(w)
self.__tag_cloud() self.__tag_cloud()
@@ -472,6 +480,7 @@ class MainController(Controller):
self.view['tag_path'].set_text(txt + ", " +tag1) self.view['tag_path'].set_text(txt + ", " +tag1)
self.__tag_cloud() self.__tag_cloud()
#elif event.type == gtk.gdk.MOTION_NOTIFY: #elif event.type == gtk.gdk.MOTION_NOTIFY:
# window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT) # window = tag_cloud.get_window(gtk.TEXT_WINDOW_TEXT)
# if window: # if window:
@@ -511,10 +520,13 @@ class MainController(Controller):
return return
self.model.new() self.model.new()
# clear "details" buffer # cleanup files and details
buf = self.view['description'].get_buffer() try:
buf.set_text("") self.model.files_list.clear()
self.view['description'].set_buffer(buf) except:
pass
self.__hide_details()
self.view['tag_path_box'].hide()
self.__activate_ui() self.__activate_ui()
self.__tag_cloud() self.__tag_cloud()
@@ -640,6 +652,17 @@ class MainController(Controller):
if not path: if not path:
path = Dialogs.LoadDBFile().run() path = Dialogs.LoadDBFile().run()
# 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 path: if path:
if not self.model.open(path): if not self.model.open(path):
Dialogs.Err("Error opening file - pyGTKtalog", Dialogs.Err("Error opening file - pyGTKtalog",
@@ -928,6 +951,28 @@ class MainController(Controller):
return return
# NOTE: add tags / images # NOTE: add tags / images
def on_delete_tag_activate(self, menu_item):
ids = self.__get_tv_selection_ids(self.view['files'])
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)
self.model.get_root_entries()
self.view['files'].set_model(self.model.files_list)
self.__tag_cloud()
return
def on_add_tag1_activate(self, menu_item): def on_add_tag1_activate(self, menu_item):
#try: #try:
tags = Dialogs.TagsDialog().run() tags = Dialogs.TagsDialog().run()
@@ -1123,7 +1168,7 @@ class MainController(Controller):
print "abort = %s" % self.model.abort print "abort = %s" % self.model.abort
print "self.model.config.recent = %s" % self.model.config.recent print "self.model.config.recent = %s" % self.model.config.recent
print "source: %s" % self.model.source print "source: %s" % self.model.source
self.__tag_cloud()
##################### #####################
# observed properetis # observed properetis
@@ -1335,11 +1380,7 @@ class MainController(Controller):
buf = gtk.TextBuffer() buf = gtk.TextBuffer()
if not file_id: if not file_id:
buf.set_text('') self.__hide_details()
self.view['img_container'].hide()
self.view['exifinfo'].hide()
self.view['thumb_box'].hide()
self.view['description'].set_buffer(buf)
return return
#self.view['description'].show() #self.view['description'].show()
set = self.model.get_file_info(file_id) set = self.model.get_file_info(file_id)
@@ -1452,6 +1493,16 @@ class MainController(Controller):
cloud['name'] + "(%d)" % cloud['count'], cloud['name'] + "(%d)" % cloud['count'],
tag) tag)
except: except:
print "fuckup", cloud if __debug__:
print "c_main.py: __tag_cloud: error on tag:", cloud
def __hide_details(self):
"""hide details and "reset" tabs visibility"""
buf = self.view['description'].get_buffer()
buf.set_text('')
self.view['img_container'].hide()
self.view['exifinfo'].hide()
self.view['thumb_box'].hide()
self.view['description'].set_buffer(buf)
pass # end of class pass # end of class

View File

@@ -162,6 +162,25 @@ class MainModel(ModelMT):
self.get_tags() self.get_tags()
return return
def get_tags_by_file_id(self, file_id_list):
"""return dictionary of tags by connected files"""
# SQL: get tags by file_ids
if len(file_id_list) == 1:
sql = "(%d)" % file_id_list[0]
else:
sql = str(tuple(file_id_list))
sql = """SELECT DISTINCT t.id, t.tag FROM tags_files f
LEFT JOIN tags t on t.id = f.tag_id
WHERE f.file_id in """ + sql + """
ORDER BY t.tag"""
self.db_cursor.execute(sql)
res = self.db_cursor.fetchall()
retval = {}
for tag in res:
retval[tag[0]] = tag[1]
return retval
def get_tag_by_id(self, tag_id): def get_tag_by_id(self, tag_id):
"""get tag (string) by its id""" """get tag (string) by its id"""
# SQL: get tag by id # SQL: get tag by id
@@ -192,6 +211,18 @@ class MainModel(ModelMT):
return tmp return tmp
def delete_tags(self, file_id_list, tag_id_list):
"""remove tags from selected files"""
for file_id in file_id_list:
# SQL: remove tags for selected file
if len(tag_id_list) == 1:
sql = "(%d)" % tag_id_list[0]
else:
sql = str(tuple(tag_id_list))
sql = """DELETE FROM tags_files WHERE file_id = ?
AND tag_id IN """ + sql
self.db_cursor.execute(sql, (int(file_id), ))
def get_tags(self): def get_tags(self):
"""fill tags dict with values from db""" """fill tags dict with values from db"""
if not self.selected_tags: if not self.selected_tags:
@@ -398,7 +429,7 @@ class MainModel(ModelMT):
self.__create_database() self.__create_database()
self.__clear_trees() self.__clear_trees()
self.tag_cloud = [] self.tag_cloud = []
self.selected_tags = None self.selected_tags = {}
return return
def save(self, filename=None): def save(self, filename=None):
@@ -420,6 +451,8 @@ class MainModel(ModelMT):
self.unsaved_project = False self.unsaved_project = False
self.__create_internal_dirname() self.__create_internal_dirname()
self.filename = filename self.filename = filename
self.tag_cloud = []
self.selected_tags = {}
try: try:
tar = tarfile.open(filename, "r:gz") tar = tarfile.open(filename, "r:gz")
@@ -501,7 +534,6 @@ class MainModel(ModelMT):
for row in self.files_list: for row in self.files_list:
if row[0] == file_id: if row[0] == file_id:
print row[0], row[1], row[2]
row[1] = new_name row[1] = new_name
break break
@@ -522,11 +554,24 @@ class MainModel(ModelMT):
"""re-fetch discs tree""" """re-fetch discs tree"""
self.__fetch_db_into_treestore() self.__fetch_db_into_treestore()
def get_root_entries(self, parent_id): def get_root_entries(self, parent_id=None):
"""Get all children down from sepcified root""" """Get all children down from sepcified root"""
self.__clear_files_tree() self.__clear_files_tree()
# if we are in "tag" mode, do the boogie
# directories first # directories first
if not parent_id and self.selected_tags:
# no parent_id, get all the tagged dirs
id_filter = self.__filter2()
if id_filter != None:
if len(id_filter) == 1:
id_filter = "(%d)" % id_filter[0]
else:
id_filter = str(tuple(id_filter))
sql = """SELECT id, filename, size, date FROM files
WHERE parent_id!=id AND type=1 AND id in """ + \
id_filter + """ ORDER BY filename"""
else:
# we have parent_id, get all the tagged dirs with parent_id
if not self.selected_tags: if not self.selected_tags:
sql = """SELECT id, filename, size, date FROM files sql = """SELECT id, filename, size, date FROM files
WHERE parent_id=? AND type=1 WHERE parent_id=? AND type=1
@@ -541,8 +586,11 @@ class MainModel(ModelMT):
sql="""SELECT id, filename, size, date FROM files sql="""SELECT id, filename, size, date FROM files
WHERE 1=0""" WHERE 1=0"""
if not parent_id and self.selected_tags:
self.db_cursor.execute(sql)
else:
self.db_cursor.execute(sql, (parent_id,)) self.db_cursor.execute(sql, (parent_id,))
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
myiter = self.files_list.insert_before(None, None) myiter = self.files_list.insert_before(None, None)
@@ -556,6 +604,15 @@ class MainModel(ModelMT):
self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY) self.files_list.set_value(myiter, 6, gtk.STOCK_DIRECTORY)
# all the rest # all the rest
if not parent_id and self.selected_tags:
# no parent_id, get all the tagged files
if id_filter != None:
sql = """SELECT f.id, f.filename, f.size, f.date, f.type
FROM files f
WHERE f.type!=1 AND id IN """ + id_filter + \
""" ORDER BY f.filename"""
else:
# we have parent_id, get all the tagged files with parent_id
if not self.selected_tags: if not self.selected_tags:
sql = """SELECT f.id, f.filename, f.size, f.date, f.type sql = """SELECT f.id, f.filename, f.size, f.date, f.type
FROM files f FROM files f
@@ -572,8 +629,11 @@ class MainModel(ModelMT):
FROM files f FROM files f
WHERE 1=0""" WHERE 1=0"""
if not parent_id and self.selected_tags:
self.db_cursor.execute(sql)
else:
self.db_cursor.execute(sql, (parent_id,)) self.db_cursor.execute(sql, (parent_id,))
data = self.db_cursor.fetchall() data = self.db_cursor.fetchall()
for row in data: for row in data:
myiter = self.files_list.insert_before(None, None) myiter = self.files_list.insert_before(None, None)
@@ -948,6 +1008,7 @@ class MainModel(ModelMT):
def __create_internal_dirname(self): def __create_internal_dirname(self):
"""create temporary directory for working thumb/image files and """create temporary directory for working thumb/image files and
database""" database"""
# TODO: change this stupid rutine into tempfile mkdtemp method
self.cleanup() self.cleanup()
self.internal_dirname = "/tmp/pygtktalog%d" % \ self.internal_dirname = "/tmp/pygtktalog%d" % \
datetime.now().microsecond datetime.now().microsecond
@@ -1085,6 +1146,30 @@ class MainModel(ModelMT):
return list(set(parents).union(filtered_ids)) return list(set(parents).union(filtered_ids))
def __filter2(self):
"""return list of ids of files (WITHOUT their parent) that
corresponds to tags"""
filtered_ids = []
count = 0
for tid in self.selected_tags:
temp1 = []
sql = """SELECT file_id
FROM tags_files
WHERE tag_id=? """
self.db_cursor.execute(sql, (tid, ))
data = self.db_cursor.fetchall()
for row in data:
temp1.append(row[0])
if count > 0:
filtered_ids = list(set(filtered_ids).intersection(temp1))
else:
filtered_ids = temp1
count += 1
return filtered_ids
def __scan(self): def __scan(self):
"""scan content of the given path""" """scan content of the given path"""
self.busy = True self.busy = True

View File

@@ -23,6 +23,7 @@
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
import gtk import gtk
import gobject
import os import os
import utils.globals import utils.globals
@@ -498,6 +499,65 @@ class TagsDialog(object):
return entry.get_text() return entry.get_text()
return None return None
class TagsRemoveDialog(object):
"""Sepcific dialog for display stats"""
def __init__(self, tag_dict=None):
self.gladefile = os.path.join(utils.globals.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")
# fill model with dict
model = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
for tag in self.tag_dict:
myiter = model.insert_before(None, None)
model.set_value(myiter, 0, tag)
model.set_value(myiter, 1, self.tag_dict[tag])
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): class EditDialog(object):
"""Sepcific dialog for display stats""" """Sepcific dialog for display stats"""