Vladar

tin 1.0 (Tags In Names)

Feb 15th, 2015
303
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.96 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Version 1.0 (30.01.2012)
  5.  
  6. #
  7. # Copyright (c) 2012 Vladar ([email protected])
  8. #
  9. # Permission is hereby granted, free of charge, to any person
  10. # obtaining a copy of this software and associated documentation
  11. # files (the "Software"), to deal in the Software without
  12. # restriction, including without limitation the rights to use,
  13. # copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. # copies of the Software, and to permit persons to whom the
  15. # Software is furnished to do so, subject to the following
  16. # conditions:
  17. #
  18. # The above copyright notice and this permission notice shall be
  19. # included in all copies or substantial portions of the Software.
  20. #
  21. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  23. # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  25. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  26. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  27. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  28. # OTHER DEALINGS IN THE SOFTWARE.
  29. #
  30.  
  31. #
  32. # REQUIRED MODULES:
  33. # python-tk, python-imaging, python-imaging-tk
  34. #
  35.  
  36. #
  37. #   CONTROLS:
  38. #       All tags list:
  39. #           move to selected tags - Double click, Enter
  40. #           move to excluded tags - Right click, Backspace
  41. #       Selected tags list:
  42. #           move to all tags - Double click, Enter
  43. #           move to excluded tags - Right click, Backspace
  44. #       Excluded tags list:
  45. #           move to all tags - Double click, Enter
  46. #           move to selected tags - Right click, Backspace
  47. #
  48.  
  49. import os, sys, shutil, subprocess
  50. from Tkinter import *
  51. from tkMessageBox import showerror, askyesno
  52. from tkFileDialog import askdirectory, asksaveasfilename
  53. from PIL import Image, ImageTk
  54.  
  55. ## CONFIG ##
  56.  
  57. # preview sizes
  58. PREVIEW_SMALL = 128
  59. PREVIEW_MEDIUM = 256
  60. PREVIEW_BIG = 512
  61.  
  62. # tag separation symbol
  63. SEPARATOR = "."
  64. # default preview size
  65. PREVIEW = PREVIEW_MEDIUM
  66. # preview filter (NEAREST, BILINEAR, BICUBIC, ANTIALIAS)
  67. FILTER = Image.NEAREST
  68.  
  69. ## CONST ##
  70.  
  71. KiB = 1024
  72. MiB = 1048576
  73. GiB = 1073741824
  74.  
  75. ## GLOBAL ##
  76.  
  77. _tags = list()
  78. _filter = list()
  79.  
  80. ## FUNCTIONS ##
  81.  
  82. def _tag_search(arg, dirname, names):
  83.     if dirname != ".":
  84.         # get tags from dirname
  85.         dirtags = os.path.split(dirname)[1].split(SEPARATOR)
  86.         for dirtag in dirtags:
  87.             tag_add(dirtag)
  88.  
  89.     for name in names:
  90.         # get tags from filename
  91.         nametags = name.split(SEPARATOR)[1:-1]
  92.         for nametag in nametags:
  93.             tag_add(nametag)
  94.  
  95.  
  96. def _tag_filter(tags, dirname, names):
  97.     inc = tags[0]
  98.     exc = tags[1]
  99.     # check for tags in dirname
  100.     dirtags = list()
  101.     for subdir in dirname.split(os.sep)[1:]:
  102.         dirtags += os.path.split(subdir)[1].split(SEPARATOR)
  103.     # if excluded tags in dirtags
  104.     for item in exc:
  105.         if item in dirtags:
  106.             return
  107.  
  108.     for i in xrange(len(dirtags)):
  109.         dirtags[i] = unicode(dirtags[i], "utf-8")
  110.  
  111.     match_dir = [item for item in inc if item in dirtags]
  112.     if match_dir:
  113.         # not-in-dirname tags
  114.         inc_left = [item for item in inc if not item in match_dir]
  115.     else:
  116.         inc_left = list(inc)
  117.     inc_left.sort()
  118.     for name in names:
  119.         filename = os.path.join(dirname, name)
  120.         if os.path.isfile(filename):
  121.             # check for tags in filename
  122.             nametags = name.split(SEPARATOR)[1:-1]
  123.  
  124.             for i in xrange(len(nametags)):
  125.                 nametags[i] = unicode(nametags[i], "utf-8")
  126.  
  127.             # if excluded tags in nametags
  128.             exc_in_name = [item for item in exc if item in nametags]
  129.             if exc_in_name:
  130.                 break
  131.  
  132.             match_name = [item for item in inc_left if item in nametags]
  133.             match_name.sort()
  134.             if match_name == inc_left:
  135.                 _filter.append(filename)
  136.  
  137.  
  138. def tag_add(tag):
  139.     if not tag in _tags:
  140.         _tags.append(tag)
  141.  
  142.  
  143. def tag_search():
  144.     del _tags[:]
  145.     os.path.walk(".", _tag_search, None)
  146.     _tags.sort()
  147.  
  148.  
  149. def tag_filter(tags, xtags=[]):
  150.     del _filter[:]
  151.     os.path.walk(".", _tag_filter, (tags, xtags))
  152.     _filter.sort()
  153.  
  154.  
  155. ## GUI ##
  156.  
  157. # global #
  158.  
  159. _list_all_tags = None
  160. _list_sel_tags = None
  161. _list_exc_tags = None
  162. _list_filtered = None
  163. _edit_filename = None
  164. _image_preview = None
  165. _image_info = None
  166. _zoom = 1.0
  167. _index = -1
  168. _name = None
  169.  
  170. # functions #
  171.  
  172. def attach_scroll(listbox, scrollbar, side="left", fill="y"):
  173.     scrollbar.pack(side=side, fill=fill)
  174.     listbox.config(yscrollcommand=scrollbar.set)
  175.     scrollbar.config(command=listbox.yview)
  176.  
  177.  
  178. def sort_listbox(listbox):
  179.     temp_list = list(listbox.get(0, END))
  180.     temp_list.sort()
  181.     listbox.delete(0, END)
  182.     for item in temp_list:
  183.         listbox.insert(END, item)
  184.  
  185.  
  186. def get_all_tags():
  187.     tag_search()
  188.     sel_tags = _list_sel_tags.get(0, END)
  189.     exc_tags = _list_exc_tags.get(0, END)
  190.     _list_all_tags.delete(0, END)
  191.     _list_sel_tags.delete(0, END)
  192.     _list_exc_tags.delete(0, END)
  193.     for tag in _tags:
  194.         if tag not in sel_tags and tag not in exc_tags:
  195.             _list_all_tags.insert(END, tag)
  196.     for tag in sel_tags:
  197.         if tag in _tags:
  198.             _list_sel_tags.insert(END, tag)
  199.     for tag in exc_tags:
  200.         if tag in _tags:
  201.             _list_exc_tags.insert(END, tag)
  202.  
  203.  
  204. def get_filtered():
  205.     # clear
  206.     _list_filtered.delete(0, END)
  207.     _edit_filename.delete(0, END)
  208.     _image_preview.config(image=None)
  209.     _image_preview.image = None
  210.     _image_info.config(text="")
  211.     # get tags
  212.     tags = _list_sel_tags.get(0, END)
  213.     xtags = _list_exc_tags.get(0, END)
  214.     # filter
  215.     tag_filter(tags, xtags)
  216.     for filename in _filter:
  217.         _list_filtered.insert(END, filename)
  218.     global _index, _name
  219.     _index = -1
  220.     _name = None
  221.  
  222.  
  223. def select_dir():
  224.     dir = askdirectory()
  225.     if dir:
  226.         os.chdir(dir)
  227.         _list_sel_tags.delete(0, END)
  228.         _list_exc_tags.delete(0, END)
  229.         get_all_tags()
  230.         get_filtered()
  231.  
  232.  
  233. def move_tag(src, dst):
  234.     selected = src.curselection()
  235.     if selected:
  236.         tag = src.selection_get()
  237.         if tag:
  238.             dst.insert(0, tag)
  239.             src.delete(selected[0])
  240.             sort_listbox(dst)
  241.             get_filtered()
  242.  
  243.  
  244. def on_tag_from_all_to_sel(event):
  245.     move_tag(_list_all_tags, _list_sel_tags)
  246.  
  247.  
  248. def on_tag_from_sel_to_all(event):
  249.     move_tag(_list_sel_tags, _list_all_tags)
  250.  
  251.  
  252. def on_tag_from_all_to_exc(event):
  253.     move_tag(_list_all_tags, _list_exc_tags)
  254.  
  255.  
  256. def on_tag_from_exc_to_all(event):
  257.     move_tag(_list_exc_tags, _list_all_tags)
  258.  
  259.  
  260. def on_tag_from_sel_to_exc(event):
  261.     move_tag(_list_sel_tags, _list_exc_tags)
  262.  
  263.  
  264. def on_tag_from_exc_to_sel(event):
  265.     move_tag(_list_exc_tags, _list_sel_tags)
  266.  
  267.  
  268. def get_size_str(filename):
  269.     """get_size_str(filename) -> str
  270.    Return string with filename size.
  271.    """
  272.     try:
  273.         sz = os.path.getsize(filename)
  274.     except:
  275.         return ""
  276.     if sz < KiB:
  277.         return "%d B" % (sz,)
  278.     elif sz < MiB:
  279.         return "%.3f KiB" % (float(sz)/KiB,)
  280.     elif sz < GiB:
  281.         return "%.3f MiB" % (float(sz)/MiB,)
  282.     else:
  283.         return "%.3f GiB" % (float(sz)/GiB,)
  284.  
  285.  
  286. def on_select_file(event=None):
  287.     global _index, _name
  288.     if _list_filtered.curselection():
  289.         _index = _list_filtered.curselection()[0]
  290.         _name = _list_filtered.selection_get()
  291.  
  292.         # show filename
  293.         _edit_filename.delete(0, END)
  294.         _edit_filename.insert(0, _name)
  295.  
  296.         # show thumbnail
  297.         try:
  298.             image = Image.open(_name)
  299.         except:
  300.             _image_preview.config(image=None)
  301.             _image_preview.image = None
  302.             _image_info.config(text="%s\n%s" % (
  303.                 os.path.basename(_name), get_size_str(_name),))
  304.             return
  305.         w, h = image.size
  306.         image.thumbnail((PREVIEW, PREVIEW), FILTER)
  307.         imagetk = ImageTk.PhotoImage(image)
  308.         _image_preview.config(image=imagetk)
  309.         _image_preview.image = imagetk
  310.  
  311.         # show image info
  312.         info = "%dx%d\n%s" % (w, h, get_size_str(_name))
  313.         _image_info.config(text=info)
  314.  
  315.  
  316. def preview_small():
  317.     global PREVIEW
  318.     PREVIEW = PREVIEW_SMALL
  319.     _image_preview.config(width=PREVIEW, height=PREVIEW)
  320.     on_select_file()
  321.  
  322.  
  323. def preview_medium():
  324.     global PREVIEW
  325.     PREVIEW = PREVIEW_MEDIUM
  326.     _image_preview.config(width=PREVIEW, height=PREVIEW)
  327.     on_select_file()
  328.  
  329.  
  330. def preview_big():
  331.     global PREVIEW
  332.     PREVIEW = PREVIEW_BIG
  333.     _image_preview.config(width=PREVIEW, height=PREVIEW)
  334.     on_select_file()
  335.  
  336.  
  337. def do_del(event=None):
  338.     """Delete selected file"""
  339.     if _index >= 0:
  340.         if askyesno("Delete", "Are you sure?"):
  341.             try:
  342.                 os.remove(_name)
  343.             except:
  344.                 showerror(title="Error",
  345.                           message="Cannot remove the file")
  346.                 return
  347.             idx = int(_index)
  348.             get_all_tags()
  349.             get_filtered()
  350.             _list_filtered.focus_set()
  351.             if idx >= _list_filtered.size(): _list_filtered.see(END)
  352.             else: _list_filtered.see(idx)
  353.             _list_filtered.activate(idx)
  354.  
  355.  
  356. def do_renmov(event=None):
  357.     """Rename/move selected file"""
  358.     if _index >= 0:
  359.         dst = _edit_filename.get()
  360.         if _name != dst:
  361.             try:
  362.                 os.renames(_name, dst)
  363.             except:
  364.                 showerror(title="Error",
  365.                           message="Cannot rename/move the file")
  366.                 return
  367.             get_all_tags()
  368.             get_filtered()
  369.             _list_filtered.focus_set()
  370.             try:
  371.                 idx = _filter.index(dst)
  372.             except:
  373.                 idx = _index
  374.             _list_filtered.see(idx)
  375.             _list_filtered.activate(idx)
  376.  
  377.  
  378. def do_copy():
  379.     """Copy selected file"""
  380.     if _index >= 0:
  381.         dst = asksaveasfilename(initialfile=os.path.basename(_name))
  382.         if dst:
  383.             try:
  384.                 shutil.copy2(_name, dst)
  385.             except:
  386.                 showerror(title="Error",
  387.                           message="Cannot copy the file")
  388.                 return
  389.             idx = int(_index)
  390.             get_all_tags()
  391.             get_filtered()
  392.             _list_filtered.focus_set()
  393.             _list_filtered.see(idx)
  394.             _list_filtered.activate(idx)
  395.             _edit_filename.delete(0, END)
  396.  
  397. # viewer #
  398.  
  399. def on_view(event):
  400.     if _index >= 0:
  401.         if os.name == "posix":
  402.             subprocess.Popen(("xdg-open", _name))
  403.         elif os.name == "nt":
  404.             os.startfile(_name)
  405.         elif os.name == "mac":
  406.             subprocess.Popen(("open", _name))
  407.  
  408.  
  409. # gui setup function #
  410.  
  411. def gui():
  412.     global _list_all_tags,\
  413.            _list_sel_tags,\
  414.            _list_exc_tags,\
  415.            _list_filtered,\
  416.            _edit_filename,\
  417.            _image_preview,\
  418.            _image_info
  419.  
  420.     # root
  421.     root = Tk()
  422.     root.title("TIN")
  423.  
  424.     # root -> frame_menu
  425.     frame_menu = Frame(root)
  426.     frame_menu.pack(side="top", fill="x")
  427.  
  428.     # root -> frame_menu -> btn_dir
  429.     btn_dir = Button(frame_menu,
  430.         text="Select dir", command=select_dir)
  431.     btn_dir.pack(side="left")
  432.  
  433.     # root -> frame_menu -> btn_reload_tags
  434.     btn_reload_tags = Button(frame_menu,
  435.         text="Reload tags", command=get_all_tags)
  436.     btn_reload_tags.pack(side="left")
  437.  
  438.     # root -> frame_menu -> btn_preview_big
  439.     btn_preview_big = Button(frame_menu,
  440.         text="%d" % (PREVIEW_BIG,), command=preview_big)
  441.     btn_preview_big.pack(side="right")
  442.  
  443.     # root -> frame_menu -> btn_preview_medium
  444.     btn_preview_medium = Button(frame_menu,
  445.         text="%d" % (PREVIEW_MEDIUM,), command=preview_medium)
  446.     btn_preview_medium.pack(side="right")
  447.  
  448.     # root -> frame_menu -> btn_preview_small
  449.     btn_preview_small = Button(frame_menu,
  450.         text="%d" % (PREVIEW_SMALL,), command=preview_small)
  451.     btn_preview_small.pack(side="right")
  452.  
  453.     # root -> frame_top
  454.     frame_top = Frame(root)
  455.     frame_top.pack(side="top", fill="both", expand=1)
  456.  
  457.     # root -> frame_top -> pan
  458.     pan = PanedWindow(frame_top, orient="vertical")
  459.     pan.pack(side="left", fill="both", expand=1)
  460.  
  461.     # root -> frame_top -> pan -> frame_tags
  462.     frame_tags = Frame(pan)
  463.     pan.add(frame_tags)
  464.  
  465.     # root -> frame_top -> pan -> frame_tags -> _list_all_tags
  466.     _list_all_tags = Listbox(frame_tags, height=8)
  467.     _list_all_tags.pack(side="left", fill="both", expand=1)
  468.     _list_all_tags.bind("<Double-Button-1>", on_tag_from_all_to_sel)
  469.     _list_all_tags.bind("<Return>", on_tag_from_all_to_sel)
  470.     _list_all_tags.bind("<Button-3>", on_tag_from_all_to_exc)
  471.     _list_all_tags.bind("<BackSpace>", on_tag_from_all_to_exc)
  472.     scroll_list_all_tags = Scrollbar(frame_tags)
  473.     attach_scroll(_list_all_tags, scroll_list_all_tags)
  474.  
  475.     # root -> frame_top -> pan -> frame_tags -> _list_sel_tags
  476.     _list_sel_tags = Listbox(frame_tags, height=8)
  477.     _list_sel_tags.pack(side="left", fill="both", expand=1)
  478.     _list_sel_tags.bind("<Double-Button-1>", on_tag_from_sel_to_all)
  479.     _list_sel_tags.bind("<Return>", on_tag_from_sel_to_all)
  480.     _list_sel_tags.bind("<Button-3>", on_tag_from_sel_to_exc)
  481.     _list_sel_tags.bind("<BackSpace>", on_tag_from_sel_to_exc)
  482.     scroll_list_sel_tags = Scrollbar(frame_tags)
  483.     attach_scroll(_list_sel_tags, scroll_list_sel_tags)
  484.  
  485.  
  486.     # root -> frame_top -> pan -> frame_tags -> _list_exc_tags
  487.     _list_exc_tags = Listbox(frame_tags, height=8)
  488.     _list_exc_tags.pack(side="left", fill="both", expand=1)
  489.     _list_exc_tags.bind("<Double-Button-1>", on_tag_from_exc_to_all)
  490.     _list_exc_tags.bind("<Return>", on_tag_from_exc_to_all)
  491.     _list_exc_tags.bind("<Button-3>", on_tag_from_exc_to_sel)
  492.     _list_exc_tags.bind("<BackSpace>", on_tag_from_exc_to_sel)
  493.     scroll_list_exc_tags = Scrollbar(frame_tags)
  494.     attach_scroll(_list_exc_tags, scroll_list_exc_tags)
  495.  
  496.  
  497.     # root -> frame_top -> pan -> frame_files
  498.     frame_files = Frame(pan)
  499.     pan.add(frame_files)
  500.  
  501.     # root -> frame_top -> pan -> frame_files -> _list_filtered
  502.     _list_filtered = Listbox(frame_files, height=10)
  503.     _list_filtered.pack(side="left", fill="both", expand=1)
  504.     _list_filtered.bind("<ButtonRelease-1>", on_select_file)
  505.     _list_filtered.bind("<KeyRelease>", on_select_file)
  506.     _list_filtered.bind("<Delete>", do_del)
  507.     scroll_list_filtered = Scrollbar(frame_files)
  508.     attach_scroll(_list_filtered, scroll_list_filtered)
  509.  
  510.     # root -> frame_top -> _image_preview
  511.     img = PhotoImage()
  512.     _image_preview = Label(
  513.         frame_top, image=img, cursor="hand2",
  514.         width=PREVIEW, height=PREVIEW)
  515.     _image_preview.pack(side="top")
  516.     _image_preview.bind("<Button-1>", on_view)
  517.  
  518.     # root -> frame_top -> _image_info
  519.     _image_info = Message(frame_top, justify=CENTER, aspect=1000000)
  520.     _image_info.pack(side="top", fill="x")
  521.  
  522.     # root -> frame_actions
  523.     frame_actions = Frame(root)
  524.     frame_actions.pack(side="bottom", fill="x")
  525.  
  526.     # root -> frame_actions -> btn_del
  527.     btn_del = Button(frame_actions,
  528.         text="Delete", command=do_del)
  529.     btn_del.pack(side="left")
  530.  
  531.     # root -> frame_actions -> _edit_filename
  532.     _edit_filename = Entry(frame_actions)
  533.     _edit_filename.pack(side="left", fill="x", expand=1)
  534.     _edit_filename.bind("<Return>", do_renmov)
  535.  
  536.     # root -> frame_actions -> btn_copy
  537.     btn_copy = Button(frame_actions,
  538.         text="Copy", command=do_copy)
  539.     btn_copy.pack(side="right")
  540.  
  541.     # root -> frame_actions -> btn_renmov
  542.     btn_renmov = Button(frame_actions,
  543.         text="Rename/Move", command=do_renmov)
  544.     btn_renmov.pack(side="right")
  545.  
  546.     get_all_tags()
  547.     get_filtered()
  548.  
  549.     root.mainloop()
  550.  
  551.  
  552. def main():
  553.     if len(sys.argv) > 1:
  554.         os.chdir(sys.argv[1])
  555.     gui()
  556.  
  557.  
  558. if __name__ == '__main__':
  559.     main()
Advertisement
Add Comment
Please, Sign In to add comment