Advertisement
Guest User

MangaViewer v0.0.3

a guest
Sep 7th, 2011
589
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.34 KB | None | 0 0
  1. from Tkinter import *
  2. from ttk import *
  3. import Image, ImageTk, tkFileDialog, os
  4.  
  5.  
  6. VERSION = "v0.0.3"
  7.  
  8.  
  9. """
  10. folderDialog Class
  11.  
  12. Dialog that asks the user to select a folder. Returns folder and gets destroyed.
  13.  
  14. """
  15. class folderDialog(Toplevel):
  16.     def __init__(self, parent, callback, dir="./", fileFilter=None):
  17.         Toplevel.__init__(self, parent)
  18.         self.transient(parent)
  19.  
  20.         self.title("Browse Folders")
  21.         self.parent = parent
  22.         self.dir = StringVar()
  23.         self.callback = callback
  24.         self.fileFilter = fileFilter
  25.  
  26.         if os.path.exists(dir) and os.path.isdir(dir):
  27.             self.dir.set(os.path.abspath(dir))
  28.         else:
  29.             self.dir.set(os.path.abspath("./"))
  30.  
  31.         self.body = Frame(self)
  32.         self.body.grid(row=0,column=0,padx=5,pady=5, sticky=(N,S,E,W))
  33.  
  34.         Label(self.body, text="Please select a folder").grid(row=0,column=0, sticky=(N,S,W), pady=3)
  35.         Label(self.body, text="You are in folder:").grid(row=1,column=0, sticky=(N,S,W))
  36.         Entry(self.body, textvariable=self.dir, state="readonly").grid(row=2,column=0,sticky=(N,S,E,W),columnspan=2)
  37.         self.treeview = Treeview(self.body, columns=("dir", "imgs"), show="headings")
  38.         self.treeview.grid(row=3,column=0,sticky=(N,S,E,W),rowspan=3,pady=5,padx=(0,5))
  39.  
  40.         self.treeview.column("imgs", width=30, anchor=E)
  41.         self.treeview.heading("dir", text="Select a Folder:", anchor=W)
  42.         self.treeview.heading("imgs", text="Image Count", anchor=E)
  43.         #self.treeview.heading(0, text="Select Directory")
  44.         #self.listbox = Listbox(self.body, activestyle="dotbox", font=("Menu", 10))
  45.         #self.listbox.grid(row=3,column=0, sticky=(N,S,E,W),rowspan=3,pady=5,padx=(0,5))
  46.  
  47.         ok = Button(self.body, text="Use Folder")
  48.         ok.grid(row=3,column=1,sticky=(N,E,W), pady=5)
  49.  
  50.         cancel = Button(self.body, text="Cancel")
  51.         cancel.grid(row=4,column=1,sticky=(N,E,W), pady=5)        
  52.  
  53.         self.grab_set()
  54.  
  55.         self.protocol("WM_DELETE_WINDOW", self.cancel)
  56.         self.bind("<Escape>", self.cancel)
  57.         cancel.bind("<Button-1>", self.cancel)
  58.         ok.bind("<Button-1>", self.selectFolder)
  59.         self.treeview.bind("<Left>", self.newFolder)
  60.         self.treeview.bind("<Right>", self.newFolder)
  61.         self.treeview.bind("<Return>", self.selectFolder)
  62.         self.treeview.bind("<Up>", self.onUpDown)
  63.         self.treeview.bind("<Down>", self.onUpDown)
  64.         self.treeview.bind("<<TreeviewSelect>>", self.onChange)
  65.  
  66.         self.geometry("%dx%d+%d+%d" % (450, 400,
  67.             parent.winfo_rootx()+int(parent.winfo_width()/2 - 200),
  68.             parent.winfo_rooty()+int(parent.winfo_height()/2 - 150)
  69.         ))
  70.  
  71.         self.updateListing()
  72.         self.treeview.focus_set()
  73.         self.resizable(0,0)
  74.  
  75.         self.columnconfigure(0, weight=1)
  76.         self.rowconfigure(0, weight=1)
  77.         self.body.columnconfigure(0, weight=1)
  78.         self.body.rowconfigure(5, weight=1)
  79.  
  80.         self.wait_window(self)
  81.  
  82.     def newFolder(self, event):
  83.         newDir = self.dir.get()
  84.         if event.keysym == "Left":
  85.             #newDir = os.path.join(newDir, "..")
  86.             self.upFolder()
  87.             return
  88.         else:
  89.             selected = self.getSelected()
  90.            
  91.             if selected == ".":
  92.                 #special super cool stuff here
  93.                 self.selectFolder()
  94.                 return
  95.             elif selected == "..":
  96.                 self.upFolder()
  97.                 return
  98.             else:
  99.                 newDir = os.path.join(newDir, selected)
  100.        
  101.         self.dir.set(os.path.abspath(newDir))
  102.         self.updateListing()
  103.  
  104.     def upFolder(self):
  105.         cur = os.path.split(self.dir.get())
  106.         newDir = cur[0]
  107.         cur = cur[1]
  108.  
  109.         self.dir.set(os.path.abspath(newDir))
  110.         self.updateListing()
  111.  
  112.         children = self.treeview.get_children()
  113.         for child in children:
  114.             if self.treeview.item(child, "text") == cur:
  115.                 self.treeview.selection_set(child)
  116.                 self.treeview.focus(child)
  117.                 #print "please see"
  118.                 self.treeview.see(child)
  119.                 return
  120.  
  121.     def onChange(self, event=None):
  122.         #print event
  123.         sel = self.treeview.focus()
  124.         if sel == '':
  125.             return #not possible, but just in case
  126.        
  127.         if self.treeview.item(sel, "values")[1] == "?":
  128.             #print "Has ?"
  129.             self.imgCount()
  130.    
  131.     def imgCount(self):      
  132.         folder = os.path.join(self.dir.get(), self.getSelected())
  133.         folder = os.path.abspath(folder)
  134.  
  135.         count = 0
  136.  
  137.         dirList = os.listdir(folder)
  138.         for fname in dirList:
  139.             if self.fileFilter == None:
  140.                 count = count + 1
  141.             else:
  142.                 ext = os.path.splitext(fname)[1].lower()[1:]
  143.                 #print ext
  144.                 for fil in self.fileFilter:
  145.                     #print fil
  146.                     if ext == fil:
  147.                         count = count + 1
  148.                         break
  149.  
  150.         #print count
  151.         sel = self.treeview.focus()  
  152.         newV = (self.treeview.item(sel, "values")[0], str(count))
  153.         self.treeview.item(sel, value=newV)
  154.  
  155.  
  156.     def onUpDown(self, event):
  157.         sel = self.treeview.selection()
  158.         if len(sel) == 0:
  159.             return
  160.         active = self.treeview.index(sel[0])
  161.         children = self.treeview.get_children()
  162.         length = len(children)
  163.         toSelect = 0
  164.         if event.keysym == "Up" and active == 0:
  165.             toSelect = length - 1
  166.         elif event.keysym == "Down" and active == length-1:
  167.             toSelect = 0
  168.         else:
  169.             return
  170.        
  171.         toSelect = children[toSelect]
  172.         self.treeview.selection_set(toSelect)
  173.         self.treeview.focus(toSelect)
  174.         self.treeview.see(toSelect)
  175.         return 'break'
  176.  
  177.  
  178.     def updateListing(self, event=None):
  179.         folder = self.dir.get()
  180.         children = self.treeview.get_children()
  181.         for child in children:
  182.             self.treeview.delete(child)
  183.         #self.treeview.set_children("", '')
  184.         dirList = os.listdir(folder)
  185.  
  186.         first = self.treeview.insert("", END, text=".", values=("(.) - Current Folder", "?"))
  187.         self.treeview.selection_set(first)
  188.         self.treeview.focus(first)
  189.  
  190.         self.treeview.insert("", END, text="..", values=("(..)", "?"))
  191.         #self.listbox.insert(END, "(.) - Current Folder")
  192.         #self.listbox.insert(END, "(..)")
  193.         for fname in dirList:
  194.             if os.path.isdir(os.path.join(folder, fname)):
  195.                 #self.listbox.insert(END,fname+"/")
  196.                 self.treeview.insert("", END, values=(fname+"/", "?"), text=fname)
  197.  
  198.     def selectFolder(self, event=None):
  199.         selected = os.path.join(self.dir.get(), self.getSelected())
  200.         selected = os.path.abspath(selected)
  201.         self.callback(selected, self)
  202.         self.cancel()
  203.    
  204.     def getSelected(self):
  205.         selected = self.treeview.selection()
  206.  
  207.         if len(selected) == 0:
  208.             selected = self.treeview.identify_row(0)
  209.         else:
  210.             selected = selected[0]
  211.  
  212.         return self.treeview.item(selected, "text")
  213.  
  214.     def ok(self):
  215.  
  216.         #print "value is", self.e.get()
  217.  
  218.         self.top.destroy()
  219.    
  220.     def cancel(self, event=None):
  221.         self.parent.focus_set()
  222.         self.destroy()
  223.  
  224.  
  225.  
  226.  
  227. """
  228. Img Class
  229.  
  230. Stores path to img and manipulates (resizes).
  231.  
  232. """
  233. class Img:
  234.     def __init__(self, path):
  235.         self.path = path
  236.         self.size = 0,0
  237.         self.oSize = 0,0
  238.         self.img = None
  239.         self.tkpi = None
  240.  
  241.         split = os.path.split(self.path)
  242.         self.folderName = os.path.split(split[0])[1]
  243.         self.fileName = split[1]
  244.        
  245.         self.stats()
  246.         #print "Loaded " + path
  247.         self.img = None
  248.  
  249.     def stats(self):
  250.         self.img = Image.open(self.path)
  251.         self.size = self.img.size
  252.         self.oSize = self.img.size
  253.  
  254.     def load(self):
  255.         self.img = Image.open(self.path)#.convert("RGB") #RGB for better resizing
  256.         #print self.img.mode
  257.         if self.img.mode == "P":
  258.             self.img = self.img.convert("L") #L scales much more nicely than P
  259.  
  260.     def unload(self):
  261.         self.img = None
  262.         self.tkpi = None
  263.         self.size = self.oSize
  264.  
  265.     def fit(self, size):
  266.         #ratio = min(1.0 * size[0] / self.oSize[0], 1.0 * size[1] / self.oSize[1])
  267.         ratio = 1.0 * size[0] / self.oSize[0]
  268.         ratio = min(ratio, 1.0)
  269.         #print ratio
  270.         self.size = (int(self.oSize[0] * ratio), int(self.oSize[1] * ratio))
  271.         #print self.size
  272.  
  273.     def resize(self, size):
  274.         self.fit(size)
  275.         self.load()
  276.         self.img = self.img.resize(self.size, Image.BICUBIC)
  277.         #self.img = self.img.resize(self.size, Image.ANTIALIAS)
  278.         self.tkpi = ImageTk.PhotoImage(self.img)
  279.         return self.tkpi
  280.  
  281.     def quickResize(self, size):
  282.         self.fit(size)
  283.         if self.img == None:
  284.             self.load()
  285.         self.img = self.img.resize(self.size)
  286.         self.tkpi = ImageTk.PhotoImage(self.img)
  287.         return self.tkpi
  288.  
  289.  
  290.  
  291.  
  292.  
  293. """
  294. MangaViewer Class
  295.  
  296. The main class, runs everything.
  297.  
  298. """
  299. class MangaViewer:
  300.     def __init__(self, root):
  301.         self.root = root
  302.         self.setTitle(VERSION)
  303.         #root.state("zoomed")
  304.        
  305.         self.frame = Frame(self.root)#, bg="#333333")#, cursor="none")
  306.         self.canvas = Canvas(self.frame,xscrollincrement=15,yscrollincrement=15,bg="#1f1f1f", highlightthickness=0)
  307.         scrolly = Scrollbar(self.frame, orient=VERTICAL, command=self.canvas.yview)
  308.        
  309.         self.canvas.configure(yscrollcommand=scrolly.set)
  310.  
  311.         #self.img = Image.open("C:\\Users\\Alex\\Media\\manga\\Boku wa Tomodachi ga Sukunai\\16\\02-03.png")
  312.         #self.tkpi = ImageTk.PhotoImage(self.img)
  313.         #self.imgId = self.canvas.create_image(0,0, image=self.tkpi, anchor="nw")
  314.         #self.canvas.configure(scrollregion=self.canvas.bbox(ALL))
  315.         self.files = []
  316.         self.current = 0
  317.  
  318.         self.canvas.bind("<Configure>", self.onConfig)
  319.         self.root.bind("<Up>", self.onScroll)
  320.         self.root.bind("<Down>", self.onScroll)
  321.         self.root.bind("<Left>", self.onNewImg)
  322.         self.root.bind("<Right>", self.onNewImg)
  323.         self.root.bind("<d>", self.getNewDirectory)
  324.         self.root.bind("<f>", self.toggleFull)
  325.         self.root.bind("<Motion>", self.onMouseMove)
  326.         #Windows
  327.         self.root.bind("<MouseWheel>", self.onMouseScroll)
  328.         # Linux
  329.         self.root.bind("<Button-4>", self.onMouseScroll)
  330.         self.root.bind("<Button-5>", self.onMouseScroll)
  331.         self.root.bind("<Escape>", lambda e: self.root.quit())
  332.  
  333.         self.frame.grid(column=0, row=0, sticky=(N,S,E,W))
  334.         self.canvas.grid(column=0,row=0, sticky=(N,S,E,W))
  335.         #scrolly.grid(column=1, row=0, sticky=(N,S))
  336.  
  337.         self.root.columnconfigure(0, weight=1)
  338.         self.root.rowconfigure(0, weight=1)
  339.         self.frame.columnconfigure(0, weight=1)
  340.         self.frame.rowconfigure(0, weight=1)
  341.  
  342.         self.resizeTimeO = None
  343.         self.mouseTimeO = self.root.after(1000, lambda x: x.frame.configure(cursor="none"), self)
  344.         self.lastDir = os.path.abspath("C:\Users\Alex\Media\manga")
  345.         self.imgId = None
  346.         self.fullscreen = False
  347.    
  348.     def toggleFull(self, event=None):
  349.         if self.fullscreen:
  350.             root.overrideredirect(False)
  351.         else:
  352.             root.overrideredirect(True)
  353.        
  354.         self.fullscreen = not self.fullscreen
  355.         self.onConfig(None)
  356.  
  357.     def setTitle(self, *titles):
  358.         st = ""
  359.         for title in titles:
  360.             st = st + " " + str(title)
  361.         self.root.title("MangaViewer  - " + st)
  362.      
  363.     def setTitleToImg(self):
  364.         self.setTitle(self.files[self.current].folderName,"-",
  365.             self.files[self.current].fileName,"(",(self.current+1),"/",len(self.files),")")
  366.  
  367.     def onMouseMove(self, event):
  368.         """hide cursor after some time"""
  369.  
  370.         #print event
  371.         self.frame.configure(cursor="")
  372.         if self.mouseTimeO != None:
  373.             self.root.after_cancel(self.mouseTimeO)
  374.         self.mouseTimeO = self.root.after(1000, lambda x: x.frame.configure(cursor="none"), self)
  375.  
  376.     def onMouseScroll(self, event):
  377.         #mousewheel for windows, mousewheel linux, or down key
  378.         if event.num == 4 or event.delta == 120:
  379.             self.canvas.yview("scroll", -3, "units")
  380.         else:
  381.             self.canvas.yview("scroll", 3, "units")
  382.  
  383.     def onScroll(self, event):
  384.         """called when the up or down arrow key is pressed"""
  385.  
  386.         if event.keysym == "Down":
  387.             self.canvas.yview("scroll", 1, "units")
  388.         else:
  389.             self.canvas.yview("scroll", -1, "units")
  390.  
  391.     def onNewImg(self, event):
  392.         """called when the left or right arrow key is pressed, changes the image"""
  393.  
  394.         change = 1 #right key
  395.         if event.keysym == "Left":
  396.             change = -1
  397.  
  398.         newImg = self.current + change
  399.         if newImg < 0 or newImg >= len(self.files):
  400.             self.getNewDirectory()
  401.             return
  402.  
  403.         #self.img = self.files[newImg];
  404.         #self.tkpi = ImageTk.PhotoImage(self.img)
  405.         #self.canvas.delete(self.imgId)
  406.         #self.imgId = self.canvas.create_image(0,0, image=self.tkpi, anchor="nw")
  407.         #self.canvas.configure(scrollregion=self.canvas.bbox(ALL)) #needed?
  408.         self.files[self.current].unload()
  409.         self.current = newImg
  410.         self.setTitleToImg()
  411.         self.onConfig(None, True)
  412.  
  413.     def getNewDirectory(self, event=None):
  414.         folderDialog(self.root, self.selNewDirectory, self.lastDir, fileFilter=["jpg", "png", "gif", "jpeg"])
  415.        
  416.    
  417.     def selNewDirectory(self, dirname, fd):
  418.         """callback given to folderDialog"""
  419.  
  420.         fd.cancel() #destroy the folderDialog
  421.  
  422.         if self.lastDir == dirname:
  423.             return
  424.  
  425.         self.lastDir = dirname
  426.         #print dirname
  427.         dirList = os.listdir(dirname)
  428.         self.files = []
  429.         self.current = -2
  430.         for fname in dirList:
  431.             ext = os.path.splitext(fname)[1].lower()
  432.             if ext == ".png" or ext == ".jpg" or ext == ".jpeg" or ext == ".gif":
  433.                 self.files.append(Img(os.path.join(dirname, fname)))
  434.         self.current = 0
  435.         if len(self.files) == 0:
  436.              return
  437.         self.setTitleToImg()
  438.         self.onConfig(None, True)
  439.    
  440.     def resize(self, finalResize=False):
  441.         """resizes the image"""
  442.  
  443.         canvasSize = (self.canvas.winfo_width(), self.canvas.winfo_height())
  444.         #print canvasSize
  445.         tkpi = None
  446.         if finalResize:
  447.             tkpi = self.files[self.current].resize(canvasSize)
  448.         else:
  449.             tkpi = self.files[self.current].quickResize(canvasSize)
  450.  
  451.             if self.resizeTimeO != None: #is this the best way to do this?
  452.                 self.root.after_cancel(self.resizeTimeO)
  453.             self.root.after(200, self.onConfig, None, True)
  454.        
  455.         if self.imgId != None:
  456.             self.canvas.delete(self.imgId)
  457.  
  458.         self.imgId = self.canvas.create_image(0,0, image=tkpi, anchor="nw")
  459.         #self.canvas.configure(scrollregion=self.canvas.bbox(ALL))
  460.         bbox = self.canvas.bbox(ALL)
  461.         #nBbox = (bbox[0], bbox[1]-60, bbox[2], bbox[3]+60)
  462.         nBbox = bbox
  463.         self.canvas.configure(scrollregion=nBbox)
  464.         #print self.canvas.bbox(ALL)
  465.  
  466.     def onConfig(self, event, finalResize=False):
  467.         """runs the resize method and centers the image"""
  468.  
  469.         if self.current < 0 or self.current >= len(self.files):
  470.             return
  471.        
  472.         self.canvas.yview("moveto", 0.0)
  473.         self.resize(finalResize)
  474.        
  475.         newX = (self.canvas.winfo_width() - self.files[self.current].size[0])/2
  476.         #newY - 60 TODO change to preference padding
  477.         newY = (self.canvas.winfo_height() - self.files[self.current].size[1])/2# - 60
  478.         newY = max(newY, 0)
  479.  
  480.         self.canvas.coords(self.imgId, newX, newY)
  481.         self.canvas.yview("moveto", 0.0)
  482.         bbox = self.canvas.bbox(ALL)
  483.         nbbox = (0,0, bbox[2], max(bbox[3], self.canvas.winfo_height()))
  484.         self.canvas.configure(scrollregion=nbbox)
  485.  
  486. root = Tk()
  487. MangaViewer(root)
  488. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement