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(" ") 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(" ") 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