Drophash in wxPython

"So who is the real Mr. Tines? The monster who'd smash a world, or the nice guy I'm hearing now?"
—— The Blabber, Vernor Vinge.

S I T E    M A P

A while back, I wrote a little utility in raw Win32 for getting the MD5 and SHA-1 hashes of a bunch of files dragged on to it. As a little 5-finger exercise for Python, I thought I'd do the same again.

Unfortunately, the standard distro only comes with Tk for its GUI set, and to get a cross-application drag and drop requires installing some extra TK DnD library, so if it needs to install something above and beyond Python 2.5 (that's the version I used, but it doesn't use any new language features), I might as well require something useful — so I require wxPython instead (and I used version 2.8.1.1).

This one is neater than the Win32 version since I can use the nifty HTML window widget and place the results for each new batch of files in a table, with the files and hashes cross-indexed.

Simply cut, paste and save the text of the section below as drophash.py, and save the linked icon file drophash.ico to the same folder, and double-click.

#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F


import wx
import wx.html as html
from md5 import *
from sha import *

def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
    
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()
        
    raw = digest.hexdigest()
    work = []
    i = 0
    while i < len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)

class FileDropTarget(wx.FileDropTarget):
    def __init__(self, window):
        wx.FileDropTarget.__init__(self)
        self.window = window

    hashes = (md5, sha)
    nbsp = list("&nbsp;")

    def OnDropFiles(self, x, y, filenames) :
        self.window.AppendToPage("<table border=1><tr><th>File</th><th>MD5</th><th>SHA-1</th></tr>")
        for file in filenames :
            self.window.AppendToPage("<tr><td>%s</td>" % file)

            for hashOp in self.hashes :
                anHash = getHash(file, hashOp, self.nbsp)
                self.window.AppendToPage("<td><pre>%s</pre></td>" % anHash)

            self.window.AppendToPage("</tr>")
        self.window.AppendToPage("</table>")

def decorate(frame):
    frame.SetTitle("wxPython - Drophash")
    _icon = wx.EmptyIcon()
    _icon.CopyFromBitmap(wx.Bitmap("drophash.ico", wx.BITMAP_TYPE_ANY))
    frame.SetIcon(_icon)
    frame.style = wx.DEFAULT_FRAME_STYLE
    pane = html.HtmlWindow(frame, -1)
    dt = FileDropTarget(pane)
    pane.SetDropTarget(dt)


if __name__ == "__main__":
    theApp = wx.App(False)
    frame  = wx.Frame(None, -1)
    decorate(frame)
    theApp.SetTopWindow(frame)
    frame.Show()
    theApp.MainLoop()

As an alternative, a pure Tk version. Not so fancy; you have to select the files you want (or put them on the command line). Just extract this section as tkhash.py and double click…

#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F

import Tkinter
import ScrolledText
import tkFileDialog
import sys
import tkFont

from md5 import *
from sha import *

hashes = {"MD5" : md5, "SHA" : sha}
nbsp = list(" ")

def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
    
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()
        
    raw = digest.hexdigest()
    work = []
    i = 0
    while i < len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)


def doGetHashes() :
  filenames = tkFileDialog.askopenfilenames()
  updateCanvas(filenames)
  
def updateCanvas(filenames):
  textarea.config(state=Tkinter.NORMAL)
  for file in filenames :
    textarea.insert(Tkinter.END, ("%s\n" % file))

    for hashOp in hashes :
      anHash = getHash(file, hashes[hashOp], nbsp)
      textarea.insert(Tkinter.END, ("%s " % hashOp) )

      textarea.insert(Tkinter.END, ("%s\n" % anHash), 'mono' )

    textarea.insert(Tkinter.END, "-------------------------\n")
  textarea.tag_config('mono', font='courier')
  textarea.config(state=Tkinter.DISABLED)

if __name__ == "__main__":
  root = Tkinter.Tk()
  root.title("tkFileHasher")

  textarea = ScrolledText.ScrolledText()
  textarea.pack()
  textarea.config(state=Tkinter.DISABLED)
  Tkinter.Button(text="Select files to hash...", command=doGetHashes).pack()
  updateCanvas(sys.argv[1:])
  Tkinter.mainloop()

Now for IronPython 1.1 — annoyingly the WebBrowser control doesn't accept normal DnD, nor does it let you paint it to a bitmap to blit on another control, so this one uses a ListView.

# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F

import clr
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")

from System.Windows.Forms import *
from System.Drawing import *

from md5 import *
from sha import *

pane = None
hashes = [md5, sha]
nbsp = list(" ")


def getHash(file, hashOp, delimiter) :

    try :
        reader = open(file, 'rb')
    except :
        return ""
   
    try :
        digest = hashOp()
        while True :
            chunk = reader.read(1024)
            if not chunk :
                break
            digest.update(chunk)
    finally:
        reader.close()

    raw = digest.hexdigest()
    work = []
    i = 0
    while i < len(raw) :
        work.append(raw[i])
        i += 1
        if 0 == (i%8) :
            work = work + delimiter

    return ''.join(work)

def dragOver(sender, evt):
  evt.Effect = DragDropEffects.Link
 
def dragDrop(sender, evt):
  global pane
  data = evt.Data
  data = data.GetData(DataFormats.FileDrop)
  num = pane.Items.Count
 
  for file in data:
    pane.Items.Add(file)
    for hashOp in hashes :
      anHash = getHash(file, hashOp, nbsp)
      pane.Items[num].SubItems.Add(anHash)
    num += 1
      
    


def decorate(frame):
    global pane
    frame.Text = "IronPython - Drophash"
    ico = Image.FromFile("drophash.ico")
    frame.Icon = Icon.FromHandle(ico.GetHicon());
    pane = ListView()
    pane.Dock = DockStyle.Fill;
    frame.Controls.Add(pane)
    pane.AllowDrop = True
    pane.DragOver += dragOver
    pane.DragEnter += dragOver
    pane.DragDrop += dragDrop
    pane.View = View.Details
    pane.Columns.Add("File name", 100, HorizontalAlignment.Left)
    pane.Columns.Add("MD5", 200, HorizontalAlignment.Left)
    pane.Columns.Add("SHA-1", 200, HorizontalAlignment.Left)


if __name__ == "__main__":
    frame = Form()
    decorate(frame)
    Application.Run(frame)

Jython next; but rather than coding the messy .ico to image transformation again, use a PNG icon instead:—

# -*- coding: ISO-8859-1 -*-

#MD5 test suite:
#MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
#MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
#MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
#MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
#MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
#MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
#MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a

#SHA test suite:
#SHA ("abc") = A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
#SHA ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
#SHA (A million repetitions of "a") = 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F


import sys
import os.path
sys.packageManager.makeJavaPackage("javax.swing", "JWindow", None)
sys.packageManager.makeJavaPackage("java.awt", "Window", None)
sys.packageManager.makeJavaPackage("java.awt.dnd", "DropTargetListener", None)
sys.packageManager.makeJavaPackage("java.awt.datatransfer", "DataFlavor", None)
import javax.swing
import java.lang
import java.awt.dnd
import java.awt.datatransfer

from md5 import *
from sha import *

pane = None
hashes = [md5, sha]
nbsp = list("&nbsp;")
target = None

def getHash(file, hashOp, delimiter) :

  try :
    reader = open(file, 'rb')
  except :
    return ""
  
  try :
    digest = hashOp()

  while True :
    chunk = reader.read(1024)
    if not chunk :
      break

      digest.update(chunk)
  finally:
    reader.close()
        
  raw = digest.hexdigest()
  work = []
  i = 0
  while i < len(raw) :
    work.append(raw[i])
    i += 1
    if 0 == (i%8) :
      work = work + delimiter

  return ''.join(work)
  
def OnDropFiles(filenames) :
  global pane, nbsp, hashes
  pane.text += "<table border=1><tr><th>File</th><th>MD5</th><th>SHA-1</th></tr>"
  for file in filenames :
    pane.text += "<tr><td>%s</td>" % file

    for hashOp in hashes :
      anHash = getHash(str(file), hashOp, nbsp)
      pane.text += "<td><pre>%s</pre></td>" % anHash

  pane.text +="</tr>"
  pane.text +="</table>"

  

class dropHandler(java.awt.dnd.DropTargetAdapter):
  def __init__(self):
    java.awt.dnd.DropTargetAdapter.__init__(self)

  def drop(self, evt):
    if not evt.transferable.isDataFlavorSupported(java.awt.datatransfer.DataFlavor.javaFileListFlavor):
      return
    evt.acceptDrop(-1)
    names = evt.transferable.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor)
    OnDropFiles(names)

def decorate(frame):
  global pane
  frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE)
  frame.title = "Jython - Drophash"
  frame.layout = java.awt.BorderLayout()

  pane = javax.swing.JLabel()
  pane.border = javax.swing.BorderFactory.createEmptyBorder(5,5,5,5)
  frame.contentPane.add(javax.swing.JScrollPane(pane), java.awt.BorderLayout.CENTER)
  pane.text = "<html>"
  icon = java.awt.Toolkit.getDefaultToolkit().createImage("drophash.png")
  frame.setIconImage(icon)
  target = java.awt.dnd.DropTarget(pane, dropHandler())
  
  
  width = 800
  height = 600
  frame.setSize(width, height);
  screenDim = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
  frame.setLocation(
        (screenDim.width - width) / 2,
        (screenDim.height - height) / 2
        )
  



if __name__ == "__main__":
  frame = javax.swing.JFrame()
  decorate(frame)
  frame.visible = True

Made with Cascading Style Sheets

This document maintained by domain webmaster - tines at ravna and tines dot com.

Material Copyright © 2000-2007 Mr. Tines

S I T E   M A P
Hosted by www.Geocities.ws

1