zim-wiki team mailing list archive
-
zim-wiki team
-
Mailing list archive
-
Message #00780
new plugin: fileview
Am Dienstag, den 25.05.2010, 12:21 +0200 schrieb Jaap Karssenberg:
> I'm really interested in the plugin to preview / browse attachments.
> This is something I wanted to create myself, but didn't find time for
> yet. Might even want this as core functionality instead of as a
> plugin.
Sorry for the even later reply.
Here is a very basic version of the plugin.
Still a lot to do, but works for me. I use it to browse collections of
pdf files (technical documentation, data sheets, etc).
For more flexibility I suggest changing the zim layout to something like
this:
+-----------------------------+
|menu |
+-----+----------------+------+
| | top pane | |
| | | |
| s +----------------+ s |
| i | wiki editor | i |
| d | | d |
| e | | e |
| b +----------------+ b |
| a |tabs| | a |
| r | bottom pane | r |
| | | |
+----------------------+------+
# -*- coding: utf-8 -*-
#
# Copyright 2010 Thorsten Hackbarth <thorsten.hackbarth@xxxxxx>
# License: same as zim (gpl)
#
# 2010-06-29
#
# TODO:
# * integer plugin_preferences do not to work as expected (zim bug?)
# * toggle: hide/show => disconect/update
# * toggle: toolbar button state wrong
# * toggle-btn icon
# * where to store thumbnails?
# in current dir: .thumb_<filename>.jpg (hide on windows?)
# ~/.thumbnails/ (gnome/nautilus)
# .zim/cache/
# Thumbs.db
# let the user decide
# * thumbnail all images?
# * dont start all thumbnailing processes at a time, and make them nice 10
# * small and lagre thumbs
# * textrendering: syntay-hl
# * use mimtype not extension
# * rethumb: thmub-size==0 or broken (e.g. shutdown while thumbnailing)
# * option to remove all thumbnails
# * update view when thumb-cration finished
# * code cleanup
# * new gui concept for zim : sidepane r/l,bottom- and top pane both with tabs (see gedit)
# * show file infos in tooltip (which?)
# * update icon when thumbnail is ready
# * mimi-type specific icons
# * evaluate imagemagick python libs
#
#
# tooltip example
# http://nullege.com/codes/show/src@pygtk-2.14.1@examples@pygtk-demo@demos@xxxxxxxxxx/160/gtk.gdk.Color
# file info example
# http://ubuntuforums.org/showthread.php?t=880967
#
'''Zim plugin to display files in atachments folder.'''
import gobject
import gtk
import pango
import os
import stat
import time
import re
import logging
from datetime import date as dateclass
from zim.plugins import PluginClass
from zim.gui import Dialog
from zim.gui.widgets import Button
from zim.notebook import Path
from zim.stores import encode_filename
from zim.fs import *
from zim.errors import Error
logger = logging.getLogger('zim.plugins.fileview')
from zim.applications import Application
from zim.gui.applications import OpenWithMenu
ui_toggle_actions = (
# name, stock id, label, accelerator, tooltip, readonly
('toggle_fileview', gtk.STOCK_MISSING_IMAGE, _('Fileview'), '', 'Show Fileview',False, True), # T: menu item
)
#Menubar and toolbar
ui_xml = '''
<ui>
<menubar name='menubar'>
<menu action='view_menu'>
<placeholder name="plugin_items">
<menuitem action="toggle_fileview" />
</placeholder>
</menu>
</menubar>
<toolbar name='toolbar'>
<placeholder name='tools'>
<toolitem action='toggle_fileview'/>
</placeholder>
</toolbar>
</ui>
'''
ICON_SIZE_MIN=16
ICON_SIZE_MAX=256
THUMB_SIZE_MIN=16
THUMB_SIZE_MAX=1024
# TODO: chaneg size
pdftopng_cmd = ('convert','-size', '480x480', '-trim','+repage','-resize','480x480>','-quality','50')
#pdftopng_cmd = ('convert','-trim')
#txttopng_cmd = ('dvipng', '-q', '-bg', 'Transparent', '-T', 'tight', '-o')
#txttopng_cmd = ('convert', '-size', '480x480' 'caption:')
pdftojpg_cmd = ('convert','-size', '480x480', '-trim','+repage','-resize','480x480>','-quality','50')
class FileviewPlugin(PluginClass):
plugin_info = {
'name': _('Fileview'), # T: plugin name
'description': _('''\
This shows the storage directory of the current page as icon view at bottom pane.
ImageMagick dependencies:
html-preview: html2ps
'''), # T: plugin description
'author': 'Thorsten Hackbarth <thorsten.hackbarth@xxxxxx>',
'help': 'Plugins:Fileview',
}
plugin_preferences = (
# key, type, label, default
('icon_size', 'int', _('Icon size [px]'), [ICON_SIZE_MIN,128,ICON_SIZE_MAX]), # T: preferences option
('preview_size', 'int', _('Tooltip preview size [px]'), (THUMB_SIZE_MIN,480,THUMB_SIZE_MAX)), # T: input label
('thumb_quality', 'int', _('Preview jpeg Quality [0..100]'), (0,50,100)), # T: input label
)
@classmethod
def check_dependencies(klass):
return [("ImageMagick",Application(pdftojpg_cmd).tryexec())]
def __init__(self, ui):
PluginClass.__init__(self, ui)
self.bottompane_widget = None
self.scrollpane = None
if self.ui.ui_type == 'gtk':
self.ui.add_toggle_actions(ui_toggle_actions, self)
#self.ui.add_actions(ui_actions, self)
self.ui.add_ui(ui_xml, self)
self.ui.connect_after('open-notebook', self.do_open_notebook)
def do_open_notebook(self, ui, notebook):
self.do_preferences_changed()
#notebook.register_hook('suggest_link', self.suggest_link)
def disconnect(self):
PluginClass.disconnect(self)
def add_to_mainwindow(self):
bottompane = self.ui.mainwindow.pageview.get_parent()
if self.bottompane_widget is None:
self.bottompane_widget = FileviewPluginWidget(self)
self.scrollpane=gtk.ScrolledWindow()
self.scrollpane.set_size_request(-1,160)
self.scrollpane.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.scrollpane.add_with_viewport(self.bottompane_widget)
bottompane.pack_end(self.scrollpane, False)
#bottompane.pack_end(self.bottompane_widget, False)
#bottompane.reorder_child(self.bottompane_widget, 0)
self.handlerID_do_open_notebook=self.ui.connect_after('open-notebook', self.do_open_notebook)
#self.bottompane_widget.show_all()
self.scrollpane.show_all()
def remove_from_mainwindow(self):
if self.bottompane_widget is not None:
#doesnt work:? self.ui.disconnect(self.handlerID_do_open_notebook)
self.scrollpane.hide_all()
def do_preferences_changed(self):
print self.preferences['icon_size']
# bug?
#
# self.preferences['icon_size'] is integer after changeing it
# but must be (min,val,max) for the dialog, which is strange
self.add_to_mainwindow()
def toggle_fileview(self, enable=None):
action = self.actiongroup.get_action('toggle_fileview')
if enable is None or enable != action.get_active():
action.activate()
else:
self.do_toggle_fileview(enable=enable)
def do_toggle_fileview(self, enable=None):
#~ print 'do_toggle_fileview', enable
if enable is None:
action = self.actiongroup.get_action('toggle_fileview')
enable = action.get_active()
if enable:
# print "enabled"
self.add_to_mainwindow()
else:
# print "disabled"
self.remove_from_mainwindow()
self.uistate['active'] = enable
return False # we can be called from idle event
class Fileview(gtk.IconView):
'''Custom fileview widget class. Adds an 'activate' signal for what i dont know yet'''
# define signals we want to use - (closure type, return type and arg types)
__gsignals__ = {
'activate': (gobject.SIGNAL_RUN_LAST, None, ()),
}
# Need to register classes defining gobject signals
gobject.type_register(Fileview)
class FileviewPluginWidget(gtk.HBox):
__gsignals__ = {
'modified-changed': (gobject.SIGNAL_RUN_LAST, None, ()),
}
def __init__(self, plugin):
gtk.HBox.__init__(self)
self.plugin = plugin
self.fileview = Fileview()
self.store = None
self.store=gtk.ListStore(str, gtk.gdk.Pixbuf,str) #gtk.TextBuffer()
#self.fileview.set_buffer(self.textbuffer)
self.fileview.set_model(self.store)
self.fileview.set_text_column(0)
self.fileview.set_pixbuf_column(1)
self.pack_start(self.fileview, True)
self.plugin.ui.connect('open-page', self.on_open_page)
self.fileview.connect_object('button-press-event',FileviewPluginWidget.on_button_press_event, self)
#self.fileview.set_tooltip_column(2) # filename as tooltip
self.fileview.props.has_tooltip = True
# custom tooltip
self.fileview.connect("query-tooltip", self.query_tooltip_icon_view_cb)
def on_button_press_event(self,event):
# print 'on_button_press_event'
if event.button == 3:
popup_menu=gtk.Menu()
x = int(event.x)
y = int(event.y)
time = event.time
#iteminfo = self.fileview.get_item_at_pos(x, y)
pathinfo = self.fileview.get_path_at_pos(x, y)
if pathinfo is not None:
self.fileview.grab_focus()
print self.store.get_value(self.store.get_iter(pathinfo),2)
popup_menu.popup(None, None, None, event.button, time)
self.do_populate_popup(popup_menu,pathinfo)
return True
return False
def thumbnailfile(self,path,filename):
return path+'/.thumb_'+filename+'.jpg'
def do_populate_popup(self, menu,pathinfo):
# print "do_populate_popup"
file= File(self.store.get_value(self.store.get_iter(pathinfo),2)+os.sep+self.store.get_value(self.store.get_iter(pathinfo),0))
# open with & open folder
item = gtk.MenuItem(_('Open Folder'))
# T: menu item to open containing folder of files
menu.prepend(item)
dir = file.dir
if dir.exists():
item.connect('activate', lambda o: self.plugin.ui.open_file(dir))
else:
item.set_sensitive(False)
item = gtk.MenuItem(_('Open With...'))
menu.prepend(item)
submenu = OpenWithMenu(file)
item.set_submenu(submenu)
menu.show_all()
def query_tooltip_icon_view_cb(self, widget, x, y, keyboard_tip, tooltip):
if not widget.get_tooltip_context(x, y, keyboard_tip):
return False
else:
model, path, iter = widget.get_tooltip_context(x, y, keyboard_tip)
value = model.get(iter, 0)
tooltip.set_markup("<b>Filename: </b> %s \n <b>Size: </b> %s \n <b>Date: </b> %s" %(value[0],'(UNKNOWN)','(UNKNOWN)'))
filename=model.get(iter, 0)[0]
filepath=model.get(iter, 2)[0]
filenameabs=filepath+os.sep+filename
#thumbfilenameabs=model.get(iter, 3)[0]
if isinstance(self.plugin.preferences['preview_size'], int):
w=self.plugin.preferences['preview_size']
h=self.plugin.preferences['preview_size']
else:
w=480
h=480
# TODO: textfiles: use text not thumb for preview
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filenameabs,w,h)
tooltip.set_icon(pixbuf)
except:
try:
thumbfilenameabs=self.thumbnailfile(filepath,filename)
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(thumbfilenameabs,w,h)
tooltip.set_icon(pixbuf)
except:
logger.debug('No such thumbnail:%s ',filenameabs)
tooltip.set_icon_from_stock(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DIALOG)
widget.set_tooltip_item(tooltip, path)
return True
def file_to_image(self,filenameabs,thumbfilenameabs):
logger.debug('file_to_image()');
extension=filenameabs.split(".")[-1].upper()
print extension
magickextensions=('SVG','PDF','PS','EPS','DVI','DJVU','RAW','DOT','HTML','HTM','TTF','XCF')
textextensions=('SH','BAT','TXT','C','C++','CPP','H','H++','HPP','PY','PL')
#'AVI','MPG','M2V','M4V','MPEG'
if extension in magickextensions:
# pdf to thumbnail
try:
#touch the thumbnail first
open(thumbfilenameabs, "a")
#filenameabs=path+os.sep+filename
logger.debug(' trying Imagemagick')
filenameabs_p1=filenameabs +'[0]'
pdftopng = Application(pdftopng_cmd)
pdftopng.spawn((filenameabs_p1, thumbfilenameabs))
except:
logger.debug(' Error converting PDF')
elif extension in textextensions:
#convert -size 400x caption:@- caption_manual.gif
try:
#touch the thumbnail first
open(thumbfilenameabs, "a")
textcont='caption:'
file = open(filenameabs)
linecount=0;
# lines: 18 at 128px
# linewidth 35 at 128px
while linecount<18:
line = file.readline()
if not line:
break
linecount+=1
textcont+=line[0:35]
logger.debug(' trying TXT')
txttopng_cmd = ('convert','-family','System','-size', '200x320', '-frame', '4' ,'-quality','40')
txttopng = Application(txttopng_cmd)
txttopng.spawn((textcont,thumbfilenameabs))
except:
logger.warn(' Error converting TXT')
def set_current_folder(self,path):
self.store.clear()
if isinstance(self.plugin.preferences['icon_size'],int):
w = self.plugin.preferences['icon_size']
h = self.plugin.preferences['icon_size']
else:
w=128
h=128
filelist = os.listdir(path)
for filename in filelist:
pixbuf=None
# self.textbuffer.insert_at_cursor(filename+'\n')
filenameabs=path+os.sep+filename
thumbfilenameabs=filenameabs
if os.path.isfile(filenameabs) and filename[0]!='.': #.encode('utf-8')):
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filenameabs,w,h)
#pixbuf = rotate_pixbuf(pixbuf)
except:
logger.debug('No such image: %s', filenameabs)
thumbfilenameabs=self.thumbnailfile(path,filename)
# ToDo: check for existance
# ToDo: check age
try:
print "thumb check:" +thumbfilenameabs
#print os.stat(thumbfilenameabs)[st_mtime]
if (not os.path.isfile(thumbfilenameabs)) or (os.stat(thumbfilenameabs).st_mtime < os.stat(filenameabs).st_mtime):
#print 'thumbnail does not exist or too old'
self.file_to_image(filenameabs,thumbfilenameabs)
#else:
#print 'thumbnail up to date'
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(thumbfilenameabs,w,h)
except:
logger.debug('No such thumbnail:%s %s',thumbfilenameabs, filenameabs)
widget = gtk.HBox() # Need *some* widget here...
pixbuf = widget.render_icon(gtk.STOCK_MISSING_IMAGE,gtk.ICON_SIZE_DIALOG)
if pixbuf:
self.store.append( [filename,pixbuf,path] )
def on_open_page(self, ui, page, path):
try:
#print path
#print encode_filename(page.name)
self.set_current_folder(str(self.plugin.ui.notebook.get_attachments_dir(page)+os.sep))
except AssertionError:
pass
Follow ups
References