From 81c04612339bc05ccb176e199fc189f019bd2d3e Mon Sep 17 00:00:00 2001 From: eager Date: Fri, 16 Oct 2020 11:28:04 -0700 Subject: [PATCH 01/10] Refactor ThumbnailCtrl, create stand-alone ScrolledThumbnail widget ThumbnailCtrl is more of a image browser demo application than a widget, in that it reads files from a directory, selects which files to display, deletes files, displays the source directory path in a text ctrl, etc. This makes it unlikely that it could be used in any other application, for example, to provide thumbnails of files with different file types than the ones hard-coded in the class. ThumbnailCtrl delegates most of its operations to ScrolledThumbnail which actually implements a scrolled window of thumbnails, a Thumb class, which contains information about a thumbnail, and an ImageHandler class, which manipulates images. There was poor isolation of functionality between these classes, violating object-oriented design, with one class making changes to the internal data of another class. Additionally, there was substantial non-functional code, as well as code which did not function correctly. This refactoring maintains the functionality and interfaces of ThumbnailCtrl, except for those which were unused. Existing uses of the thumbnailctrl package should work without modification. A new package, scrolledthumbnail, contains the functionality for a scrolled window containing thumbnails, an extendable Thumb class, and image manipulation classes. The scrolledthumbnail package can be used in other applications, independent of the ThumbnailCtrl class, and without the functional restrictions of that application. Detailed changes: ThumbnailCtrl.py (demo program): - Always import from wx.lib.agw - Optional code to use PIL instead of native image handling - Add setting for thumbnail width and height - Increase size of demo window thumbnailctrl.py: - Move Thumb, ScrolledThumbnail, ImageHandler to scrolledthumbnail.py - Remove EVT_THUMBNAILS_CAPTION_CHANGED (unused) - Add EVT_THUMBNAILS_CHAR to respond to keystrokes - Remove image processing code - Add scrolling dialog for delete files - Move directory processing from ScrolledThumbnail - Move file delete processing from ScrolledThumbnail - List all files to be deleted in scrolling dialog - Remove unused or unimplemented methods and options scrolledthumbnail.py: - Move Thumb, ScrolledThumbnail, ImageHander classes from thumbnailctrl.py - Add documentation for ScrolledThumbnail widget - Add example program which does not use ThumbnailCtrl - New EVT_THUMBNAILS_CHAR event for key press - Remove unused options and dead code - Add Rotate() to PILImageHandler and NativeImageHandler - Throw event EVT_THUMBNAILS_CHAR for keystroke on thumbnail - Fix failure to rotate images correctly - Redisplay window when thumb size changed - Simplify logic - Remove popup dialog when rotating images --- demo/agw/ThumbnailCtrl.py | 51 +- wx/lib/agw/scrolledthumbnail.py | 2119 +++++++++++++++++++++++++++++ wx/lib/agw/thumbnailctrl.py | 2269 ++----------------------------- 3 files changed, 2259 insertions(+), 2180 deletions(-) create mode 100644 wx/lib/agw/scrolledthumbnail.py diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index 7ab5f8f3..f688e670 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -12,13 +12,13 @@ except: sys.path.append(os.path.split(dirName)[0]) -try: - from agw import thumbnailctrl as TC -except ImportError: # if it's not there locally, try the wxPython lib. - import wx.lib.agw.thumbnailctrl as TC +import wx.lib.agw.thumbnailctrl as TC import images +from wx.lib.agw.scrolledthumbnail import EVT_THUMBNAILS_SEL_CHANGED, EVT_THUMBNAILS_POINTED, \ +EVT_THUMBNAILS_DCLICK + class ThumbnailCtrlDemo(wx.Frame): @@ -32,7 +32,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.statusbar = self.CreateStatusBar(2) self.statusbar.SetStatusWidths([-2, -1]) # statusbar fields - statusbar_fields = [("ThumbnailCtrl Demo, Andrea Gavana @ 10 Dec 2005"), + statusbar_fields = [("ThumbnailCtrl Demo, Michael Eager @ 15 Oct 2020"), ("Welcome To wxPython!")] for i in range(len(statusbar_fields)): @@ -46,6 +46,7 @@ class ThumbnailCtrlDemo(wx.Frame): sizer = wx.BoxSizer(wx.HORIZONTAL) scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.NativeImageHandler) + #scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.PILImageHandler) scroll.ShowFileNames() if os.path.isdir("../bitmaps"): @@ -74,6 +75,9 @@ class ThumbnailCtrlDemo(wx.Frame): self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") + self.textthumbwidth = wx.TextCtrl(self.panel, -1, "96") + self.textthumbheight = wx.TextCtrl(self.panel, -1, "80") + self.thumbsizebutton = wx.Button(self.panel, -1, "Set thumbnail size (WxH)") self.fontbutton = wx.Button(self.panel, -1, "Set caption font") self.colourbutton = wx.Button(self.panel, -1, "Set selection colour") @@ -100,18 +104,19 @@ class ThumbnailCtrlDemo(wx.Frame): self.Bind(wx.EVT_CHECKBOX, self.OnShowComboBox, self.showcombo) self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) + self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) self.Bind(wx.EVT_BUTTON, self.OnSetFont, self.fontbutton) self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) - self.TC.Bind(TC.EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) - self.TC.Bind(TC.EVT_THUMBNAILS_POINTED, self.OnPointed) - self.TC.Bind(TC.EVT_THUMBNAILS_DCLICK, self.OnDClick) + self.TC.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) + self.TC.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) + self.TC.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - splitter.SplitVertically(scroll, self.panel, 180) + splitter.SplitVertically(scroll, self.panel, 400) splitter.SetMinimumPaneSize(140) - self.SetMinSize((700, 590)) + self.SetMinSize((1000, 1000)) self.CenterOnScreen() @@ -131,6 +136,7 @@ class ThumbnailCtrlDemo(wx.Frame): splitsizer = wx.BoxSizer(wx.VERTICAL) optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) zoomsizer = wx.BoxSizer(wx.HORIZONTAL) + thumbsizesizer = wx.BoxSizer(wx.HORIZONTAL) customsizer = wx.StaticBoxSizer(self.customsizer_staticbox, wx.VERTICAL) thumbsizer = wx.StaticBoxSizer(self.thumbsizer_staticbox, wx.VERTICAL) radiosizer = wx.BoxSizer(wx.VERTICAL) @@ -153,7 +159,11 @@ class ThumbnailCtrlDemo(wx.Frame): splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbheight, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.thumbsizebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) optionsizer.Add(zoomsizer, 1, wx.EXPAND, 0) + optionsizer.Add(thumbsizesizer, 1, wx.EXPAND, 0) optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) @@ -193,9 +203,10 @@ class ThumbnailCtrlDemo(wx.Frame): msg = "This Is The About Dialog Of The ThumbnailCtrl Demo.\n\n" + \ "Author: Andrea Gavana @ 10 Dec 2005\n\n" + \ + "Modified: Michael Eager @ 15 Oct 2020\n\n" + \ "Please Report Any Bug/Requests Of Improvements\n" + \ "To Me At The Following Addresses:\n\n" + \ - "andrea.gavana@agip.it\n" + "andrea_gavana@tin.it\n\n" + \ + "eager@eagercon.com\n\n" + \ "Welcome To wxPython " + wx.VERSION_STRING + "!!" dlg = wx.MessageDialog(self, msg, "ThumbnailCtrl Demo", @@ -362,6 +373,24 @@ class ThumbnailCtrlDemo(wx.Frame): event.Skip() + def OnSetThumbSize(self, event): + try: + width = int(self.textthumbwidth.GetValue().strip()) + height = int(self.textthumbheight.GetValue().strip()) + except: + errstr = "Error: thumb size must be integers (min 50x50)." + dlg = wx.MessageDialog(self, errstr, "ThumbnailCtrlDemo Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return + + width = max(width, 50) + height = max(height, 50) + self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) + self.TC.SetThumbSize (width, height) + + event.Skip() def OnSelChanged(self, event): diff --git a/wx/lib/agw/scrolledthumbnail.py b/wx/lib/agw/scrolledthumbnail.py new file mode 100644 index 00000000..340412d9 --- /dev/null +++ b/wx/lib/agw/scrolledthumbnail.py @@ -0,0 +1,2119 @@ +# --------------------------------------------------------------------------- # +# SCROLLEDTHUMBNAIL Control wxPython IMPLEMENTATION +# Python Code By: +# +# Michael Eager (eager@eagercon.com), 26 Sep 2020 +# +# Based on thumnailctrl.py by +# Andrea Gavana And Peter Damoc, @ 12 Dec 2005, revised 27 Dec 2012 +# andrea.gavana@gmail.com +# andrea.gavana@maerskoil.com +# +# Tags: phoenix-port, documented, unittest, py3-port +# +# End Of Comments +# --------------------------------------------------------------------------- # + +""" +:class:`ScrolledThumbnail` is a widget that can be used to display a series of +thumbnails for files in a scrollable window. + +Description +=========== + +:class:`ScrolledThumbnail` is a widget derived from :class:`wx.ScrolledWindow` +which will display the thumbnail image for a file in a scrollable, resizable +window. It supports selecting one or more thumbnails, rotating the thumbnails, +displaying information for each thumbnail, and popups for both the window and +for individual thumbnails. + +The class uses two support classes: :class:`Thumb` and :class:`ImageHandler`. + +:class:`Thumb` contains all of the information for a particular thumbnail, +including filename, bitmaped thumbnail image, caption, and other data. This +class also has methods to perform thumbnail operations such as rotation, +highlighting, setting a file name. + +:class:`ImageHandler` provides file/image handling functions, including loading +a file and creating an image from it, rotating an image, or highlighting an image. + +The implementations of these two classes included in this file support generating +thumbnails from supported image files, such as JPEG, GIF, PNG, etc., using either +WxPythons native support or the PIL image library. Additional file types can be +supported by extending these classes, for example, to provide a thumbnail image +for an MP4 file, perhaps the cover photo, an MPEG by providing the image of a +title frame, or a PDF by providing an image of the cover page. The images for +these files may be generated on the fly by a suitably extended :class:`ImageHandler`, +or may be provided with the instance of :class:`Thumb`. The list of `Thumb` +instances passed to `ScrolledThumbnail` may contain different derived classes, +as long as each contains the required funtions. + +NB: Use of :class:`ScrolledThumbnail` has not been tested with extended classes. + +:class:`ScrolledThumbnail`, :class:`Thumb`, and :class:`ImageHandler`, implemented +here are derived from the similarly named classes included in :class:`agw.ThumbnailCtrl`, +written by Andrea Gavana. That implementation was tightly integrated as a image +file browser application. The current implementation removes dependencies between +the several classes and narrows the scope of `ScrolledThumbnail` to the placement +and management of thumbnails within a window. + +An updated :class:`ThumbnailCtrl` which uses this implementation of +:class:`ScrolledThumbnail`, :class:`Thumb`, and :class:`ImageHandler` provides +all of the previous functionality, which described as a widget that can be used +to display a series of images in a "thumbnail" format; it mimics, for example, +the windows explorer behavior when you select the "view thumbnails" option. +Basically, by specifying a folder that contains some image files, the files +in the folder are displayed as miniature versions of the actual images in +a :class:`ScrolledWindow`. + +The code in the previous implementation is partly based on `wxVillaLib`, a +wxWidgets implementation of the :class:`ThumbnailCtrl` control. Andrea Gavana +notes that :class:`ThumbnailCtrl` wouldn't have been so fast and complete +without the suggestions and hints from Peter Damoc. + +Usage: +===== + +Usage example:: + + import os + import wx + from scrolledthumbnail import ScrolledThumbnail, Thumb, NativeImageHandler + + class MyFrame(wx.Frame): + + def __init__(self, parent): + wx.Frame.__init__(self, parent, -1, "ScrolledThumb Demo", size=(400,300)) + + self.scroll = ScrolledThumbnail(self, -1, size=(400,300)) + + def ShowDir(self, dir): + dir = os.getcwd() + files = os.listdir(dir) + thumbs = [] + for f in files: + if os.path.splitext(f)[1] in [".jpg", ".gif", ".png"]: + thumbs.append(Thumb(dir, f, caption=f, imagehandler=NativeImageHandler)) + self.scroll.ShowThumbs(thumbs) + + + app = wx.App(False) + frame = MyFrame(None) + frame.ShowDir(os.getcwd()) + frame.Show(True) + + app.MainLoop() + + + +Methods and Settings +==================== + +With :class:`ScrolledThumbnail` you can: + +- Create different thumbnail outlines (none, images only, full, etc...); +- Highlight thumbnails on mouse hovering; +- Show/hide file names below thumbnails; +- Change thumbnail caption font; +- Zoom in/out thumbnails (done via ``Ctrl`` key + mouse wheel or with ``+`` and ``-`` chars, + with zoom factor value customizable); +- Rotate thumbnails with these specifications: + + a) ``d`` key rotates 90 degrees clockwise; + b) ``s`` key rotates 90 degrees counter-clockwise; + c) ``a`` key rotates 180 degrees. + +- Drag and drop thumbnails from :class:`ScrolledThumbnail` to whatever application you want; +- Use local (when at least one thumbnail is selected) or global (no need for + thumbnail selection) popup menus; +- possibility to show tooltips on thumbnails, which display file information + (like file name, size, last modification date and thumbnail size). + +:note: Using highlight thumbnails on mouse hovering may be slow on slower + computers. + + +Window Styles +============= + +`No particular window styles are available for this class.` + + +Events Processing +================= + +This class processes the following events: + +This class processes the following events: + +================================== ================================================== +Event Name Description +================================== ================================================== +``EVT_THUMBNAILS_CAPTION_CHANGED`` The thumbnail caption has been changed. Not used at present. +``EVT_THUMBNAILS_DCLICK`` The user has double-clicked on a thumbnail. +``EVT_THUMBNAILS_POINTED`` The mouse cursor is hovering over a thumbnail. +``EVT_THUMBNAILS_SEL_CHANGED`` The user has changed the selected thumbnail. +``EVT_THUMBNAILS_THUMB_CHANGED`` The thumbnail of an image has changed. Used internally. +``EVT_THUMBNAILS_CHAR`` A character has been typed +================================== ================================================== + + +License And Version +=================== + +:class:`ScrolledThumbnail` is distributed under the wxPython license. + +Latest revision: Michael Eager @ 2 Oct 2020 + +Version 1.0 + +""" + +#---------------------------------------------------------------------- +# Beginning Of ThumbnailCtrl wxPython Code +#---------------------------------------------------------------------- + +import os +import wx +import six +import zlib +from math import radians + +from wx.lib.embeddedimage import PyEmbeddedImage + +if six.PY3: + import _thread as thread +else: + import thread + +#---------------------------------------------------------------------- +# Get Default Icon/Data +#---------------------------------------------------------------------- + +#---------------------------------------------------------------------- +file_broken = PyEmbeddedImage( + b"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAAK/INwWK6QAADU9J" + b"REFUeJzNWn1QE2cefja7yRI+lAKiAgKCVIqegUYMFMsM4kfvzk7nmFrnvHGcOT3bq3P/OJ1p" + b"b+zYf+r0HGxt6VVFqpYqWq9TW23RVnp6R7FXiq0UPAGhyFeQSCQJISHZ7Mf9gZuGkE02IeA9" + b"M5k3u/vuu+/z/H7v7/297y4BPzh//vxfSJIsIgjC5a/eTIPn+UnHgiDIqisIAsUwTNfmzZtf" + b"A8D7qk/5e3B0dPS6wsLCp/09MFTIbdO7ntR9vs5HRESgvr6+E0AlAD2AKZX8CsDzvEKtVsvq" + b"aKjw7LggCO7jcJQqlQoMw6gBpAIYRAgCECGxCgDPTsohEopI4n+e5xXww1MRHkry4d1Zf9fk" + b"uv90MOsCeMKfGN51pO6ZrmizKsBsWt976EjhoQ+BYMQItpSDWRNAjptPZ4xLCfl/PQTCaX2p" + b"+wNhVgSYbet7tumdRXpj1ofAbFhfqn1fmHEBHpb1AYAgAudxMy4AQRB+LReuSB/MGsETMyqA" + b"2WyG1WqVZQl/CDUZeuge0NvbKwwODk7bC7zryYFI/qF6QGNj42BnZ6dZ6rocQtNNhQNhxgQw" + b"Go24fPnyDy0tLTcFQfDpBZ7/w2198RcIMyZAW1vb+KVLlxoGBwfr7927B4Vi8qO8CfnqbCjW" + b"D4Y8MEMCCIKA77777me73d7a1tb25dDQkNXzmud/giDgdDrBsqzkuA1l7CuVSgCA0+n0y3FG" + b"BBgYGMCVK1eub926dRFN04aBgYH/el73JKRUKjE4OIi+vj5QFDWtaVG0fGRkJHiex7lz5xyn" + b"T59uBMBI9TWsAoidaW1tNTc1Nd1cv379zuzs7F91dHT8x+l0TooDnuju7h779ttvjdOdLlUq" + b"FaKjo9HV1YX9+/d379q16+QXX3xxFsAAMHU7DAgggNw1tQiCIOByuXDt2rWbK1asUOfk5KxM" + b"SUlZW19f39LX18eTJDmpbbG8cePGndra2mtDQ0NQKpUhJUPR0dHgeR6ffvqpbdeuXV+9/vrr" + b"R4eGhs4A+ArA3ZAECAXd3d24dOnSD2VlZavy8vKQmppaUFdXN2gwGO561yVJEmazGR0dHV3n" + b"z5//+NatW0MU5XebcgooikJMTAy6urqwb9++zp07d1ZfvXq1GsAn8+bNa6qsrEyGxJY4EGBT" + b"lCAIhVR0ZhgGTqcTTqcTDocDDocDgiDgm2++0Y+MjNxbuXLl7wmCQFpaWtbcuXMje3p6fly9" + b"enWyeL8gCCBJEj09PUJLS0s7z/PXWlpavigoKNhBURRYlnU/S6qMjIwEwzD47LPPbIcOHWq4" + b"cuXKPwF8D+DHF154QV1cXFxpsVjiAGwGYIUPL/ArgNFoNNbV1dmtVqvdarW6LBaLY2xszOFw" + b"OBiLxWKz2WxOs9lsHx0ddZhMJpvZbB7v7u7+effu3elZWVmJAJCUlBS1bt26nNbW1kaj0fj0" + b"I4884iYHAHfu3Lnf2dnZDWC4oaHhH08++eRWrVZLe9bxJk9RFNRqNTo6OvDhhx92Hj16tG5k" + b"ZKQBwHUAneXl5cVarfaQVqtddurUqesAUgC0BysAsWfPngOjo6ONY2NjUQzDOACMA7ADcGAi" + b"sjoBuB6U4jFTWlp6Kj4+HjzPIzExEbm5uSuPHz9+csuWLfaEhIRIl8sFgiDA8zxu3rz585Yt" + b"W3SpqanX9u7d+93GjRuv5eXlrRGvT+oQQUCtVsPhcODcuXO2w4cPi1ZvAnADgP3EiRN7NBrN" + b"ntzcXDXHcXA6nREAJF9u+BNA6OnpuQ1gGAD5gCjrUXIPFOUflAIA7siRI8Xp6ekaYOI1lVKp" + b"RGZmpqatre2QXq/v1mg0y4EJKxoMBrS1tQ2UlJQ8abPZjAD23Lhx46Pi4uI1aWlp7mEl1qdp" + b"Grdu3UJNTU1nVVVV3cjIyDUAPwJof+WVVzJ0Ol1NQUHBbxMTEyHOOoEQKOKMP/jJxoIFC/6Q" + b"mZlJidYTBAHp6emp2dnZCV1dXY0MwywnCAIkSUKv1zu7u7uNu3fvTh8aGtoE4Pj7779/ec2a" + b"NZ0ZGRlZwC9Wt9vt+Pzzz22VlZUNV69eFa3+EwDTkSNHynJyct5ZtWpVikKhgMPhgEKhkJx2" + b"gxFgSv1t27ZRUVFRFMuyZGZmZmJcXNwcpVIZT9N0tMvlWpiSklKmVConBbGkpCRq3bp1Kxsb" + b"G69v3Lhxe3p6OgRBQHt7u2Hx4sVRycnJ9Ny5czO3bdv2VHV19XvNzc2frF69+pXY2FgoFAop" + b"q3ds2rQp6plnnnkzLy9v99KlS8EwDDiOC2r5LSnAiRMnIubNm/dHkiSzaZqOIUkygabpGIqi" + b"4iiKmkuSZDRJkiqVSkWRJKmkaZqMiIhAZGQkOI5zk+c4DnFxccjOztZWVVV9+eKLLw5nZGTM" + b"YxgGzc3Ndx577LGF8fHxiI2NhU6n+111dfWZixcvnispKfmzTqebe+HCBW+rtwK4/8Ybb+Rp" + b"NJojBQUFq2JiYuB0OgH8kgqLpdiXoAVobW2NfvbZZ/cWFhbOly3ngwf6yvczMzNzWJZV9Pb2" + b"/lRUVLR2cHAQt2/fvrt9+3YtSZJQKBRYtmxZYXFx8ar6+vqrTU1NF7/++mtdZWXll0ajsQET" + b"Qa4TE3HmBY1Gsz8/P3+Oy+Vyj3dP8nK9QFKA/v5+Cn7GfzBZmiAISE5OTiwqKsq4fft2k91u" + b"X9vf3283GAyWtLS0ZNFTsrOzI0pKSsrq6+v//c477/xNr9evwMRr7ZsAhl966aUFOp3uzfz8" + b"/C2LFi3C+Pj4JMLeAsjJYiUFYFk2oIRyReA4DikpKSgqKlpZW1t7saysjOvo6NAvXbo0NjEx" + b"MZLneXAchzlz5kCj0TwVGxu7RK/Xt2Mihx8DwBw4cGBDbm5uRWFh4aNKpRJ2u30S8VCsD8hY" + b"CwRzXqouz/OIiopCVlaWtrm5+X57e/tAS0uLPicnJzEhIcE9TnmeR05OTvJzzz33a0xMtyNa" + b"rVY4fvz4a6WlpRdKSkoeFZfPYpSXIi93SyzYWWASMTmlZ/2MjIys+Pj4mI6Ojoa+vj5h/fr1" + b"xaKrikIlJyfj8ccfLwNw7NVXX43TarV/LyoqWhsXF4fx8XF3TPFF2Pv/tPIAu93ul7g/+BJD" + b"EAQsXLgwqrS0NLexsfE8RVHLFi1atNn7PpVKhdzc3OUvv/zyvg0bNvwmPz8/WeyPt8sHEkLO" + b"anZaQyCYDUmO47BgwQIsX758VW1t7bc6nU5ISkpSeuYLHMeBYRhkZWWpd+zY8afCwsJklmXB" + b"MMwUlw9EXjwOhIDL4dHRUQwMDMBms4Hn+YCuJSUKz/NQqVRYsmTJcgDzlixZsnzOnDlu1xfj" + b"AMdxoGkaqampsNvtEATBvZ8olzxBEO57whIDxsbGMD4+DrVajaioKERGRoKiKMmdXn9IT09P" + b"Li4ufiIxMVEHACzLThGW47gp54IhHwz8CiCSEt3P4XDA6XTCYrGApmnQNA2VSuUWwzvyescA" + b"l8uF+fPnK59//vm/zp8/f6FoebF98VlSBOWS99WXkATwfrhnw+JmiEKhgEKhAEmSEDM6KTEI" + b"gkBcXBwRExOTkpCQAJfLNSPkg4EsD/Algncs4Hl+ktv6+onjOS0tDTRNT1q4hJN8MCKE5AFS" + b"nQx0DQAYhkFqaqpbrJkmH5YPJMJFXqFQTBmTM0E+LB5gt9slXTpU8t4E5JKXSnsDkfV+HReU" + b"AJ6YLnlf1pNLXhAEmM1msCw7KSZ598/7PEEQ4DgOLMv6VSHQt8JhIS/HG/y5vV6vh9FohFqt" + b"ducN3r8pxCgKJpMJvb29o/hlzzI4AUSEk7yUtfyJwTAMMjMzsXjxYr/zuuf7wbt376K8vLz/" + b"7NmzlzCxpA5NgHCTl1vHFzkAPjNE774aDAaUl5f3V1RU1HAc9y9MbKr4RMA8QIr4TJP3LEU3" + b"5zjOnTtItWUwGLB//36R/CVMbKXZQhbAV2dnk7zYD3G1KI537/oURbndvqKi4hTHcV9iYvd4" + b"zB/HkFPh6ZL3JurvHIBJXuB9XfzG4MCBA/3vvvtujVzysgTwR2I65KVmAl/3iUtmlmUnDQGR" + b"/P3793H48GH9A/Ki2wckH1AAzxjgS4yZJO/dD899A7EORVEYGRlBVVWV8b333vs4GMvLEsAT" + b"oQ4Ff+Q92/YniGcMEAUQ5/ljx44Nv/XWWx9ZrdaLAJqDIR9QAO/9gHCTlxsERRFEAZRKJUwm" + b"E6qrq4cPHjx4xmq11mLi1bglGPIBBfBF8mGQFyHmACaTCSdPnhx+++23z4yOjtZi4pWZKVjy" + b"sgTwFiIU8v7GuVzyIsxmM06fPj188ODBjx5Y/nsAkl+jBoLsPECEryDl7ziQMHLJkyQJi8WC" + b"mpqa4YqKCtHtmzAN8oCM9wJKpRIRERGyyAQi6CvwyeokRcFsNqOurm64oqLijMVimZbbT2pb" + b"6gJN04TNZlNZLBYwDOOeEgNtMsqpF+y1sbEx1NbWDn/wwQdhJQ8AkmZYsWJFVEZGxtZ79+49" + b"wbIsDa/VlBQJqS0of6QD3UMQBHp7e++YTKYrCIPbe8KfHyoALACwGIAqXA8MEQImPnO7A2Ak" + b"nA3/D+/OyD/Ur3BPAAAAAElFTkSuQmCC") + + +def getDataSH(): + """ Return the first part of the shadow dropped behind thumbnails. """ + + return zlib.decompress( +b'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2_A\x98\x83\rHvl\ +\xdc\x9c\n\xa4X\x8a\x9d float(height)/imgheight: + scale = float(height)/imgheight + + newW, newH = int(imgwidth*scale), int(imgheight*scale) + if newW < 1: + newW = 1 + if newH < 1: + newH = 1 + + img = img.Scale(newW, newH) + + return img + + + def GetBitmap(self, width, height): + """ + Returns the bitmap of the thumbnail + + :param `width`: the associated bitmap width; + :param `height`: the associated bitmap height. + """ + + if self._bitmap: + if self._bitmap.GetWidth() == width and self._bitmap.GetHeight() == height: + return self._bitmap + + img = self.GetThumbnail(width, height) + bmp = img.ConvertToBitmap() + + return bmp + + + def GetFullFileName(self): + """ Returns the full filename of the thumbnail. """ + + return os.path.join(self._dir, self._filename) + + + def GetCaption(self, line): + """ + Returns the caption associated to a thumbnail. + + :param `line`: the caption line we wish to retrieve (useful for multilines + caption strings). + """ + + if line + 1 >= len(self._captionbreaks): + return "" + + strs = self._caption + + return strs + + + def GetFileSize(self): + """ Returns the file size in bytes associated to a thumbnail. """ + + return self._filesize + + + def GetDisplayFileSize(self): + """ Return printable file size (with bytes, Kb, Mb suffix). """ + + size = self.GetFileSize() + if size < 1000: + size = str(size) + " bytes" + elif size < 1000000: + size = str(int(round(size/1000.0))) + " Kb" + else: + size = str(round(size/1000000.0, 2)) + " Mb" + return size + + + def GetCreationDate(self): + """ Returns the file last modification date associated to a thumbnail. """ + + return self._lastmod + + + def GetOriginalSize(self): + """ Returns a tuple containing the original image width and height, in pixels. """ + + return self._originalsize + + + def GetCaptionLinesCount(self, width): + """ + Returns the number of lines for the caption. + + :param `width`: the maximum width, in pixels, available for the caption text. + """ + + self.BreakCaption(width) + return len(self._captionbreaks) - 1 + + + def BreakCaption(self, width): + """ + Breaks the caption in several lines of text (if needed). + + :param `width`: the maximum width, in pixels, available for the caption text. + """ + + if len(self._captionbreaks) > 0 or width < 16: + return + + self._captionbreaks.append(0) + + if len(self._caption) == 0: + return + + pos = width//16 + beg = 0 + end = 0 + + dc = wx.MemoryDC() + bmp = wx.Bitmap(10, 10) + dc.SelectObject(bmp) + + while 1: + + if pos >= len(self._caption): + + self._captionbreaks.append(len(self._caption)) + break + + sw, sh = dc.GetTextExtent(self._caption[beg:pos-beg]) + + if sw > width: + + if end > 0: + + self._captionbreaks.append(end) + beg = end + + else: + + self._captionbreaks.append(pos) + beg = pos + + pos = beg + width//16 + end = 0 + + if pos < len(self._caption) and self._caption[pos] in [" ", "-", ",", ".", "_"]: + end = pos + 1 + + pos = pos + 1 + + + dc.SelectObject(wx.NullBitmap) + + + def GetInfo(self): + """ Returns info for thumbnain in display format. """ + thumbinfo = "Name: " + self.GetFileName() + "\n" \ + "Size: " + self.GetDisplayFileSize() + "\n" \ + "Modified: " + self.GetCreationDate() + "\n" \ + "Dimensions: " + str(self.GetOriginalSize()) + "\n" + return thumbinfo + + + def LoadImage(self): + """ Load image using imagehandler. """ + filename = self.GetFullFileName() + img, size, alpha = self._imagehandler.Load(filename) + self._image = img + self._originalsize = size + self._alpha = alpha + + + def Rotate(self, angle): + """ Rotate image using imagehandler. """ + img = self._imagehandler.Rotate(self._image, angle) + self._image = img + self._originalsize = (img.GetWidth, img.GetHeight) + self._alpha = img.HasAlpha() + # Clear _bitmap so thumbnail is recreated after rotate + self._bitmap = None + + + def GetHighlightBitmap(self, width, height, factor): + """ Returned highlighted bitmap of thumbnail. """ + + img = self.GetThumbnail(width, height) + img = self._imagehandler.HighlightImage(img, factor) + + bmp = img.ConvertToBitmap() + + return bmp + +# ---------------------------------------------------------------------------- # +# Class ScrolledThumbnail +# This Is The Main Class Implementation +# ---------------------------------------------------------------------------- # + +class ScrolledThumbnail(wx.ScrolledWindow): + """ This is the main class implementation of :class:`ThumbnailCtrl`. """ + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, thumboutline=THUMB_OUTLINE_IMAGE, + imagehandler=None): + """ + Default class constructor. + + :param `parent`: parent window. Must not be ``None``; + :param `id`: window identifier. A value of -1 indicates a default value; + :param `pos`: the control position. A value of (-1, -1) indicates a default position, + chosen by either the windowing system or wxPython, depending on platform; + :param `size`: the control size. A value of (-1, -1) indicates a default size, + chosen by either the windowing system or wxPython, depending on platform; + :param `thumboutline`: outline style for :class:`ScrolledThumbnail`, which may be: + + =========================== ======= ================================== + Outline Flag Value Description + =========================== ======= ================================== + ``THUMB_OUTLINE_NONE`` 0 No outline is drawn on selection + ``THUMB_OUTLINE_FULL`` 1 Full outline (image+caption) is drawn on selection + ``THUMB_OUTLINE_RECT`` 2 Only thumbnail bounding rectangle is drawn on selection (default) + ``THUMB_OUTLINE_IMAGE`` 4 Only image bounding rectangle is drawn. + =========================== ======= ================================== + + :param `imagehandler`: can be :class:`PILImageHandler` if PIL is installed (faster), or + :class:`NativeImageHandler` which only uses wxPython image methods. + """ + + wx.ScrolledWindow.__init__(self, parent, id, pos, size) + + self._items = [] + self.SetThumbSize(96, 80) + self._tOutline = thumboutline + self._selected = -1 + self._pointed = -1 + self._pmenu = None + self._gpmenu = None + self._dragging = False + self._checktext = False + self._dropShadow = True + + self._tCaptionHeight = [] + self._selectedarray = [] + self._tTextHeight = 16 + self._tCaptionBorder = 8 + self._tOutlineNotSelected = True + self._mouseeventhandled = False + self._highlight = False + self._zoomfactor = 1.4 + self.SetCaptionFont() + + self._enabletooltip = False + + self._parent = parent + + self._selectioncolour = "#009EFF" + self.grayPen = wx.Pen("#A2A2D2", 1, wx.SHORT_DASH) + self.grayPen.SetJoin(wx.JOIN_MITER) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX)) + + t, b, s = getShadow() + self.shadow = wx.MemoryDC() + self.shadow.SelectObject(s) + + self.ShowFileNames(True) + + self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) + self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) + self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseDClick) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) + self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) + self.Bind(wx.EVT_MOTION, self.OnMouseMove) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) + self.Bind(EVT_THUMBNAILS_THUMB_CHANGED, self.OnThumbChanged) + self.Bind(wx.EVT_CHAR, self.OnChar) + self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) + + self.Bind(wx.EVT_SIZE, self.OnResize) + self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) + self.Bind(wx.EVT_PAINT, self.OnPaint) + + + def GetSelectedItem(self, index): + """ + Returns the selected thumbnail. + + :param `index`: the thumbnail index (i.e., the selection). + """ + + return self.GetItem(self.GetSelection(index)) + + + def GetPointed(self): + """ Returns the pointed thumbnail index. """ + + return self._pointed + + + def GetHighlightPointed(self): + """ + Returns whether the thumbnail pointed should be highlighted or not. + + :note: Please be aware that this functionality may be slow on slower computers. + """ + + return self._highlight + + + def SetHighlightPointed(self, highlight=True): + """ + Sets whether the thumbnail pointed should be highlighted or not. + + :param `highlight`: ``True`` to enable highlight-on-point with the mouse, + ``False`` otherwise. + + :note: Please be aware that this functionality may be slow on slower computers. + """ + + self._highlight = highlight + + + def SetThumbOutline(self, outline): + """ + Sets the thumbnail outline style on selection. + + :param `outline`: the outline to use on selection. This can be one of the following + bits: + + =========================== ======= ================================== + Outline Flag Value Description + =========================== ======= ================================== + ``THUMB_OUTLINE_NONE`` 0 No outline is drawn on selection + ``THUMB_OUTLINE_FULL`` 1 Full outline (image+caption) is drawn on selection + ``THUMB_OUTLINE_RECT`` 2 Only thumbnail bounding rectangle is drawn on selection (default) + ``THUMB_OUTLINE_IMAGE`` 4 Only image bounding rectangle is drawn. + =========================== ======= ================================== + + """ + + if outline not in [THUMB_OUTLINE_NONE, THUMB_OUTLINE_FULL, THUMB_OUTLINE_RECT, + THUMB_OUTLINE_IMAGE]: + return + + self._tOutline = outline + + + def GetThumbOutline(self): + """ + Returns the thumbnail outline style on selection. + + :see: :meth:`~ScrolledThumbnail.SetThumbOutline` for a list of possible return values. + """ + + return self._tOutline + + + def SetDropShadow(self, drop): + """ + Sets whether to drop a shadow behind thumbnails or not. + + :param `drop`: ``True`` to drop a shadow behind each thumbnail, ``False`` otheriwise. + """ + + self._dropShadow = drop + self.Refresh() + + + def GetDropShadow(self): + """ + Returns whether to drop a shadow behind thumbnails or not. + """ + + return self._dropShadow + + + def GetPointedItem(self): + """ Returns the pointed thumbnail. """ + + return self.GetItem(self._pointed) + + + def GetItem(self, index): + """ + Returns the item at position `index`. + + :param `index`: the thumbnail index position. + """ + + return index >= 0 and (index < len(self._items) and [self._items[index]] or [None])[0] + + + def GetItemCount(self): + """ Returns the number of thumbnails. """ + + return len(self._items) + + + def GetThumbWidth(self): + """ Returns the thumbnail width. """ + + return self._tWidth + + + def GetThumbHeight(self): + """ Returns the thumbnail height. """ + + return self._tHeight + + + def GetThumbBorder(self): + """ Returns the thumbnail border. """ + + return self._tBorder + + + def ShowFileNames(self, show=True): + """ + Sets whether the user wants to show file names under the thumbnails or not. + + :param `show`: ``True`` to show file names under the thumbnails, ``False`` otherwise. + """ + + self._showfilenames = show + self.Refresh() + + + def SetPopupMenu(self, menu): + """ + Sets the thumbnails popup menu when at least one thumbnail is selected. + + :param `menu`: an instance of :class:`wx.Menu`. + """ + + self._pmenu = menu + + + def GetPopupMenu(self): + """ Returns the thumbnails popup menu when at least one thumbnail is selected. """ + + return self._pmenu + + + def SetGlobalPopupMenu(self, gpmenu): + """ + Sets the global thumbnails popup menu (no need of thumbnail selection). + + :param `gpmenu`: an instance of :class:`wx.Menu`. + """ + + self._gpmenu = gpmenu + + + def GetGlobalPopupMenu(self): + """ Returns the global thumbnailss popup menu (no need of thumbnail selection). """ + + return self._gpmenu + + + def GetSelectionColour(self): + """ Returns the colour used to indicate a selected thumbnail. """ + + return self._selectioncolour + + + def SetSelectionColour(self, colour=None): + """ + Sets the colour used to indicate a selected thumbnail. + + :param `colour`: a valid :class:`wx.Colour` object. If defaulted to ``None``, it + will be taken from the system settings. + """ + + if colour is None: + colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) + + self._selectioncolour = colour + + + def EnableDragging(self, enable=True): + """ + Enables/disables thumbnails drag and drop. + + :param `enable`: ``True`` to enable drag and drop, ``False`` to disable it. + """ + + self._dragging = enable + + + def EnableToolTips(self, enable=True): + """ + Globally enables/disables thumbnail file information. + + :param `enable`: ``True`` to enable thumbnail file information, ``False`` to disable it. + """ + + self._enabletooltip = enable + + if not enable and hasattr(self, "_tipwindow"): + self._tipwindow.Enable(False) + + + def GetThumbInfo(self, thumb=-1): + """ + Returns the thumbnail information. + + :param `thumb`: the index of the thumbnail for which we are collecting information. + """ + + thumbinfo = None + + if thumb >= 0: + thumbinfo = self._items[thumb].GetInfo() + \ + "Thumb: " + str(self.GetThumbSize()[0:2]) + return thumbinfo + + def SetThumbSize(self, width, height, border=6): + """ + Sets the thumbnail size as width, height and border. + + :param `width`: the desired thumbnail width; + :param `height`: the desired thumbnail height; + :param `border`: the spacing between thumbnails. + """ + + if width > 350 or height > 280: + return + + self._tWidth = width + self._tHeight = height + self._tBorder = border + self.SetScrollRate((self._tWidth + self._tBorder)/4, + (self._tHeight + self._tBorder)/4) + self.SetSizeHints(self._tWidth + self._tBorder*2 + 16, + self._tHeight + self._tBorder*2 + 8) + if self._items: + self.UpdateShow() + + + def GetThumbSize(self): + """ Returns the thumbnail size as width, height and border. """ + + return self._tWidth, self._tHeight, self._tBorder + + + def Clear(self): + """ Clears :class:`ThumbnailCtrl`. """ + + self._items = [] + self._selected = -1 + self._selectedarray = [] + self.UpdateProp() + self.Refresh() + + + def ThreadImage(self, filenames): + """ + Threaded method to load images. Used internally. + + :param `filenames`: a Python list of file names containing images. + """ + + count = 0 + + while count < len(filenames): + + if not self._isrunning: + self._isrunning = False + thread.exit() + return + + self.LoadImages(filenames[count], count) + if count < 4: + self.Refresh() + elif count%4 == 0: + self.Refresh() + + count = count + 1 + + self._isrunning = False + thread.exit() + + + def LoadImages(self, newfile, imagecount): + """ + Threaded method to load images. Used internally. + + :param `newfile`: a file name containing an image to thumbnail; + :param `imagecount`: the number of images loaded until now. + """ + + if not self._isrunning: + thread.exit() + return + + self._items[imagecount].LoadImage() + + + def ShowThumbs(self, thumbs): + """ + Shows all the thumbnails. + + :param `thumbs`: should be a sequence with instances of :class:`Thumb`; + """ + + self._isrunning = False + + # update items + self._items = thumbs + myfiles = [thumb.GetFullFileName() for thumb in thumbs] + + self._isrunning = True + + thread.start_new_thread(self.ThreadImage, (myfiles,)) + wx.MilliSleep(20) + + self._selectedarray = [] + self.UpdateProp() + self.Refresh() + + + + def SetSelection(self, value=-1): + """ + Sets thumbnail selection. + + :param `value`: the thumbnail index to select. + """ + + self._selected = value + + if value != -1: + self._selectedarray = [value] + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + self.ScrollToSelected() + self.Refresh() + + + def SetZoomFactor(self, zoom=1.4): + """ + Sets the zoom factor. + + :param `zoom`: a floating point number representing the zoom factor. Must be + greater than or equal to 1.0. + """ + + if zoom <= 1.0: + raise Exception("\nERROR: Zoom Factor Must Be Greater Than 1.0") + + self._zoomfactor = zoom + + + def GetZoomFactor(self): + """ Returns the zoom factor. """ + + return self._zoomfactor + + + def UpdateItems(self): + """ Updates thumbnail items. """ + + selected = self._selectedarray + selectedfname = [] + selecteditemid = [] + + for ii in range(len(self._selectedarray)): + selectedfname.append(self.GetSelectedItem(ii).GetFileName()) + selecteditemid.append(self.GetSelectedItem(ii).GetId()) + + self.UpdateShow() + + if len(selected) > 0: + self._selectedarray = [] + for ii in range(len(self._items)): + for jj in range(len(selected)): + if self._items[ii].GetFileName() == selectedfname[jj] and \ + self._items[ii].GetId() == selecteditemid[jj]: + + self._selectedarray.append(ii) + if len(self._selectedarray) == 1: + self.ScrollToSelected() + + if len(self._selectedarray) > 0: + self.Refresh() + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + + + def SetCaptionFont(self, font=None): + """ + Sets the font for all the thumbnail captions. + + :param `font`: a valid :class:`wx.Font` object. If defaulted to ``None``, a standard + font will be generated. + """ + + if font is None: + font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False) + + self._captionfont = font + + + def GetCaptionFont(self): + """ Returns the font for all the thumbnail captions. """ + + return self._captionfont + + + def UpdateShow(self): + """ Updates thumbnail items. """ + + self.ShowThumbs(self._items) + + + def GetCaptionHeight(self, begRow, count=1): + """ + Returns the height for the file name caption. + + :param `begRow`: the caption line at which we start measuring the height; + :param `count`: the number of lines to measure. + """ + + capHeight = 0 + for ii in range(begRow, begRow + count): + if ii < len(self._tCaptionHeight): + capHeight = capHeight + self._tCaptionHeight[ii] + + return capHeight*self._tTextHeight + + + def GetItemIndex(self, x, y): + """ + Returns the thumbnail index at position (x, y). + + :param `x`: the mouse `x` position; + :param `y`: the mouse `y` position. + """ + + col = (x - self._tBorder)//(self._tWidth + self._tBorder) + + if col >= self._cols: + col = self._cols - 1 + + row = -1 + y = y - self._tBorder + + while y > 0: + + row = row + 1 + y = y - (self._tHeight + self._tBorder + self.GetCaptionHeight(row)) + + if row < 0: + row = 0 + + index = row*self._cols + col + + if index >= len(self._items): + index = -1 + + return index + + + def UpdateProp(self, checkSize=True): + """ + Updates :class:`ThumbnailCtrl` and its visible thumbnails. + + :param `checkSize`: ``True`` to update the items visibility if the window + size has changed. + """ + + width = self.GetClientSize().GetWidth() + self._cols = (width - self._tBorder)//(self._tWidth + self._tBorder) + + if self._cols <= 0: + self._cols = 1 + + tmpvar = (len(self._items)%self._cols and [1] or [0])[0] + self._rows = len(self._items)//self._cols + tmpvar + + self._tCaptionHeight = [] + + for row in range(self._rows): + + capHeight = 0 + + for col in range(self._cols): + + ii = row*self._cols + col + + if len(self._items) > ii and \ + self._items[ii].GetCaptionLinesCount(self._tWidth - self._tCaptionBorder) > capHeight: + + capHeight = self._items[ii].GetCaptionLinesCount(self._tWidth - self._tCaptionBorder) + + self._tCaptionHeight.append(capHeight) + + self.SetVirtualSize((self._cols*(self._tWidth + self._tBorder) + self._tBorder, + self._rows*(self._tHeight + self._tBorder) + \ + self.GetCaptionHeight(0, self._rows) + self._tBorder)) + + self.SetSizeHints(self._tWidth + 2*self._tBorder + 16, + self._tHeight + 2*self._tBorder + 8 + \ + (self._rows and [self.GetCaptionHeight(0)] or [0])[0]) + + if checkSize and width != self.GetClientSize().GetWidth(): + self.UpdateProp(False) + + + def GetItem(self, pos): + """ + Return thumbnail at specified position. + + :param pos: the index of the thumbnail + :return: the Thumb + """ + + return self._items[pos] + + def InsertItem(self, thumb, pos): + """ + Inserts a thumbnail in the specified position. + + :param `pos`: the index at which we wish to insert the new thumbnail. + """ + + if pos < 0 or pos > len(self._items): + self._items.append(thumb) + else: + self._items.insert(pos, thumb) + + self.UpdateProp() + + + def RemoveItemAt(self, pos): + """ + Removes a thumbnail at the specified position. + + :param `pos`: the index at which we wish to remove the thumbnail. + """ + + del self._items[pos] + + self.UpdateProp() + + + def GetPaintRect(self): + """ Returns the paint bounding rect for the :meth:`~ScrolledThumbnail.OnPaint` method. """ + + size = self.GetClientSize() + paintRect = wx.Rect(0, 0, size.GetWidth(), size.GetHeight()) + paintRect.x, paintRect.y = self.GetViewStart() + xu, yu = self.GetScrollPixelsPerUnit() + paintRect.x = paintRect.x*xu + paintRect.y = paintRect.y*yu + + return paintRect + + + def IsSelected(self, indx): + """ + Returns whether a thumbnail is selected or not. + + :param `indx`: the index of the thumbnail to check for selection. + """ + + return self._selectedarray.count(indx) != 0 + + + def GetSelection(self, selIndex=-1): + """ + Returns the selected thumbnail. + + :param `selIndex`: if not equal to -1, the index of the selected thumbnail. + """ + + return (selIndex == -1 and [self._selected] or + [self._selectedarray[selIndex]])[0] + + + def ScrollToSelected(self): + """ Scrolls the :class:`ScrolledWindow` to the selected thumbnail. """ + + if self.GetSelection() == -1: + return + + # get row + row = self.GetSelection()//self._cols + # calc position to scroll view + + paintRect = self.GetPaintRect() + y1 = row*(self._tHeight + self._tBorder) + self.GetCaptionHeight(0, row) + y2 = y1 + self._tBorder + self._tHeight + self.GetCaptionHeight(row) + + if y1 < paintRect.GetTop(): + sy = y1 # scroll top + elif y2 > paintRect.GetBottom(): + sy = y2 - paintRect.height # scroll bottom + else: + return + + # scroll view + xu, yu = self.GetScrollPixelsPerUnit() + sy = sy/yu + (sy%yu and [1] or [0])[0] # convert sy to scroll units + x, y = self.GetViewStart() + + self.Scroll(x,sy) + + + def CalculateBestCaption(self, dc, caption, sw, width): + """ + Calculates the best caption string to show based on the actual zoom factor. + + :param `dc`: an instance of :class:`wx.DC`; + :param `caption`: the original caption string; + :param `sw`: the maximum width allowed for the caption string, in pixels; + :param `width`: the caption string width, in pixels. + """ + + caption = caption + "..." + + while sw > width: + caption = caption[1:] + sw, sh = dc.GetTextExtent(caption) + + return "..." + caption[0:-3] + + + def DrawThumbnail(self, bmp, thumb, index): + """ + Draws a visible thumbnail. + + :param `bmp`: the thumbnail version of the original image; + :param `thumb`: an instance of :class:`Thumb`; + :param `index`: the index of the thumbnail to draw. + """ + + dc = wx.MemoryDC() + dc.SelectObject(bmp) + + x = self._tBorder/2 + y = self._tBorder/2 + + # background + dc.SetPen(wx.Pen(wx.BLACK, 0, wx.TRANSPARENT)) + dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID)) + dc.DrawRectangle(0, 0, bmp.GetWidth(), bmp.GetHeight()) + + # image + if index == self.GetPointed() and self.GetHighlightPointed(): + factor = 1.5 + img = thumb.GetHighlightBitmap(self._tWidth, self._tHeight, factor) + else: + img = thumb.GetBitmap(self._tWidth, self._tHeight) + + ww = img.GetWidth() + hh = img.GetHeight() + imgRect = wx.Rect(x + (self._tWidth - img.GetWidth())/2, + y + (self._tHeight - img.GetHeight())/2, + img.GetWidth(), img.GetHeight()) + + if not thumb._alpha and self._dropShadow: + dc.Blit(imgRect.x+5, imgRect.y+5, imgRect.width, imgRect.height, self.shadow, 500-ww, 500-hh) + dc.DrawBitmap(img, imgRect.x, imgRect.y, True) + + colour = self.GetSelectionColour() + selected = self.IsSelected(index) + + colour = self.GetSelectionColour() + + # draw caption + sw, sh = 0, 0 + if self._showfilenames: + textWidth = 0 + dc.SetFont(self.GetCaptionFont()) + mycaption = thumb.GetCaption(0) + sw, sh = dc.GetTextExtent(mycaption) + + if sw > self._tWidth: + mycaption = self.CalculateBestCaption(dc, mycaption, sw, self._tWidth) + sw = self._tWidth + + textWidth = sw + 8 + tx = x + (self._tWidth - textWidth)/2 + ty = y + self._tHeight + + txtcolour = "#7D7D7D" + dc.SetTextForeground(txtcolour) + + tx = x + (self._tWidth - sw)/2 + if hh >= self._tHeight: + ty = y + self._tHeight + (self._tTextHeight - sh)/2 + 3 + else: + ty = y + hh + (self._tHeight-hh)/2 + (self._tTextHeight - sh)/2 + 3 + + dc.DrawText(mycaption, tx, ty) + + # outline + if self._tOutline != THUMB_OUTLINE_NONE and (self._tOutlineNotSelected or self.IsSelected(index)): + + dotrect = wx.Rect() + dotrect.x = x - 2 + dotrect.y = y - 2 + dotrect.width = bmp.GetWidth() - self._tBorder + 4 + dotrect.height = bmp.GetHeight() - self._tBorder + 4 + + dc.SetPen(wx.Pen((self.IsSelected(index) and [colour] or [wx.LIGHT_GREY])[0], + 0, wx.PENSTYLE_SOLID)) + dc.SetBrush(wx.Brush(wx.BLACK, wx.BRUSHSTYLE_TRANSPARENT)) + + if self._tOutline == THUMB_OUTLINE_FULL or self._tOutline == THUMB_OUTLINE_RECT: + + imgRect.x = x + imgRect.y = y + imgRect.width = bmp.GetWidth() - self._tBorder + imgRect.height = bmp.GetHeight() - self._tBorder + + if self._tOutline == THUMB_OUTLINE_RECT: + imgRect.height = self._tHeight + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + + if selected: + + dc.SetPen(self.grayPen) + dc.DrawRoundedRectangle(dotrect, 2) + + dc.SetPen(wx.Pen(wx.WHITE)) + dc.DrawRectangle(imgRect.x, imgRect.y, + imgRect.width, imgRect.height) + + pen = wx.Pen((selected and [colour] or [wx.LIGHT_GREY])[0], 2) + pen.SetJoin(wx.JOIN_MITER) + dc.SetPen(pen) + if self._tOutline == THUMB_OUTLINE_FULL: + dc.DrawRoundedRectangle(imgRect.x - 1, imgRect.y - 1, + imgRect.width + 3, imgRect.height + 3, 2) + else: + dc.DrawRectangle(imgRect.x - 1, imgRect.y - 1, + imgRect.width + 3, imgRect.height + 3) + else: + dc.SetPen(wx.Pen(wx.LIGHT_GREY)) + + dc.DrawRectangle(imgRect.x - 1, imgRect.y - 1, + imgRect.width + 2, imgRect.height + 2) + + + dc.SelectObject(wx.NullBitmap) + + + def OnPaint(self, event): + """ + Handles the ``wx.EVT_PAINT`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`PaintEvent` event to be processed. + """ + + paintRect = self.GetPaintRect() + + dc = wx.BufferedPaintDC(self) + self.PrepareDC(dc) + + dc.SetPen(wx.Pen(wx.BLACK, 0, wx.PENSTYLE_TRANSPARENT)) + dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID)) + + w, h = self.GetClientSize() + + # items + row = -1 + xwhite = self._tBorder + + for ii in range(len(self._items)): + + col = ii%self._cols + if col == 0: + row = row + 1 + + xwhite = ((w - self._cols*(self._tWidth + self._tBorder)))/(self._cols+1) + tx = xwhite + col*(self._tWidth + self._tBorder) + + ty = self._tBorder/2 + row*(self._tHeight + self._tBorder) + \ + self.GetCaptionHeight(0, row) + tw = self._tWidth + self._tBorder + th = self._tHeight + self.GetCaptionHeight(row) + self._tBorder + # visible? + if not paintRect.Intersects(wx.Rect(tx, ty, tw, th)): + continue + + thmb = wx.Bitmap(tw, th) + self.DrawThumbnail(thmb, self._items[ii], ii) + dc.DrawBitmap(thmb, tx, ty) + + rect = wx.Rect(xwhite, self._tBorder/2, + self._cols*(self._tWidth + self._tBorder), + self._rows*(self._tHeight + self._tBorder) + \ + self.GetCaptionHeight(0, self._rows)) + + w = max(self.GetClientSize().GetWidth(), rect.width) + h = max(self.GetClientSize().GetHeight(), rect.height) + dc.DrawRectangle(0, 0, w, rect.y) + dc.DrawRectangle(0, 0, rect.x, h) + dc.DrawRectangle(rect.GetRight(), 0, w - rect.GetRight(), h + 50) + dc.DrawRectangle(0, rect.GetBottom(), w, h - rect.GetBottom() + 50) + + col = len(self._items)%self._cols + + if col > 0: + rect.x = rect.x + col*(self._tWidth + self._tBorder) + rect.y = rect.y + (self._rows - 1)*(self._tHeight + self._tBorder) + \ + self.GetCaptionHeight(0, self._rows - 1) + dc.DrawRectangle(rect) + + + def OnResize(self, event): + """ + Handles the ``wx.EVT_SIZE`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`wx.SizeEvent` event to be processed. + """ + + self.UpdateProp() + self.ScrollToSelected() + self.Refresh() + + + def OnMouseDown(self, event): + """ + Handles the ``wx.EVT_LEFT_DOWN`` and ``wx.EVT_RIGHT_DOWN`` events for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + """ + + x = event.GetX() + y = event.GetY() + x, y = self.CalcUnscrolledPosition(x, y) + # get item number to select + lastselected = self._selected + self._selected = self.GetItemIndex(x, y) + + self._mouseeventhandled = False + update = False + + if event.ControlDown(): + if self._selected == -1: + self._mouseeventhandled = True + elif not self.IsSelected(self._selected): + self._selectedarray.append(self._selected) + update = True + self._mouseeventhandled = True + + elif event.ShiftDown(): + if self._selected != -1: + begindex = self._selected + endindex = lastselected + if lastselected < self._selected: + begindex = lastselected + endindex = self._selected + self._selectedarray = [] + + for ii in range(begindex, endindex+1): + self._selectedarray.append(ii) + + update = True + + self._selected = lastselected + self._mouseeventhandled = True + + else: + + if self._selected == -1: + update = len(self._selectedarray) > 0 + self._selectedarray = [] + self._mouseeventhandled = True + elif len(self._selectedarray) <= 1: + try: + update = len(self._selectedarray)== 0 or self._selectedarray[0] != self._selected + except: + update = True + self._selectedarray = [] + self._selectedarray.append(self._selected) + self._mouseeventhandled = True + + if update: + self.ScrollToSelected() + self.Refresh() + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + + self.SetFocus() + + + def OnMouseUp(self, event): + """ + Handles the ``wx.EVT_LEFT_UP`` and ``wx.EVT_RIGHT_UP`` events for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + """ + + # get item number to select + x = event.GetX() + y = event.GetY() + x, y = self.CalcUnscrolledPosition(x, y) + lastselected = self._selected + self._selected = self.GetItemIndex(x,y) + + if not self._mouseeventhandled: + # set new selection + if event.ControlDown(): + if self._selected in self._selectedarray: + self._selectedarray.remove(self._selected) + + self._selected = -1 + else: + self._selectedarray = [] + self._selectedarray.append(self._selected) + + self.ScrollToSelected() + self.Refresh() + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + + # Popup menu + if event.RightUp(): + if self._selected >= 0 and self._pmenu: + self.PopupMenu(self._pmenu) + elif self._selected >= 0 and not self._pmenu and self._gpmenu: + self.PopupMenu(self._gpmenu) + elif self._selected == -1 and self._gpmenu: + self.PopupMenu(self._gpmenu) + + if event.ShiftDown(): + self._selected = lastselected + + + def OnMouseDClick(self, event): + """ + Handles the ``wx.EVT_LEFT_DCLICK`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + """ + + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_DCLICK, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + + + def OnMouseMove(self, event): + """ + Handles the ``wx.EVT_MOTION`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + """ + + # -- drag & drop -- + if self._dragging and event.Dragging() and len(self._selectedarray) > 0: + + files = wx.FileDataObject() + for ii in range(len(self._selectedarray)): + files.AddFile(opj(self.GetSelectedItem(ii).GetFullFileName())) + + source = wx.DropSource(self) + source.SetData(files) + source.DoDragDrop(wx.Drag_DefaultMove) + + # -- light-effect -- + x = event.GetX() + y = event.GetY() + x, y = self.CalcUnscrolledPosition(x, y) + + # get item number + sel = self.GetItemIndex(x, y) + + if sel == self._pointed: + if self._enabletooltip and sel >= 0: + if not hasattr(self, "_tipwindow"): + self._tipwindow = wx.ToolTip(self.GetThumbInfo(sel)) + self._tipwindow.SetDelay(1000) + self.SetToolTip(self._tipwindow) + else: + self._tipwindow.SetDelay(1000) + self._tipwindow.SetTip(self.GetThumbInfo(sel)) + + event.Skip() + return + + if self._enabletooltip: + if hasattr(self, "_tipwindow"): + self._tipwindow.Enable(False) + + # update thumbnail + self._pointed = sel + + if self._enabletooltip and sel >= 0: + if not hasattr(self, "_tipwindow"): + self._tipwindow = wx.ToolTip(self.GetThumbInfo(sel)) + self._tipwindow.SetDelay(1000) + self._tipwindow.Enable(True) + self.SetToolTip(self._tipwindow) + else: + self._tipwindow.SetDelay(1000) + self._tipwindow.Enable(True) + self._tipwindow.SetTip(self.GetThumbInfo(sel)) + + self.Refresh() + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_POINTED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + event.Skip() + + + def OnMouseLeave(self, event): + """ + Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + """ + + if self._pointed != -1: + + self._pointed = -1 + self.Refresh() + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_POINTED, self.GetId()) + self.GetEventHandler().ProcessEvent(eventOut) + + + def OnThumbChanged(self, event): + """ + Handles the ``EVT_THUMBNAILS_THUMB_CHANGED`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`ThumbnailEvent` event to be processed. + """ + + for ii in range(len(self._items)): + if self._items[ii].GetFileName() == event.GetString(): + + self._items[ii].SetFilename(self._items[ii].GetFileName()) + if event.GetClientData(): + + img = wx.Image(event.GetClientData()) + self._items[ii].SetImage(img) + + self.Refresh() + + + def OnChar(self, event): + """ + Handles the ``wx.EVT_CHAR`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`KeyEvent` event to be processed. + + :note: You have these choices: + + (1) ``d`` key rotates 90 degrees clockwise the selected thumbnails; + (2) ``s`` key rotates 90 degrees counter-clockwise the selected thumbnails; + (3) ``a`` key rotates 180 degrees the selected thumbnails; + (4) ``Del`` key deletes the selected thumbnails; + (5) ``+`` key zooms in; + (6) ``-`` key zooms out. + """ + + if event.KeyCode == ord("s"): + self.Rotate() + elif event.KeyCode == ord("d"): + self.Rotate(270) + elif event.KeyCode == ord("a"): + self.Rotate(180) + elif event.KeyCode in [wx.WXK_ADD, wx.WXK_NUMPAD_ADD]: + self.ZoomIn() + elif event.KeyCode in [wx.WXK_SUBTRACT, wx.WXK_NUMPAD_SUBTRACT]: + self.ZoomOut() + + selected = [] + for ii in range(len(self._items)): + if self.IsSelected(ii): + selected.append(ii) + + eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_CHAR, self.GetId(), + thumbs=selected, keycode=event.KeyCode) + self.GetEventHandler().ProcessEvent(eventOut) + + event.Skip() + + def Rotate(self, angle=90): + """ + Rotates the selected thumbnails by the angle specified by `angle`. + + :param `angle`: the rotation angle for the thumbnail, in degrees. + """ + + wx.BeginBusyCursor() + + selected = [] + for ii in range(len(self._items)): + if self.IsSelected(ii): + selected.append(self._items[ii]) + + for thumb in selected: + thumb.Rotate(angle) + + wx.EndBusyCursor() + + if self.GetSelection() != -1: + self.Refresh() + + + def OnMouseWheel(self, event): + """ + Handles the ``wx.EVT_MOUSEWHEEL`` event for :class:`ThumbnailCtrl`. + + :param `event`: a :class:`MouseEvent` event to be processed. + + :note: If you hold down the ``Ctrl`` key, you can zoom in/out with the mouse wheel. + """ + + if event.ControlDown(): + if event.GetWheelRotation() > 0: + self.ZoomIn() + else: + self.ZoomOut() + else: + event.Skip() + + + def ZoomOut(self): + """ Zooms the thumbnails out. """ + + w, h, b = self.GetThumbSize() + + if w < 40 or h < 40: + return + + zoom = self.GetZoomFactor() + neww = float(w)/zoom + newh = float(h)/zoom + + self.SetThumbSize(int(neww), int(newh)) + self.OnResize(None) + self._checktext = True + + self.Refresh() + + + def ZoomIn(self): + """ Zooms the thumbnails in. """ + + size = self.GetClientSize() + w, h, b = self.GetThumbSize() + zoom = self.GetZoomFactor() + + if w*zoom + b > size.GetWidth() or h*zoom + b > size.GetHeight(): + if w*zoom + b > size.GetWidth(): + neww = size.GetWidth() - 2*self._tBorder + newh = (float(h)/w)*neww + else: + newh = size.GetHeight() - 2*self._tBorder + neww = (float(w)/h)*newh + + else: + neww = float(w)*zoom + newh = float(h)*zoom + + self.SetThumbSize(int(neww), int(newh)) + self.OnResize(None) + self._checktext = True + + self.Refresh() + + +# ---------------------------------------------------------------------------- # +# Class ThumbnailEvent +# ---------------------------------------------------------------------------- # + +class ThumbnailEvent(wx.PyCommandEvent): + """ + This class is used to send events when a thumbnail is hovered, selected, + double-clicked or when its caption has been changed. + """ + def __init__(self, evtType, evtId=-1, thumbs=None, keycode=None): + """ + Default class constructor. + + :param `evtType`: the event type; + :param `evtId`: the event identifier. + """ + + wx.PyCommandEvent.__init__(self, evtType, evtId) + self.thumbs = thumbs + self.keycode = keycode diff --git a/wx/lib/agw/thumbnailctrl.py b/wx/lib/agw/thumbnailctrl.py index 0e938164..97ef39e3 100644 --- a/wx/lib/agw/thumbnailctrl.py +++ b/wx/lib/agw/thumbnailctrl.py @@ -4,27 +4,10 @@ # # Andrea Gavana And Peter Damoc, @ 12 Dec 2005 # Latest Revision: 27 Dec 2012, 21.00 GMT -# -# -# TODO List/Caveats -# -# 1. Thumbnail Creation/Display May Be Somewhat Improved From The Execution -# Speed Point Of View; -# -# 2. The Implementation For wx.HORIZONTAL Style Is Still To Be Written; -# -# 3. I Have No Idea On How To Implement Thumbnails For Audio, Video And Other Files. -# -# 4. Other Ideas? -# -# -# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please -# Write To Me At: -# # andrea.gavana@gmail.com # andrea.gavana@maerskoil.com # -# Or, Obviously, To The wxPython Mailing List!!! +# Refactored and reorganized: Michael Eager (eager@eagercon.com), 26 Sep 2020 # # Tags: phoenix-port, documented, unittest, py3-port # @@ -52,6 +35,11 @@ control. However, :class:`ThumbnailCtrl` wouldn't have been so fast and complete without the suggestions and hints from Peter Damoc. So, if he accepts the mention, this control is his as much as mine. +:class:`ThumbnailCtrl` is more of a demo application than a widget. +The :class:`ScrolledThumbnail` is a freestanding widget, accepting a list of +:class:`Thumb` objects which represent files and which returns a thumbnail +on request. :class:`Thumb` can be extended by the user to provide thumbnails +for other data types, such as text, audio, video, or PDF files. Usage ===== @@ -137,11 +125,11 @@ This class processes the following events: ================================== ================================================== Event Name Description ================================== ================================================== -``EVT_THUMBNAILS_CAPTION_CHANGED`` The thumbnail caption has been changed. Not used at present. ``EVT_THUMBNAILS_DCLICK`` The user has double-clicked on a thumbnail. ``EVT_THUMBNAILS_POINTED`` The mouse cursor is hovering over a thumbnail. ``EVT_THUMBNAILS_SEL_CHANGED`` The user has changed the selected thumbnail. ``EVT_THUMBNAILS_THUMB_CHANGED`` The thumbnail of an image has changed. Used internally. +``EVT_THUMBNAILS_CHAR`` A key was typed in the widget ================================== ================================================== @@ -150,9 +138,9 @@ License And Version :class:`ThumbnailCtrl` is distributed under the wxPython license. -Latest revision: Andrea Gavana @ 27 Dec 2012, 21.00 GMT +Latest revision: Michael Eager @ 26 Sep 2020 -Version 0.9 +Version 1.0 """ @@ -164,170 +152,13 @@ Version 0.9 import wx import os import time -import zlib -import six -from math import pi - -from wx.lib.embeddedimage import PyEmbeddedImage - -if six.PY3: - import _thread as thread -else: - import thread - -#---------------------------------------------------------------------- -# Get Default Icon/Data -#---------------------------------------------------------------------- - -def GetMondrianData(): - """ Returns a default image placeholder as a decompressed stream of characters. """ - return \ -b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\ -\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\ -ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\ -o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\ -\xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\ -\x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\ -\x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82' - - -def GetMondrianBitmap(): - """ Returns a default image placeholder as a :class:`wx.Bitmap`. """ - - return wx.Bitmap(GetMondrianImage()) - - -def GetMondrianImage(): - """ Returns a default image placeholder as a :class:`wx.Image`. """ - - stream = six.BytesIO(GetMondrianData()) - return wx.Image(stream) - - -#---------------------------------------------------------------------- -file_broken = PyEmbeddedImage( - b"iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAAK/INwWK6QAADU9J" - b"REFUeJzNWn1QE2cefja7yRI+lAKiAgKCVIqegUYMFMsM4kfvzk7nmFrnvHGcOT3bq3P/OJ1p" - b"b+zYf+r0HGxt6VVFqpYqWq9TW23RVnp6R7FXiq0UPAGhyFeQSCQJISHZ7Mf9gZuGkE02IeA9" - b"M5k3u/vuu+/z/H7v7/297y4BPzh//vxfSJIsIgjC5a/eTIPn+UnHgiDIqisIAsUwTNfmzZtf" - b"A8D7qk/5e3B0dPS6wsLCp/09MFTIbdO7ntR9vs5HRESgvr6+E0AlAD2AKZX8CsDzvEKtVsvq" - b"aKjw7LggCO7jcJQqlQoMw6gBpAIYRAgCECGxCgDPTsohEopI4n+e5xXww1MRHkry4d1Zf9fk" - b"uv90MOsCeMKfGN51pO6ZrmizKsBsWt976EjhoQ+BYMQItpSDWRNAjptPZ4xLCfl/PQTCaX2p" - b"+wNhVgSYbet7tumdRXpj1ofAbFhfqn1fmHEBHpb1AYAgAudxMy4AQRB+LReuSB/MGsETMyqA" - b"2WyG1WqVZQl/CDUZeuge0NvbKwwODk7bC7zryYFI/qF6QGNj42BnZ6dZ6rocQtNNhQNhxgQw" - b"Go24fPnyDy0tLTcFQfDpBZ7/w2198RcIMyZAW1vb+KVLlxoGBwfr7927B4Vi8qO8CfnqbCjW" - b"D4Y8MEMCCIKA77777me73d7a1tb25dDQkNXzmud/giDgdDrBsqzkuA1l7CuVSgCA0+n0y3FG" - b"BBgYGMCVK1eub926dRFN04aBgYH/el73JKRUKjE4OIi+vj5QFDWtaVG0fGRkJHiex7lz5xyn" - b"T59uBMBI9TWsAoidaW1tNTc1Nd1cv379zuzs7F91dHT8x+l0TooDnuju7h779ttvjdOdLlUq" - b"FaKjo9HV1YX9+/d379q16+QXX3xxFsAAMHU7DAgggNw1tQiCIOByuXDt2rWbK1asUOfk5KxM" - b"SUlZW19f39LX18eTJDmpbbG8cePGndra2mtDQ0NQKpUhJUPR0dHgeR6ffvqpbdeuXV+9/vrr" - b"R4eGhs4A+ArA3ZAECAXd3d24dOnSD2VlZavy8vKQmppaUFdXN2gwGO561yVJEmazGR0dHV3n" - b"z5//+NatW0MU5XebcgooikJMTAy6urqwb9++zp07d1ZfvXq1GsAn8+bNa6qsrEyGxJY4EGBT" - b"lCAIhVR0ZhgGTqcTTqcTDocDDocDgiDgm2++0Y+MjNxbuXLl7wmCQFpaWtbcuXMje3p6fly9" - b"enWyeL8gCCBJEj09PUJLS0s7z/PXWlpavigoKNhBURRYlnU/S6qMjIwEwzD47LPPbIcOHWq4" - b"cuXKPwF8D+DHF154QV1cXFxpsVjiAGwGYIUPL/ArgNFoNNbV1dmtVqvdarW6LBaLY2xszOFw" - b"OBiLxWKz2WxOs9lsHx0ddZhMJpvZbB7v7u7+effu3elZWVmJAJCUlBS1bt26nNbW1kaj0fj0" - b"I4884iYHAHfu3Lnf2dnZDWC4oaHhH08++eRWrVZLe9bxJk9RFNRqNTo6OvDhhx92Hj16tG5k" - b"ZKQBwHUAneXl5cVarfaQVqtddurUqesAUgC0BysAsWfPngOjo6ONY2NjUQzDOACMA7ADcGAi" - b"sjoBuB6U4jFTWlp6Kj4+HjzPIzExEbm5uSuPHz9+csuWLfaEhIRIl8sFgiDA8zxu3rz585Yt" - b"W3SpqanX9u7d+93GjRuv5eXlrRGvT+oQQUCtVsPhcODcuXO2w4cPi1ZvAnADgP3EiRN7NBrN" - b"ntzcXDXHcXA6nREAJF9u+BNA6OnpuQ1gGAD5gCjrUXIPFOUflAIA7siRI8Xp6ekaYOI1lVKp" - b"RGZmpqatre2QXq/v1mg0y4EJKxoMBrS1tQ2UlJQ8abPZjAD23Lhx46Pi4uI1aWlp7mEl1qdp" - b"Grdu3UJNTU1nVVVV3cjIyDUAPwJof+WVVzJ0Ol1NQUHBbxMTEyHOOoEQKOKMP/jJxoIFC/6Q" - b"mZlJidYTBAHp6emp2dnZCV1dXY0MwywnCAIkSUKv1zu7u7uNu3fvTh8aGtoE4Pj7779/ec2a" - b"NZ0ZGRlZwC9Wt9vt+Pzzz22VlZUNV69eFa3+EwDTkSNHynJyct5ZtWpVikKhgMPhgEKhkJx2" - b"gxFgSv1t27ZRUVFRFMuyZGZmZmJcXNwcpVIZT9N0tMvlWpiSklKmVConBbGkpCRq3bp1Kxsb" - b"G69v3Lhxe3p6OgRBQHt7u2Hx4sVRycnJ9Ny5czO3bdv2VHV19XvNzc2frF69+pXY2FgoFAop" - b"q3ds2rQp6plnnnkzLy9v99KlS8EwDDiOC2r5LSnAiRMnIubNm/dHkiSzaZqOIUkygabpGIqi" - b"4iiKmkuSZDRJkiqVSkWRJKmkaZqMiIhAZGQkOI5zk+c4DnFxccjOztZWVVV9+eKLLw5nZGTM" - b"YxgGzc3Ndx577LGF8fHxiI2NhU6n+111dfWZixcvnispKfmzTqebe+HCBW+rtwK4/8Ybb+Rp" - b"NJojBQUFq2JiYuB0OgH8kgqLpdiXoAVobW2NfvbZZ/cWFhbOly3ngwf6yvczMzNzWJZV9Pb2" - b"/lRUVLR2cHAQt2/fvrt9+3YtSZJQKBRYtmxZYXFx8ar6+vqrTU1NF7/++mtdZWXll0ajsQET" - b"Qa4TE3HmBY1Gsz8/P3+Oy+Vyj3dP8nK9QFKA/v5+Cn7GfzBZmiAISE5OTiwqKsq4fft2k91u" - b"X9vf3283GAyWtLS0ZNFTsrOzI0pKSsrq6+v//c477/xNr9evwMRr7ZsAhl966aUFOp3uzfz8" - b"/C2LFi3C+Pj4JMLeAsjJYiUFYFk2oIRyReA4DikpKSgqKlpZW1t7saysjOvo6NAvXbo0NjEx" - b"MZLneXAchzlz5kCj0TwVGxu7RK/Xt2Mihx8DwBw4cGBDbm5uRWFh4aNKpRJ2u30S8VCsD8hY" - b"CwRzXqouz/OIiopCVlaWtrm5+X57e/tAS0uLPicnJzEhIcE9TnmeR05OTvJzzz33a0xMtyNa" - b"rVY4fvz4a6WlpRdKSkoeFZfPYpSXIi93SyzYWWASMTmlZ/2MjIys+Pj4mI6Ojoa+vj5h/fr1" - b"xaKrikIlJyfj8ccfLwNw7NVXX43TarV/LyoqWhsXF4fx8XF3TPFF2Pv/tPIAu93ul7g/+BJD" - b"EAQsXLgwqrS0NLexsfE8RVHLFi1atNn7PpVKhdzc3OUvv/zyvg0bNvwmPz8/WeyPt8sHEkLO" - b"anZaQyCYDUmO47BgwQIsX758VW1t7bc6nU5ISkpSeuYLHMeBYRhkZWWpd+zY8afCwsJklmXB" - b"MMwUlw9EXjwOhIDL4dHRUQwMDMBms4Hn+YCuJSUKz/NQqVRYsmTJcgDzlixZsnzOnDlu1xfj" - b"AMdxoGkaqampsNvtEATBvZ8olzxBEO57whIDxsbGMD4+DrVajaioKERGRoKiKMmdXn9IT09P" - b"Li4ufiIxMVEHACzLThGW47gp54IhHwz8CiCSEt3P4XDA6XTCYrGApmnQNA2VSuUWwzvyescA" - b"l8uF+fPnK59//vm/zp8/f6FoebF98VlSBOWS99WXkATwfrhnw+JmiEKhgEKhAEmSEDM6KTEI" - b"gkBcXBwRExOTkpCQAJfLNSPkg4EsD/Algncs4Hl+ktv6+onjOS0tDTRNT1q4hJN8MCKE5AFS" - b"nQx0DQAYhkFqaqpbrJkmH5YPJMJFXqFQTBmTM0E+LB5gt9slXTpU8t4E5JKXSnsDkfV+HReU" - b"AJ6YLnlf1pNLXhAEmM1msCw7KSZ598/7PEEQ4DgOLMv6VSHQt8JhIS/HG/y5vV6vh9FohFqt" - b"ducN3r8pxCgKJpMJvb29o/hlzzI4AUSEk7yUtfyJwTAMMjMzsXjxYr/zuuf7wbt376K8vLz/" - b"7NmzlzCxpA5NgHCTl1vHFzkAPjNE774aDAaUl5f3V1RU1HAc9y9MbKr4RMA8QIr4TJP3LEU3" - b"5zjOnTtItWUwGLB//36R/CVMbKXZQhbAV2dnk7zYD3G1KI537/oURbndvqKi4hTHcV9iYvd4" - b"zB/HkFPh6ZL3JurvHIBJXuB9XfzG4MCBA/3vvvtujVzysgTwR2I65KVmAl/3iUtmlmUnDQGR" - b"/P3793H48GH9A/Ki2wckH1AAzxjgS4yZJO/dD899A7EORVEYGRlBVVWV8b333vs4GMvLEsAT" - b"oQ4Ff+Q92/YniGcMEAUQ5/ljx44Nv/XWWx9ZrdaLAJqDIR9QAO/9gHCTlxsERRFEAZRKJUwm" - b"E6qrq4cPHjx4xmq11mLi1bglGPIBBfBF8mGQFyHmACaTCSdPnhx+++23z4yOjtZi4pWZKVjy" - b"sgTwFiIU8v7GuVzyIsxmM06fPj188ODBjx5Y/nsAkl+jBoLsPECEryDl7ziQMHLJkyQJi8WC" - b"mpqa4YqKCtHtmzAN8oCM9wJKpRIRERGyyAQi6CvwyeokRcFsNqOurm64oqLijMVimZbbT2pb" - b"6gJN04TNZlNZLBYwDOOeEgNtMsqpF+y1sbEx1NbWDn/wwQdhJQ8AkmZYsWJFVEZGxtZ79+49" - b"wbIsDa/VlBQJqS0of6QD3UMQBHp7e++YTKYrCIPbe8KfHyoALACwGIAqXA8MEQImPnO7A2Ak" - b"nA3/D+/OyD/Ur3BPAAAAAElFTkSuQmCC") - - -def getDataSH(): - """ Return the first part of the shadow dropped behind thumbnails. """ - - return zlib.decompress( -b'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2_A\x98\x83\rHvl\ -\xdc\x9c\n\xa4X\x8a\x9d float(height)/imgheight: - scale = float(height)/imgheight - - newW, newH = int(imgwidth*scale), int(imgheight*scale) - if newW < 1: - newW = 1 - if newH < 1: - newH = 1 - - img = img.Scale(newW, newH) - - bmp = img.ConvertToBitmap() - - self._image = img - - return bmp - - - def GetOriginalImage(self): - """ Returns the bitmap associated to a thumbnail, as a file name. """ - - original = opj((self._dir + "/" + self._filename)) - - return original - - - def GetFullFileName(self): - """ Returns the full filename of the thumbnail. """ - - return self._dir + "/" + self._filename - - - def GetCaption(self, line): - """ - Returns the caption associated to a thumbnail. - - :param `line`: the caption line we wish to retrieve (useful for multilines - caption strings). - """ - - if line + 1 >= len(self._captionbreaks): - return "" - - strs = self._caption - - return strs - - - def GetFileSize(self): - """ Returns the file size associated to a thumbnail. """ - - return self._filesize - - - def GetCreationDate(self): - """ Returns the file last modification date associated to a thumbnail. """ - - return self._lastmod - - - def GetOriginalSize(self): - """ Returns a tuple containing the original image width and height, in pixels. """ - - if hasattr(self, "_threadedimage"): - img = self._threadedimage - else: - img = GetMondrianImage() - - if hasattr(self, "_originalsize"): - imgwidth, imgheight = self._originalsize - else: - imgwidth, imgheight = (img.GetWidth(), img.GetHeight()) - - return imgwidth, imgheight - - - def GetCaptionLinesCount(self, width): - """ - Returns the number of lines for the caption. - - :param `width`: the maximum width, in pixels, available for the caption text. - """ - - self.BreakCaption(width) - return len(self._captionbreaks) - 1 - - - def BreakCaption(self, width): - """ - Breaks the caption in several lines of text (if needed). - - :param `width`: the maximum width, in pixels, available for the caption text. - """ - - if len(self._captionbreaks) > 0 or width < 16: - return - - self._captionbreaks.append(0) - - if len(self._caption) == 0: - return - - pos = width//16 - beg = 0 - end = 0 - - dc = wx.MemoryDC() - bmp = wx.Bitmap(10, 10) - dc.SelectObject(bmp) - - while 1: - - if pos >= len(self._caption): - - self._captionbreaks.append(len(self._caption)) - break - - sw, sh = dc.GetTextExtent(self._caption[beg:pos-beg]) - - if sw > width: - - if end > 0: - - self._captionbreaks.append(end) - beg = end - - else: - - self._captionbreaks.append(pos) - beg = pos - - pos = beg + width//16 - end = 0 - - if pos < len(self._caption) and self._caption[pos] in [" ", "-", ",", ".", "_"]: - end = pos + 1 - - pos = pos + 1 - - - dc.SelectObject(wx.NullBitmap) - - - def SetRotation(self, angle=0): - """ - Sets the thumbnail rotation. - - :param `angle`: the thumbnail rotation, in radians. - """ - - self._rotation = angle - - - def GetRotation(self): - """ Returns the thumbnail rotation, in radians. """ - - return self._rotation # ---------------------------------------------------------------------------- # @@ -909,7 +244,8 @@ class ThumbnailCtrl(wx.Panel): def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, thumboutline=THUMB_OUTLINE_IMAGE, - thumbfilter=THUMB_FILTER_IMAGES, imagehandler=PILImageHandler): + thumbfilter=None, # Ignored, included for backward compatibility + imagehandler=PILImageHandler): """ Default class constructor. @@ -930,8 +266,7 @@ class ThumbnailCtrl(wx.Panel): ``THUMB_OUTLINE_IMAGE`` 4 Only image bounding rectangle is drawn. =========================== ======= ================================== - :param `thumbfilter`: filter for image/video/audio files. Actually only - ``THUMB_FILTER_IMAGES`` is implemented; + :param `thumbfilter`: filter for image/video/audio files. Ignored. :param `imagehandler`: can be :class:`PILImageHandler` if PIL is installed (faster), or :class:`NativeImageHandler` which only uses wxPython image methods. """ @@ -942,7 +277,7 @@ class ThumbnailCtrl(wx.Panel): self._combo = wx.ComboBox(self, -1, style=wx.CB_DROPDOWN | wx.CB_READONLY) self._scrolled = ScrolledThumbnail(self, -1, thumboutline=thumboutline, - thumbfilter=thumbfilter, imagehandler = imagehandler) + imagehandler = imagehandler) subsizer = wx.BoxSizer(wx.HORIZONTAL) subsizer.Add((3, 0), 0) @@ -959,13 +294,13 @@ class ThumbnailCtrl(wx.Panel): methods = ["GetSelectedItem", "GetPointed", "GetHighlightPointed", "SetHighlightPointed", "SetThumbOutline", "GetThumbOutline", "GetPointedItem", "GetItem", "GetItemCount", "GetThumbWidth", "GetThumbHeight", "GetThumbBorder", - "ShowFileNames", "SetPopupMenu", "GetPopupMenu", "SetGlobalPopupMenu", + "SetPopupMenu", "GetPopupMenu", "SetGlobalPopupMenu", "GetGlobalPopupMenu", "SetSelectionColour", "GetSelectionColour", - "EnableDragging", "SetThumbSize", "GetThumbSize", "ShowThumbs", "ShowDir", - "GetShowDir", "SetSelection", "GetSelection", "SetZoomFactor", + "EnableDragging", "SetThumbSize", "GetThumbSize", "ShowThumbs", + "SetSelection", "GetSelection", "SetZoomFactor", "GetZoomFactor", "SetCaptionFont", "GetCaptionFont", "GetItemIndex", "InsertItem", "RemoveItemAt", "IsSelected", "Rotate", "ZoomIn", "ZoomOut", - "EnableToolTips", "GetThumbInfo", "GetOriginalImage", "SetDropShadow", "GetDropShadow"] + "EnableToolTips", "GetThumbInfo", "SetDropShadow", "GetDropShadow"] for method in methods: setattr(self, method, getattr(self._scrolled, method)) @@ -975,6 +310,9 @@ class ThumbnailCtrl(wx.Panel): self._subsizer = subsizer self._combo.Bind(wx.EVT_COMBOBOX, self.OnComboBox) + self.Bind(EVT_THUMBNAILS_CHAR, self.OnThumbChar) + + self._imagehandler = imagehandler def ShowComboBox(self, show=True): @@ -1071,409 +409,45 @@ class ThumbnailCtrl(wx.Panel): return self.thumbnail_ctrl._scrolled.SetBackgroundColour(colour) -# ---------------------------------------------------------------------------- # -# Class ScrolledThumbnail -# This Is The Main Class Implementation -# ---------------------------------------------------------------------------- # - -class ScrolledThumbnail(wx.ScrolledWindow): - """ This is the main class implementation of :class:`ThumbnailCtrl`. """ - - def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=wx.DefaultSize, thumboutline=THUMB_OUTLINE_IMAGE, - thumbfilter=THUMB_FILTER_IMAGES, imagehandler=PILImageHandler): + def ShowDir(self, folder): """ - Default class constructor. + Shows thumbnails for a particular folder. - :param `parent`: parent window. Must not be ``None``; - :param `id`: window identifier. A value of -1 indicates a default value; - :param `pos`: the control position. A value of (-1, -1) indicates a default position, - chosen by either the windowing system or wxPython, depending on platform; - :param `size`: the control size. A value of (-1, -1) indicates a default size, - chosen by either the windowing system or wxPython, depending on platform; - :param `thumboutline`: outline style for :class:`ScrolledThumbnail`, which may be: - - =========================== ======= ================================== - Outline Flag Value Description - =========================== ======= ================================== - ``THUMB_OUTLINE_NONE`` 0 No outline is drawn on selection - ``THUMB_OUTLINE_FULL`` 1 Full outline (image+caption) is drawn on selection - ``THUMB_OUTLINE_RECT`` 2 Only thumbnail bounding rectangle is drawn on selection (default) - ``THUMB_OUTLINE_IMAGE`` 4 Only image bounding rectangle is drawn. - =========================== ======= ================================== - - :param `thumbfilter`: filter for image/video/audio files. Actually only - ``THUMB_FILTER_IMAGES`` is implemented; - :param `imagehandler`: can be :class:`PILImageHandler` if PIL is installed (faster), or - :class:`NativeImageHandler` which only uses wxPython image methods. + :param `folder`: a directory containing the images to thumbnail; """ - wx.ScrolledWindow.__init__(self, parent, id, pos, size) + self._dir = folder - self.SetThumbSize(96, 80) - self._tOutline = thumboutline - self._filter = thumbfilter - self._imageHandler = imagehandler() - self._selected = -1 - self._pointed = -1 - self._labelcontrol = None - self._pmenu = None - self._gpmenu = None - self._dragging = False - self._checktext = False - self._orient = THUMB_VERTICAL - self._dropShadow = True + self.RecreateComboBox(folder) - self._tCaptionHeight = [] - self._selectedarray = [] - self._tTextHeight = 16 - self._tCaptionBorder = 8 - self._tOutlineNotSelected = True - self._mouseeventhandled = False - self._highlight = False - self._zoomfactor = 1.4 - self.SetCaptionFont() - self._items = [] + # update items + thumbs = [] - self._enabletooltip = False + filenames = self.ListDirectory(self._dir, extensions) - self._parent = parent + filenames.sort() + for files in filenames: - self._selectioncolour = "#009EFF" - self.grayPen = wx.Pen("#A2A2D2", 1, wx.SHORT_DASH) - self.grayPen.SetJoin(wx.JOIN_MITER) - self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX)) + caption = (self._showfilenames and [files] or [""])[0] + fullfile = opj(self._dir + "/" + files) + if not os.path.isfile(fullfile): + continue - t, b, s = getShadow() - self.shadow = wx.MemoryDC() - self.shadow.SelectObject(s) + stats = os.stat(fullfile) + size = stats[6] - self.ShowFileNames(True) + lastmod = time.strftime(TIME_FMT, time.localtime(stats[8])) - self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_LEFT_UP, self.OnMouseUp) - self.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseDClick) - self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseDown) - self.Bind(wx.EVT_RIGHT_UP, self.OnMouseUp) - self.Bind(wx.EVT_MOTION, self.OnMouseMove) - self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) - self.Bind(EVT_THUMBNAILS_THUMB_CHANGED, self.OnThumbChanged) - self.Bind(wx.EVT_CHAR, self.OnChar) - self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) + thumbs.append(Thumb(folder, files, caption, size, lastmod, + imagehandler=self._imagehandler)) - self.Bind(wx.EVT_SIZE, self.OnResize) - self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x: None) - self.Bind(wx.EVT_PAINT, self.OnPaint) + return self.ShowThumbs(thumbs) - def GetSelectedItem(self, index): - """ - Returns the selected thumbnail. - - :param `index`: the thumbnail index (i.e., the selection). - """ - - return self.GetItem(self.GetSelection(index)) - - - def GetPointed(self): - """ Returns the pointed thumbnail index. """ - - return self._pointed - - - def GetHighlightPointed(self): - """ - Returns whether the thumbnail pointed should be highlighted or not. - - :note: Please be aware that this functionality may be slow on slower computers. - """ - - return self._highlight - - - def SetHighlightPointed(self, highlight=True): - """ - Sets whether the thumbnail pointed should be highlighted or not. - - :param `highlight`: ``True`` to enable highlight-on-point with the mouse, - ``False`` otherwise. - - :note: Please be aware that this functionality may be slow on slower computers. - """ - - self._highlight = highlight - - - def SetThumbOutline(self, outline): - """ - Sets the thumbnail outline style on selection. - - :param `outline`: the outline to use on selection. This can be one of the following - bits: - - =========================== ======= ================================== - Outline Flag Value Description - =========================== ======= ================================== - ``THUMB_OUTLINE_NONE`` 0 No outline is drawn on selection - ``THUMB_OUTLINE_FULL`` 1 Full outline (image+caption) is drawn on selection - ``THUMB_OUTLINE_RECT`` 2 Only thumbnail bounding rectangle is drawn on selection (default) - ``THUMB_OUTLINE_IMAGE`` 4 Only image bounding rectangle is drawn. - =========================== ======= ================================== - - """ - - if outline not in [THUMB_OUTLINE_NONE, THUMB_OUTLINE_FULL, THUMB_OUTLINE_RECT, - THUMB_OUTLINE_IMAGE]: - return - - self._tOutline = outline - - - def GetThumbOutline(self): - """ - Returns the thumbnail outline style on selection. - - :see: :meth:`~ScrolledThumbnail.SetThumbOutline` for a list of possible return values. - """ - - return self._tOutline - - - def SetDropShadow(self, drop): - """ - Sets whether to drop a shadow behind thumbnails or not. - - :param `drop`: ``True`` to drop a shadow behind each thumbnail, ``False`` otheriwise. - """ - - self._dropShadow = drop - self.Refresh() - - - def GetDropShadow(self): - """ - Returns whether to drop a shadow behind thumbnails or not. - """ - - return self._dropShadow - - - def GetPointedItem(self): - """ Returns the pointed thumbnail. """ - - return self.GetItem(self._pointed) - - - def GetItem(self, index): - """ - Returns the item at position `index`. - - :param `index`: the thumbnail index position. - """ - - return index >= 0 and (index < len(self._items) and [self._items[index]] or [None])[0] - - - def GetItemCount(self): - """ Returns the number of thumbnails. """ - - return len(self._items) - - - def SortItems(self): - """ Sorts the items accordingly to the :func:`~CmpThumb` function. """ - - self._items.sort(key=KeyThumb) - - - def GetThumbWidth(self): - """ Returns the thumbnail width. """ - - return self._tWidth - - - def GetThumbHeight(self): - """ Returns the thumbnail height. """ - - return self._tHeight - - - def GetThumbBorder(self): - """ Returns the thumbnail border. """ - - return self._tBorder - - - def GetCaption(self): - """ Returns the thumbnail caption. """ - - return self._caption - - - def SetLabelControl(self, statictext): - """ - Sets the thumbnail label as :class:`StaticText`. - - :param `statictext`: an instance of :class:`StaticText`. - """ - - self._labelcontrol = statictext - - - def ShowFileNames(self, show=True): - """ - Sets whether the user wants to show file names under the thumbnails or not. - - :param `show`: ``True`` to show file names under the thumbnails, ``False`` otherwise. - """ - - self._showfilenames = show - self.Refresh() - - - def SetOrientation(self, orient=THUMB_VERTICAL): - """ - Set the :class:`ThumbnailCtrl` orientation (partially implemented). - - :param `orient`: one of ``THUMB_VERTICAL``, ``THUMB_HORIZONTAL``. - - .. todo:: Correctly implement the ``THUMB_HORIZONTAL`` orientation. - """ - - self._orient = orient - - - def SetPopupMenu(self, menu): - """ - Sets the thumbnails popup menu when at least one thumbnail is selected. - - :param `menu`: an instance of :class:`wx.Menu`. - """ - - self._pmenu = menu - - - def GetPopupMenu(self): - """ Returns the thumbnails popup menu when at least one thumbnail is selected. """ - - return self._pmenu - - - def SetGlobalPopupMenu(self, gpmenu): - """ - Sets the global thumbnails popup menu (no need of thumbnail selection). - - :param `gpmenu`: an instance of :class:`wx.Menu`. - """ - - self._gpmenu = gpmenu - - - def GetGlobalPopupMenu(self): - """ Returns the global thumbnailss popup menu (no need of thumbnail selection). """ - - return self._gpmenu - - - def GetSelectionColour(self): - """ Returns the colour used to indicate a selected thumbnail. """ - - return self._selectioncolour - - - def SetSelectionColour(self, colour=None): - """ - Sets the colour used to indicate a selected thumbnail. - - :param `colour`: a valid :class:`wx.Colour` object. If defaulted to ``None``, it - will be taken from the system settings. - """ - - if colour is None: - colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) - - self._selectioncolour = colour - - - def EnableDragging(self, enable=True): - """ - Enables/disables thumbnails drag and drop. - - :param `enable`: ``True`` to enable drag and drop, ``False`` to disable it. - """ - - self._dragging = enable - - - def EnableToolTips(self, enable=True): - """ - Globally enables/disables thumbnail file information. - - :param `enable`: ``True`` to enable thumbnail file information, ``False`` to disable it. - """ - - self._enabletooltip = enable - - if not enable and hasattr(self, "_tipwindow"): - self._tipwindow.Enable(False) - - - def GetThumbInfo(self, thumb=-1): - """ - Returns the thumbnail information. - - :param `thumb`: the index of the thumbnail for which we are collecting information. - """ - - thumbinfo = None - - if thumb >= 0: - thumbinfo = "Name: " + self._items[thumb].GetFileName() + "\n" \ - "Size: " + self._items[thumb].GetFileSize() + "\n" \ - "Modified: " + self._items[thumb].GetCreationDate() + "\n" \ - "Dimensions: " + str(self._items[thumb].GetOriginalSize()) + "\n" \ - "Thumb: " + str(self.GetThumbSize()[0:2]) - - return thumbinfo - - - def SetThumbSize(self, width, height, border=6): - """ - Sets the thumbnail size as width, height and border. - - :param `width`: the desired thumbnail width; - :param `height`: the desired thumbnail height; - :param `border`: the spacing between thumbnails. - """ - - if width > 350 or height > 280: - return - - self._tWidth = width - self._tHeight = height - self._tBorder = border - self.SetScrollRate((self._tWidth + self._tBorder)/4, - (self._tHeight + self._tBorder)/4) - self.SetSizeHints(self._tWidth + self._tBorder*2 + 16, - self._tHeight + self._tBorder*2 + 8) - - - def GetThumbSize(self): - """ Returns the thumbnail size as width, height and border. """ - - return self._tWidth, self._tHeight, self._tBorder - - - def Clear(self): - """ Clears :class:`ThumbnailCtrl`. """ - - self._items = [] - self._selected = -1 - self._selectedarray = [] - self.UpdateProp() - self.Refresh() + def GetShowDir(self): + """ Returns the working directory with images. """ + return self._dir def ListDirectory(self, directory, fileExtList): """ @@ -1487,1016 +461,41 @@ class ScrolledThumbnail(wx.ScrolledWindow): return [f for f in os.listdir(directory) if lSplitExt(f)[1].lower() in fileExtList] - def ThreadImage(self, filenames): + def ShowFileNames(self, show=True): """ - Threaded method to load images. Used internally. + Sets whether the user wants to show file names under the thumbnails or not. - :param `filenames`: a Python list of file names containing images. + :param `show`: ``True`` to show file names under the thumbnails, ``False`` otherwise. """ - count = 0 + self._showfilenames = show + self._scrolled.ShowFileNames(show) - while count < len(filenames): - if not self._isrunning: - self._isrunning = False - thread.exit() - return + def OnThumbChar(self, ev): + for th in ev.thumbs: + thumb = self._scrolled.GetItem(th) - self.LoadImages(filenames[count], count) - if count < 4: - self.Refresh() - elif count%4 == 0: - self.Refresh() + if ev.keycode == wx.WXK_DELETE: + self.DeleteFiles(ev.thumbs) - count = count + 1 - - self._isrunning = False - thread.exit() - - - def LoadImages(self, newfile, imagecount): - """ - Threaded method to load images. Used internally. - - :param `newfile`: a file name containing an image to thumbnail; - :param `imagecount`: the number of images loaded until now. - """ - - if not self._isrunning: - thread.exit() - return - - img, originalsize, alpha = self._imageHandler.LoadThumbnail(newfile, (300, 240)) - try: - self._items[imagecount]._threadedimage = img - self._items[imagecount]._originalsize = originalsize - self._items[imagecount]._bitmap = img - self._items[imagecount]._alpha = alpha - except: - return - - - def ShowThumbs(self, thumbs, caption): - """ - Shows all the thumbnails. - - :param `thumbs`: should be a sequence with instances of :class:`Thumb`; - :param `caption`: the caption text for the current selected thumbnail. - """ - - self.SetCaption(caption) - - self._isrunning = False - - # update items - self._items = thumbs - myfiles = [thumb.GetFullFileName() for thumb in thumbs] - - items = self._items[:] - self._items.sort(key=KeyThumb) - - newfiles = SortFiles(items, self._items, myfiles) - self._isrunning = True - - thread.start_new_thread(self.ThreadImage, (newfiles,)) - wx.MilliSleep(20) - - self._selectedarray = [] - self.UpdateProp() - self.Refresh() - - - def ShowDir(self, folder, filter=THUMB_FILTER_IMAGES): - """ - Shows thumbnails for a particular folder. - - :param `folder`: a directory containing the images to thumbnail; - :param `filter`: filter images, video audio (currently implemented only for - images). - - .. todo:: Find a way to create thumbnails of video, audio and other formats. - """ - - self._dir = folder - if filter >= 0: - self._filter = filter - - self._parent.RecreateComboBox(folder) - - # update items - thumbs = [] - - filenames = self.ListDirectory(self._dir, extensions) - - for files in filenames: - - caption = (self._showfilenames and [files] or [""])[0] - fullfile = opj(self._dir + "/" + files) - if not os.path.isfile(fullfile): - continue - - stats = os.stat(fullfile) - size = stats[6] - - if size < 1000: - size = str(size) + " bytes" - elif size < 1000000: - size = str(int(round(size/1000.0))) + " Kb" - else: - size = str(round(size/1000000.0, 2)) + " Mb" - - lastmod = time.strftime(TIME_FMT, time.localtime(stats[8])) - - if self._filter & THUMB_FILTER_IMAGES: - thumbs.append(Thumb(self, folder, files, caption, size, lastmod)) - - return self.ShowThumbs(thumbs, caption=self._dir) - - - def GetShowDir(self): - """ Returns the working directory with images. """ - - return self._dir - - - def SetSelection(self, value=-1): - """ - Sets thumbnail selection. - - :param `value`: the thumbnail index to select. - """ - - self._selected = value - - if value != -1: - self._selectedarray = [value] - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - self.ScrollToSelected() - self.Refresh() - - - def SetZoomFactor(self, zoom=1.4): - """ - Sets the zoom factor. - - :param `zoom`: a floating point number representing the zoom factor. Must be - greater than or equal to 1.0. - """ - - if zoom <= 1.0: - raise Exception("\nERROR: Zoom Factor Must Be Greater Than 1.0") - - self._zoomfactor = zoom - - - def GetZoomFactor(self): - """ Returns the zoom factor. """ - - return self._zoomfactor - - - def IsAudioVideo(self, fname): - """ - Returns ``True`` if a file contains either audio or video data. - Currently unused as :class:`ThumbnailCtrl` recognizes only image files. - - :param `fname`: a file name. - - .. todo:: Find a way to create thumbnails of video, audio and other formats. - """ - - return os.path.splitext(fname)[1].lower() in \ - [".mpg", ".mpeg", ".vob"] - - - def IsVideo(self, fname): - """ - Returns ``True`` if a file contains video data. - Currently unused as :class:`ThumbnailCtrl` recognizes only image files. - - :param `fname`: a file name. - - .. todo:: Find a way to create thumbnails of video, audio and other formats. - """ - - return os.path.splitext(fname)[1].lower() in \ - [".m1v", ".m2v"] - - - def IsAudio(self, fname): - """ - Returns ``True`` if a file contains audio data. - Currently unused as :class:`ThumbnailCtrl` recognizes only image files. - - :param `fname`: a file name. - - .. todo:: Find a way to create thumbnails of video, audio and other formats. - """ - - return os.path.splitext(fname)[1].lower() in \ - [".mpa", ".mp2", ".mp3", ".ac3", ".dts", ".pcm"] - - - def UpdateItems(self): - """ Updates thumbnail items. """ - - selected = self._selectedarray - selectedfname = [] - selecteditemid = [] - - for ii in range(len(self._selectedarray)): - selectedfname.append(self.GetSelectedItem(ii).GetFileName()) - selecteditemid.append(self.GetSelectedItem(ii).GetId()) - - self.UpdateShow() - - if len(selected) > 0: - self._selectedarray = [] - for ii in range(len(self._items)): - for jj in range(len(selected)): - if self._items[ii].GetFileName() == selectedfname[jj] and \ - self._items[ii].GetId() == selecteditemid[jj]: - - self._selectedarray.append(ii) - if len(self._selectedarray) == 1: - self.ScrollToSelected() - - if len(self._selectedarray) > 0: - self.Refresh() - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - - def SetCaption(self, caption=""): - """ - Sets the current caption string. - - :param `caption`: the current caption string. - """ - - self._caption = caption - if self._labelcontrol: - - maxWidth = self._labelcontrol.GetSize().GetWidth()/8 - if len(caption) > maxWidth: - caption = "..." + caption[len(caption) + 3 - maxWidth] - - self._labelcontrol.SetLabel(caption) - - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_CAPTION_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - - def SetCaptionFont(self, font=None): - """ - Sets the font for all the thumbnail captions. - - :param `font`: a valid :class:`wx.Font` object. If defaulted to ``None``, a standard - font will be generated. - """ - - if font is None: - font = wx.Font(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False) - - self._captionfont = font - - - def GetCaptionFont(self): - """ Returns the font for all the thumbnail captions. """ - - return self._captionfont - - - def UpdateShow(self): - """ Updates thumbnail items. """ - - self.ShowThumbs(self._items) - - - def GetCaptionHeight(self, begRow, count=1): - """ - Returns the height for the file name caption. - - :param `begRow`: the caption line at which we start measuring the height; - :param `count`: the number of lines to measure. - """ - - capHeight = 0 - for ii in range(begRow, begRow + count): - if ii < len(self._tCaptionHeight): - capHeight = capHeight + self._tCaptionHeight[ii] - - return capHeight*self._tTextHeight - - - def GetItemIndex(self, x, y): - """ - Returns the thumbnail index at position (x, y). - - :param `x`: the mouse `x` position; - :param `y`: the mouse `y` position. - """ - - col = (x - self._tBorder)//(self._tWidth + self._tBorder) - - if col >= self._cols: - col = self._cols - 1 - - row = -1 - y = y - self._tBorder - - while y > 0: - - row = row + 1 - y = y - (self._tHeight + self._tBorder + self.GetCaptionHeight(row)) - - if row < 0: - row = 0 - - index = row*self._cols + col - - if index >= len(self._items): - index = -1 - - return index - - - def UpdateProp(self, checkSize=True): - """ - Updates :class:`ThumbnailCtrl` and its visible thumbnails. - - :param `checkSize`: ``True`` to update the items visibility if the window - size has changed. - """ - - width = self.GetClientSize().GetWidth() - self._cols = (width - self._tBorder)//(self._tWidth + self._tBorder) - - if self._cols <= 0: - self._cols = 1 - - tmpvar = (len(self._items)%self._cols and [1] or [0])[0] - self._rows = len(self._items)//self._cols + tmpvar - - self._tCaptionHeight = [] - - for row in range(self._rows): - - capHeight = 0 - - for col in range(self._cols): - - ii = row*self._cols + col - - if len(self._items) > ii and \ - self._items[ii].GetCaptionLinesCount(self._tWidth - self._tCaptionBorder) > capHeight: - - capHeight = self._items[ii].GetCaptionLinesCount(self._tWidth - self._tCaptionBorder) - - self._tCaptionHeight.append(capHeight) - - self.SetVirtualSize((self._cols*(self._tWidth + self._tBorder) + self._tBorder, - self._rows*(self._tHeight + self._tBorder) + \ - self.GetCaptionHeight(0, self._rows) + self._tBorder)) - - self.SetSizeHints(self._tWidth + 2*self._tBorder + 16, - self._tHeight + 2*self._tBorder + 8 + \ - (self._rows and [self.GetCaptionHeight(0)] or [0])[0]) - - if checkSize and width != self.GetClientSize().GetWidth(): - self.UpdateProp(False) - - - def InsertItem(self, thumb, pos): - """ - Inserts a thumbnail in the specified position. - - :param `pos`: the index at which we wish to insert the new thumbnail. - """ - - if pos < 0 or pos > len(self._items): - self._items.append(thumb) - else: - self._items.insert(pos, thumb) - - self.UpdateProp() - - - def RemoveItemAt(self, pos): - """ - Removes a thumbnail at the specified position. - - :param `pos`: the index at which we wish to remove the thumbnail. - """ - - del self._items[pos] - - self.UpdateProp() - - - def GetPaintRect(self): - """ Returns the paint bounding rect for the :meth:`~ScrolledThumbnail.OnPaint` method. """ - - size = self.GetClientSize() - paintRect = wx.Rect(0, 0, size.GetWidth(), size.GetHeight()) - paintRect.x, paintRect.y = self.GetViewStart() - xu, yu = self.GetScrollPixelsPerUnit() - paintRect.x = paintRect.x*xu - paintRect.y = paintRect.y*yu - - return paintRect - - - def IsSelected(self, indx): - """ - Returns whether a thumbnail is selected or not. - - :param `indx`: the index of the thumbnail to check for selection. - """ - - return self._selectedarray.count(indx) != 0 - - - def GetSelection(self, selIndex=-1): - """ - Returns the selected thumbnail. - - :param `selIndex`: if not equal to -1, the index of the selected thumbnail. - """ - - return (selIndex == -1 and [self._selected] or \ - [self._selectedarray[selIndex]])[0] - - - def GetOriginalImage(self, index=None): - """ - Returns the original image associated to a thumbnail. - - :param `index`: the index of the thumbnail. If defaulted to ``None``, the current - selection is used. - """ - - if index is None: - index = self.GetSelection() - - return self._items[index].GetOriginalImage() - - - def ScrollToSelected(self): - """ Scrolls the :class:`ScrolledWindow` to the selected thumbnail. """ - - if self.GetSelection() == -1: - return - - # get row - row = self.GetSelection()//self._cols - # calc position to scroll view - - paintRect = self.GetPaintRect() - y1 = row*(self._tHeight + self._tBorder) + self.GetCaptionHeight(0, row) - y2 = y1 + self._tBorder + self._tHeight + self.GetCaptionHeight(row) - - if y1 < paintRect.GetTop(): - sy = y1 # scroll top - elif y2 > paintRect.GetBottom(): - sy = y2 - paintRect.height # scroll bottom - else: - return - - # scroll view - xu, yu = self.GetScrollPixelsPerUnit() - sy = sy/yu + (sy%yu and [1] or [0])[0] # convert sy to scroll units - x, y = self.GetViewStart() - - self.Scroll(x,sy) - - - def CalculateBestCaption(self, dc, caption, sw, width): - """ - Calculates the best caption string to show based on the actual zoom factor. - - :param `dc`: an instance of :class:`wx.DC`; - :param `caption`: the original caption string; - :param `sw`: the maximum width allowed for the caption string, in pixels; - :param `width`: the caption string width, in pixels. - """ - - caption = caption + "..." - - while sw > width: - caption = caption[1:] - sw, sh = dc.GetTextExtent(caption) - - return "..." + caption[0:-3] - - - def DrawThumbnail(self, bmp, thumb, index): - """ - Draws a visible thumbnail. - - :param `bmp`: the thumbnail version of the original image; - :param `thumb`: an instance of :class:`Thumb`; - :param `index`: the index of the thumbnail to draw. - """ - - dc = wx.MemoryDC() - dc.SelectObject(bmp) - - x = self._tBorder/2 - y = self._tBorder/2 - - # background - dc.SetPen(wx.Pen(wx.BLACK, 0, wx.TRANSPARENT)) - dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID)) - dc.DrawRectangle(0, 0, bmp.GetWidth(), bmp.GetHeight()) - - # image - img = thumb.GetBitmap(self._tWidth, self._tHeight) - ww = img.GetWidth() - hh = img.GetHeight() - - if index == self.GetPointed() and self.GetHighlightPointed(): - factor = 1.5 - img = self._imageHandler.HighlightImage(img.ConvertToImage(), factor).ConvertToBitmap() - - imgRect = wx.Rect(x + (self._tWidth - img.GetWidth())/2, - y + (self._tHeight - img.GetHeight())/2, - img.GetWidth(), img.GetHeight()) - - if not thumb._alpha and self._dropShadow: - dc.Blit(imgRect.x+5, imgRect.y+5, imgRect.width, imgRect.height, self.shadow, 500-ww, 500-hh) - dc.DrawBitmap(img, imgRect.x, imgRect.y, True) - - colour = self.GetSelectionColour() - selected = self.IsSelected(index) - - colour = self.GetSelectionColour() - - # draw caption - sw, sh = 0, 0 - if self._showfilenames: - textWidth = 0 - dc.SetFont(self.GetCaptionFont()) - mycaption = thumb.GetCaption(0) - sw, sh = dc.GetTextExtent(mycaption) - - if sw > self._tWidth: - mycaption = self.CalculateBestCaption(dc, mycaption, sw, self._tWidth) - sw = self._tWidth - - textWidth = sw + 8 - tx = x + (self._tWidth - textWidth)/2 - ty = y + self._tHeight - - txtcolour = "#7D7D7D" - dc.SetTextForeground(txtcolour) - - tx = x + (self._tWidth - sw)/2 - if hh >= self._tHeight: - ty = y + self._tHeight + (self._tTextHeight - sh)/2 + 3 - else: - ty = y + hh + (self._tHeight-hh)/2 + (self._tTextHeight - sh)/2 + 3 - - dc.DrawText(mycaption, tx, ty) - - # outline - if self._tOutline != THUMB_OUTLINE_NONE and (self._tOutlineNotSelected or self.IsSelected(index)): - - dotrect = wx.Rect() - dotrect.x = x - 2 - dotrect.y = y - 2 - dotrect.width = bmp.GetWidth() - self._tBorder + 4 - dotrect.height = bmp.GetHeight() - self._tBorder + 4 - - dc.SetPen(wx.Pen((self.IsSelected(index) and [colour] or [wx.LIGHT_GREY])[0], - 0, wx.PENSTYLE_SOLID)) - dc.SetBrush(wx.Brush(wx.BLACK, wx.BRUSHSTYLE_TRANSPARENT)) - - if self._tOutline == THUMB_OUTLINE_FULL or self._tOutline == THUMB_OUTLINE_RECT: - - imgRect.x = x - imgRect.y = y - imgRect.width = bmp.GetWidth() - self._tBorder - imgRect.height = bmp.GetHeight() - self._tBorder - - if self._tOutline == THUMB_OUTLINE_RECT: - imgRect.height = self._tHeight - - dc.SetBrush(wx.TRANSPARENT_BRUSH) - - if selected: - - dc.SetPen(self.grayPen) - dc.DrawRoundedRectangle(dotrect, 2) - - dc.SetPen(wx.Pen(wx.WHITE)) - dc.DrawRectangle(imgRect.x, imgRect.y, - imgRect.width, imgRect.height) - - pen = wx.Pen((selected and [colour] or [wx.LIGHT_GREY])[0], 2) - pen.SetJoin(wx.JOIN_MITER) - dc.SetPen(pen) - if self._tOutline == THUMB_OUTLINE_FULL: - dc.DrawRoundedRectangle(imgRect.x - 1, imgRect.y - 1, - imgRect.width + 3, imgRect.height + 3, 2) - else: - dc.DrawRectangle(imgRect.x - 1, imgRect.y - 1, - imgRect.width + 3, imgRect.height + 3) - else: - dc.SetPen(wx.Pen(wx.LIGHT_GREY)) - - dc.DrawRectangle(imgRect.x - 1, imgRect.y - 1, - imgRect.width + 2, imgRect.height + 2) - - - dc.SelectObject(wx.NullBitmap) - - - def OnPaint(self, event): - """ - Handles the ``wx.EVT_PAINT`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`PaintEvent` event to be processed. - """ - - paintRect = self.GetPaintRect() - - dc = wx.BufferedPaintDC(self) - self.PrepareDC(dc) - - dc.SetPen(wx.Pen(wx.BLACK, 0, wx.PENSTYLE_TRANSPARENT)) - dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID)) - - w, h = self.GetClientSize() - - # items - row = -1 - xwhite = self._tBorder - - for ii in range(len(self._items)): - - col = ii%self._cols - if col == 0: - row = row + 1 - - xwhite = ((w - self._cols*(self._tWidth + self._tBorder)))/(self._cols+1) - tx = xwhite + col*(self._tWidth + self._tBorder) - - ty = self._tBorder/2 + row*(self._tHeight + self._tBorder) + \ - self.GetCaptionHeight(0, row) - tw = self._tWidth + self._tBorder - th = self._tHeight + self.GetCaptionHeight(row) + self._tBorder - # visible? - if not paintRect.Intersects(wx.Rect(tx, ty, tw, th)): - continue - - thmb = wx.Bitmap(tw, th) - self.DrawThumbnail(thmb, self._items[ii], ii) - dc.DrawBitmap(thmb, tx, ty) - - rect = wx.Rect(xwhite, self._tBorder/2, - self._cols*(self._tWidth + self._tBorder), - self._rows*(self._tHeight + self._tBorder) + \ - self.GetCaptionHeight(0, self._rows)) - - w = max(self.GetClientSize().GetWidth(), rect.width) - h = max(self.GetClientSize().GetHeight(), rect.height) - dc.DrawRectangle(0, 0, w, rect.y) - dc.DrawRectangle(0, 0, rect.x, h) - dc.DrawRectangle(rect.GetRight(), 0, w - rect.GetRight(), h + 50) - dc.DrawRectangle(0, rect.GetBottom(), w, h - rect.GetBottom() + 50) - - col = len(self._items)%self._cols - - if col > 0: - rect.x = rect.x + col*(self._tWidth + self._tBorder) - rect.y = rect.y + (self._rows - 1)*(self._tHeight + self._tBorder) + \ - self.GetCaptionHeight(0, self._rows - 1) - dc.DrawRectangle(rect) - - - def OnResize(self, event): - """ - Handles the ``wx.EVT_SIZE`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`wx.SizeEvent` event to be processed. - """ - - self.UpdateProp() - self.ScrollToSelected() - self.Refresh() - - - def OnMouseDown(self, event): - """ - Handles the ``wx.EVT_LEFT_DOWN`` and ``wx.EVT_RIGHT_DOWN`` events for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - """ - - x = event.GetX() - y = event.GetY() - x, y = self.CalcUnscrolledPosition(x, y) - # get item number to select - lastselected = self._selected - self._selected = self.GetItemIndex(x, y) - - self._mouseeventhandled = False - update = False - - if event.ControlDown(): - if self._selected == -1: - self._mouseeventhandled = True - elif not self.IsSelected(self._selected): - self._selectedarray.append(self._selected) - update = True - self._mouseeventhandled = True - - elif event.ShiftDown(): - if self._selected != -1: - begindex = self._selected - endindex = lastselected - if lastselected < self._selected: - begindex = lastselected - endindex = self._selected - self._selectedarray = [] - - for ii in range(begindex, endindex+1): - self._selectedarray.append(ii) - - update = True - - self._selected = lastselected - self._mouseeventhandled = True - - else: - - if self._selected == -1: - update = len(self._selectedarray) > 0 - self._selectedarray = [] - self._mouseeventhandled = True - elif len(self._selectedarray) <= 1: - try: - update = len(self._selectedarray)== 0 or self._selectedarray[0] != self._selected - except: - update = True - self._selectedarray = [] - self._selectedarray.append(self._selected) - self._mouseeventhandled = True - - if update: - self.ScrollToSelected() - self.Refresh() - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - self.SetFocus() - - - def OnMouseUp(self, event): - """ - Handles the ``wx.EVT_LEFT_UP`` and ``wx.EVT_RIGHT_UP`` events for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - """ - - # get item number to select - x = event.GetX() - y = event.GetY() - x, y = self.CalcUnscrolledPosition(x, y) - lastselected = self._selected - self._selected = self.GetItemIndex(x,y) - - if not self._mouseeventhandled: - # set new selection - if event.ControlDown(): - if self._selected in self._selectedarray: - self._selectedarray.remove(self._selected) - - self._selected = -1 - else: - self._selectedarray = [] - self._selectedarray.append(self._selected) - - self.ScrollToSelected() - self.Refresh() - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_SEL_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - # Popup menu - if event.RightUp(): - if self._selected >= 0 and self._pmenu: - self.PopupMenu(self._pmenu) - elif self._selected >= 0 and not self._pmenu and self._gpmenu: - self.PopupMenu(self._gpmenu) - elif self._selected == -1 and self._gpmenu: - self.PopupMenu(self._gpmenu) - - if event.ShiftDown(): - self._selected = lastselected - - - def OnMouseDClick(self, event): - """ - Handles the ``wx.EVT_LEFT_DCLICK`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - """ - - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_DCLICK, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - - def OnMouseMove(self, event): - """ - Handles the ``wx.EVT_MOTION`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - """ - - # -- drag & drop -- - if self._dragging and event.Dragging() and len(self._selectedarray) > 0: - - files = wx.FileDataObject() - for ii in range(len(self._selectedarray)): - files.AddFile(opj(self.GetSelectedItem(ii).GetFullFileName())) - - source = wx.DropSource(self) - source.SetData(files) - source.DoDragDrop(wx.Drag_DefaultMove) - - # -- light-effect -- - x = event.GetX() - y = event.GetY() - x, y = self.CalcUnscrolledPosition(x, y) - - # get item number - sel = self.GetItemIndex(x, y) - - if sel == self._pointed: - if self._enabletooltip and sel >= 0: - if not hasattr(self, "_tipwindow"): - self._tipwindow = wx.ToolTip(self.GetThumbInfo(sel)) - self._tipwindow.SetDelay(1000) - self.SetToolTip(self._tipwindow) - else: - self._tipwindow.SetDelay(1000) - self._tipwindow.SetTip(self.GetThumbInfo(sel)) - - event.Skip() - return - - if self._enabletooltip: - if hasattr(self, "_tipwindow"): - self._tipwindow.Enable(False) - - # update thumbnail - self._pointed = sel - - if self._enabletooltip and sel >= 0: - if not hasattr(self, "_tipwindow"): - self._tipwindow = wx.ToolTip(self.GetThumbInfo(sel)) - self._tipwindow.SetDelay(1000) - self._tipwindow.Enable(True) - self.SetToolTip(self._tipwindow) - else: - self._tipwindow.SetDelay(1000) - self._tipwindow.Enable(True) - self._tipwindow.SetTip(self.GetThumbInfo(sel)) - - self.Refresh() - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_POINTED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - event.Skip() - - - def OnMouseLeave(self, event): - """ - Handles the ``wx.EVT_LEAVE_WINDOW`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - """ - - if self._pointed != -1: - - self._pointed = -1 - self.Refresh() - eventOut = ThumbnailEvent(wxEVT_THUMBNAILS_POINTED, self.GetId()) - self.GetEventHandler().ProcessEvent(eventOut) - - - def OnThumbChanged(self, event): - """ - Handles the ``EVT_THUMBNAILS_THUMB_CHANGED`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`ThumbnailEvent` event to be processed. - """ - - for ii in range(len(self._items)): - if self._items[ii].GetFileName() == event.GetString(): - - self._items[ii].SetFilename(self._items[ii].GetFileName()) - if event.GetClientData(): - - img = wx.Image(event.GetClientData()) - self._items[ii].SetImage(img) - - self.Refresh() - - - def OnChar(self, event): - """ - Handles the ``wx.EVT_CHAR`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`KeyEvent` event to be processed. - - :note: You have these choices: - - (1) ``d`` key rotates 90 degrees clockwise the selected thumbnails; - (2) ``s`` key rotates 90 degrees counter-clockwise the selected thumbnails; - (3) ``a`` key rotates 180 degrees the selected thumbnails; - (4) ``Del`` key deletes the selected thumbnails; - (5) ``+`` key zooms in; - (6) ``-`` key zooms out. - """ - - if event.KeyCode == ord("s"): - self.Rotate() - elif event.KeyCode == ord("d"): - self.Rotate(270) - elif event.KeyCode == ord("a"): - self.Rotate(180) - elif event.KeyCode == wx.WXK_DELETE: - self.DeleteFiles() - elif event.KeyCode in [wx.WXK_ADD, wx.WXK_NUMPAD_ADD]: - self.ZoomIn() - elif event.KeyCode in [wx.WXK_SUBTRACT, wx.WXK_NUMPAD_SUBTRACT]: - self.ZoomOut() - - event.Skip() - - - def Rotate(self, angle=90): - """ - Rotates the selected thumbnails by the angle specified by `angle`. - - :param `angle`: the rotation angle for the thumbnail, in degrees. - """ - - wx.BeginBusyCursor() - - count = 0 - selected = [] - - for ii in range(len(self._items)): - if self.IsSelected(ii): - selected.append(self._items[ii]) - - dlg = wx.ProgressDialog("Thumbnail Rotation", - "Rotating Thumbnail... Please Wait", - maximum = len(selected)+1, - parent=None) - - for thumb in selected: - count = count + 1 - if TN_USE_PIL: - newangle = thumb.GetRotation()*180/pi + angle - fil = opj(thumb.GetFullFileName()) - with Image.open(fil) as fid: - pil = fid.rotate(newangle) - img = wx.Image(pil.size[0], pil.size[1]) - img.SetData(pil.convert('RGB').tobytes()) - thumb.SetRotation(newangle*pi/180) - else: - img = thumb._threadedimage - newangle = thumb.GetRotation() + angle*pi/180 - thumb.SetRotation(newangle) - img = img.Rotate(newangle, (img.GetWidth()/2, img.GetHeight()/2), True) - - thumb.SetRotatedImage(img) - dlg.Update(count) - - wx.EndBusyCursor() - dlg.Destroy() - - if self.GetSelection() != -1: - self.Refresh() - - - def DeleteFiles(self): + def DeleteFiles(self, thumbs): """ Deletes the selected thumbnails and their associated files. - .. warning:: This method deletes the original files too. + + :parem `thumbs`: List of indexs to thumbnails. + """ - dlg = wx.MessageDialog(self, 'Are you sure you want to delete the files?', - 'Confirmation', - wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + msg = "" + for index in thumbs: + thumb = self._scrolled.GetItem(index) + msg += thumb.GetFileName() + '\n' - if dlg.ShowModal() == wx.ID_YES: + dlg = ScrolledTextDialog(self, 'Delete these files?', msg) + + if dlg.ShowModal() == wx.ID_OK: errordelete = [] count = 0 @@ -2504,16 +503,16 @@ class ScrolledThumbnail(wx.ScrolledWindow): wx.BeginBusyCursor() - for ii in range(len(self._items)): - if self.IsSelected(ii): - thumb = self._items[ii] - files = self._items[ii].GetFullFileName() - filename = opj(files) - try: - os.remove(filename) - count = count + 1 - except: - errordelete.append(files) + # Delete thumbnails & files from highest index to lowest + for index in reversed(thumbs): + thumb = self._scrolled.GetItem(index) + filename = thumb.GetFullFileName() + try: + os.remove(filename) + self._scrolled.RemoveItemAt(index) + count = count + 1 + except: + errordelete.append(self._scrolled.GetFileName(index)) wx.EndBusyCursor() @@ -2528,71 +527,3 @@ class ScrolledThumbnail(wx.ScrolledWindow): wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() - - if count: - self.UpdateShow() - - - def OnMouseWheel(self, event): - """ - Handles the ``wx.EVT_MOUSEWHEEL`` event for :class:`ThumbnailCtrl`. - - :param `event`: a :class:`MouseEvent` event to be processed. - - :note: If you hold down the ``Ctrl`` key, you can zoom in/out with the mouse wheel. - """ - - if event.ControlDown(): - if event.GetWheelRotation() > 0: - self.ZoomIn() - else: - self.ZoomOut() - else: - event.Skip() - - - def ZoomOut(self): - """ Zooms the thumbnails out. """ - - w, h, b = self.GetThumbSize() - - if w < 40 or h < 40: - return - - zoom = self.GetZoomFactor() - neww = float(w)/zoom - newh = float(h)/zoom - - self.SetThumbSize(int(neww), int(newh)) - self.OnResize(None) - self._checktext = True - - self.Refresh() - - - def ZoomIn(self): - """ Zooms the thumbnails in. """ - - size = self.GetClientSize() - w, h, b = self.GetThumbSize() - zoom = self.GetZoomFactor() - - if w*zoom + b > size.GetWidth() or h*zoom + b > size.GetHeight(): - if w*zoom + b > size.GetWidth(): - neww = size.GetWidth() - 2*self._tBorder - newh = (float(h)/w)*neww - else: - newh = size.GetHeight() - 2*self._tBorder - neww = (float(w)/h)*newh - - else: - neww = float(w)*zoom - newh = float(h)*zoom - - self.SetThumbSize(int(neww), int(newh)) - self.OnResize(None) - self._checktext = True - - self.Refresh() - - From 0a03c37a261937d68b2fc4e666ace7b191668121 Mon Sep 17 00:00:00 2001 From: eager Date: Wed, 21 Oct 2020 13:31:19 -0700 Subject: [PATCH 02/10] Fixes from code review. --- wx/lib/agw/scrolledthumbnail.py | 6 +++--- wx/lib/agw/thumbnailctrl.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wx/lib/agw/scrolledthumbnail.py b/wx/lib/agw/scrolledthumbnail.py index 340412d9..d10b0ac0 100644 --- a/wx/lib/agw/scrolledthumbnail.py +++ b/wx/lib/agw/scrolledthumbnail.py @@ -1984,9 +1984,9 @@ class ScrolledThumbnail(wx.ScrolledWindow): (1) ``d`` key rotates 90 degrees clockwise the selected thumbnails; (2) ``s`` key rotates 90 degrees counter-clockwise the selected thumbnails; (3) ``a`` key rotates 180 degrees the selected thumbnails; - (4) ``Del`` key deletes the selected thumbnails; - (5) ``+`` key zooms in; - (6) ``-`` key zooms out. + (4) ``+`` key zooms in; + (5) ``-`` key zooms out. + All other keys cause an EVT_THUMBNAILS_CHAR event to be thrown. """ if event.KeyCode == ord("s"): diff --git a/wx/lib/agw/thumbnailctrl.py b/wx/lib/agw/thumbnailctrl.py index 97ef39e3..53a8856f 100644 --- a/wx/lib/agw/thumbnailctrl.py +++ b/wx/lib/agw/thumbnailctrl.py @@ -153,8 +153,8 @@ import wx import os import time -from wx.lib.agw.scrolledthumbnail import ScrolledThumbnail, EVT_THUMBNAILS_CHAR, \ -PILImageHandler, NativeImageHandler, Thumb +from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, EVT_THUMBNAILS_CHAR, +PILImageHandler, NativeImageHandler, Thumb) # Image File Name Extensions: Am I Missing Some Extensions Here? extensions = [".jpeg", ".jpg", ".bmp", ".png", ".ico", ".tiff", ".ani", ".cur", ".gif", From f511c293cc22255ef50e02fb76fcac55e4c30ad8 Mon Sep 17 00:00:00 2001 From: eagerm Date: Fri, 23 Oct 2020 15:33:00 -0700 Subject: [PATCH 03/10] Add ScrolledThumbnail demo New: demo/agw/ScrolledThumbnail.py --- demo/agw/ScrolledThumbnail.py | 640 ++++++++++++++++++++++++++++++++++ demo/agw/ThumbnailCtrl.py | 4 +- demo/agw/__demo__.py | 3 +- demo/demodata.py | 1 + 4 files changed, 645 insertions(+), 3 deletions(-) create mode 100644 demo/agw/ScrolledThumbnail.py diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py new file mode 100644 index 00000000..4c5e03fe --- /dev/null +++ b/demo/agw/ScrolledThumbnail.py @@ -0,0 +1,640 @@ +#!/usr/bin/env python + +# ScrolledThumbnail Demo Michael Eager @ 2020 Oct 23 +# Adapted from ThumbnailCtrl Demo Andrea Gavana @ 10 Dec 2005 + +import wx +import os +import sys +import time +import images +from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, + Thumb, + NativeImageHandler, + EVT_THUMBNAILS_SEL_CHANGED, + EVT_THUMBNAILS_POINTED, + EVT_THUMBNAILS_DCLICK) +try: + dirName = os.path.dirname(os.path.abspath(__file__)) +except: + dirName = os.path.dirname(os.path.abspath(sys.argv[0])) + +sys.path.append(os.path.split(dirName)[0]) + +class ScrolledThumbnailDemo(wx.Frame): + + def __init__(self, parent, log): + + wx.Frame.__init__(self, parent) + + self.SetIcon(images.Mondrian.GetIcon()) + self.SetTitle("ScrolledThumbnail wxPython Demo ;-)") + + self.statusbar = self.CreateStatusBar(2) + self.statusbar.SetStatusWidths([-2, -1]) + # statusbar fields + statusbar_fields = [("ScrolledThumbnail Demo, Michael Eager @ 23 Oct 2020"), + ("Welcome To wxPython!")] + + for i in range(len(statusbar_fields)): + self.statusbar.SetStatusText(statusbar_fields[i], i) + + self.SetMenuBar(self.CreateMenuBar()) + + splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | + wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) + self.panel = wx.Panel(splitter, -1) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + self.scroll = ScrolledThumbnail(splitter, -1, size=(400,300)) + + self.log = log + + self.thumbsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Style") + self.customsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Customization") + self.optionsizer_staticbox = wx.StaticBox(self.panel, -1, "More Options") + self.dirsizer_staticbox = wx.StaticBox(self.panel, -1, "Directory Selection") + self.dirbutton = wx.Button(self.panel, -1, "Change Directory") + self.radiostyle1 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_NONE", style=wx.RB_GROUP) + self.radiostyle2 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_FULL") + self.radiostyle3 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_RECT") + self.radiostyle4 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_IMAGE") + self.highlight = wx.CheckBox(self.panel, -1, "Highlight on pointing") + self.showfiles = wx.CheckBox(self.panel, -1, "Show file names") + self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") + self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") + self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") + self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") + self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") + self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") + self.textthumbwidth = wx.TextCtrl(self.panel, -1, "96") + self.textthumbheight = wx.TextCtrl(self.panel, -1, "80") + self.thumbsizebutton = wx.Button(self.panel, -1, "Set thumbnail size (WxH)") + self.fontbutton = wx.Button(self.panel, -1, "Set caption font") + self.colourbutton = wx.Button(self.panel, -1, "Set selection colour") + + self.radios = [self.radiostyle1, self.radiostyle2, self.radiostyle3, + self.radiostyle4] + self.thumbstyles = ["THUMB_OUTLINE_NONE", "THUMB_OUTLINE_FULL", "THUMB_OUTLINE_RECT", + "THUMB_OUTLINE_IMAGE"] + + self.SetProperties() + self.DoLayout() + + self.panel.SetSizer(sizer) + sizer.Layout() + + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle4) + self.Bind(wx.EVT_CHECKBOX, self.OnHighlight, self.highlight) + self.Bind(wx.EVT_CHECKBOX, self.OnShowFiles, self.showfiles) + self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) + self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) + self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) + self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) + self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) + self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) + self.Bind(wx.EVT_BUTTON, self.OnSetFont, self.fontbutton) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) + self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) + + self.scroll.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) + self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) + self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) + + splitter.SplitVertically(self.scroll, self.panel, 400) + + splitter.SetMinimumPaneSize(140) + self.SetMinSize((1000, 1000)) + self.CenterOnScreen() + + def SetProperties(self): + + self.radiostyle4.SetValue(1) + self.showfiles.SetValue(1) + boldFont = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "") + self.zoombutton.SetFont(boldFont) + self.fontbutton.SetFont(boldFont) + self.dirbutton.SetFont(boldFont) + self.colourbutton.SetFont(boldFont) + + + def DoLayout(self): + + splitsizer = wx.BoxSizer(wx.VERTICAL) + optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) + zoomsizer = wx.BoxSizer(wx.HORIZONTAL) + thumbsizesizer = wx.BoxSizer(wx.HORIZONTAL) + customsizer = wx.StaticBoxSizer(self.customsizer_staticbox, wx.VERTICAL) + thumbsizer = wx.StaticBoxSizer(self.thumbsizer_staticbox, wx.VERTICAL) + radiosizer = wx.BoxSizer(wx.VERTICAL) + dirsizer = wx.StaticBoxSizer(self.dirsizer_staticbox, wx.HORIZONTAL) + dirsizer.Add(self.dirbutton, 0, wx.LEFT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(dirsizer, 0, wx.EXPAND|wx.TOP|wx.LEFT, 5) + radiosizer.Add(self.radiostyle1, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle2, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle3, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle4, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + thumbsizer.Add(radiosizer, 1, wx.EXPAND, 0) + splitsizer.Add(thumbsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + customsizer.Add(self.highlight, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.showfiles, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbheight, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.thumbsizebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + optionsizer.Add(zoomsizer, 1, wx.EXPAND, 0) + optionsizer.Add(thumbsizesizer, 1, wx.EXPAND, 0) + optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) + optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) + self.panel.SetAutoLayout(True) + self.panel.SetSizer(splitsizer) + splitsizer.Fit(self.panel) + + + def CreateMenuBar(self): + + file_menu = wx.Menu() + + AS_EXIT = wx.NewIdRef() + file_menu.Append(AS_EXIT, "&Exit") + self.Bind(wx.EVT_MENU, self.OnClose, id=AS_EXIT) + + help_menu = wx.Menu() + + AS_ABOUT = wx.NewIdRef() + help_menu.Append(AS_ABOUT, "&About...") + self.Bind(wx.EVT_MENU, self.OnAbout, id=AS_ABOUT) + + menu_bar = wx.MenuBar() + + menu_bar.Append(file_menu, "&File") + menu_bar.Append(help_menu, "&Help") + + return menu_bar + + + def OnClose(self, event): + + self.Destroy() + + + def OnAbout(self, event): + + msg = "This Is The About Dialog Of The ScrollThumbnail Demo.\n\n" + \ + "Author: Michael Eager @ 23 Oct 2020\n\n" + \ + "Adapted from the ThumbnailCtrl Demo\n" + \ + "By Andrea Gavana @ 10 Dec 2005\n\n" + \ + "Please Report Any Bug/Requests Of Improvements\n" + \ + "To Me At The Following Addresses:\n" + \ + "eager@eagercon.com\n\n" + \ + "Welcome To wxPython " + wx.VERSION_STRING + "!!" + + dlg = wx.MessageDialog(self, msg, "ScrolledThumbnail Demo", + wx.OK | wx.ICON_INFORMATION) + + dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) + dlg.ShowModal() + dlg.Destroy() + + + def OnSetDirectory(self, event): + + dlg = wx.DirDialog(self, "Choose a directory with images:", + defaultPath=os.getcwd(), + style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) + + # If the user selects OK, then we process the dialog's data. + # This is done by getting the path data from the dialog - BEFORE + # we destroy it. + if dlg.ShowModal() == wx.ID_OK: + self.ShowDir(dlg.GetPath()) + self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) + + # Only destroy a dialog after you're done with it. + dlg.Destroy() + + + def ShowDir(self, dir): + files = os.listdir(dir) + thumbs = [] + for f in files: + if os.path.splitext(f)[1] in [".jpg", ".gif", ".png"]: + statinfo = os.stat(os.path.join(dir, f)) + size = statinfo.st_size + modtime = statinfo.st_mtime + TIME_FMT = '%d %b %Y, %H:%M:%S' + lastmod = time.strftime(TIME_FMT, time.localtime(modtime)) + thumbs.append(Thumb(dir, f, caption=f, size=size, lastmod=lastmod, + imagehandler=NativeImageHandler)) + self.scroll.ShowThumbs(thumbs) + + + def OnChangeOutline(self, event): # wxGlade: MyFrame. + + radio = event.GetEventObject() + pos = self.radios.index(radio) + + if pos == 0: + self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_NONE) + elif pos == 1: + self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_FULL) + elif pos == 2: + self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_RECT) + elif pos == 3: + self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_IMAGE) + + self.scroll.Refresh() + + self.log.write("OnChangeOutline: Outline changed to: %s\n"%self.thumbstyles[pos]) + event.Skip() + + + def OnHighlight(self, event): # wxGlade: MyFrame. + + if self.highlight.GetValue() == 1: + self.scroll.SetHighlightPointed(True) + self.log.write("OnHighlight: Highlight thumbs on pointing\n") + else: + self.scroll.SetHighlightPointed(False) + self.log.write("OnHighlight: Don't Highlight thumbs on pointing\n") + + event.Skip() + + + def OnShowFiles(self, event): # wxGlade: MyFrame. + + if self.showfiles.GetValue() == 1: + self.scroll.ShowFileNames(True) + self.log.write("OnShowFiles: Thumbs file names shown\n") + else: + self.scroll.ShowFileNames(False) + self.log.write("OnShowFiles: Thumbs file names not shown\n") + + self.scroll.Refresh() + + event.Skip() + + + def OnEnableDragging(self, event): + + if self.enabledragging.GetValue() == 1: + self.scroll.EnableDragging(True) + self.log.write("OnEnableDragging: Thumbs drag and drop enabled\n") + else: + self.scroll.EnableDragging(False) + self.log.write("OnEnableDragging: Thumbs drag and drop disabled\n") + + self.scroll.Refresh() + + event.Skip() + + + def OnSetPopup(self, event): # wxGlade: MyFrame. + + if self.setpopup.GetValue() == 1: + menu = self.CreatePopups() + self.scroll.SetPopupMenu(menu) + self.log.write("OnSetPopup: Popups enabled on thumbs\n") + else: + self.scroll.SetPopupMenu(None) + self.log.write("OnSetPopup: Popups disabled on thumbs\n") + + event.Skip() + + + def OnSetGlobalPopup(self, event): + + if self.setgpopup.GetValue() == 1: + menu = self.CreateGlobalPopups() + self.scroll.SetGlobalPopupMenu(menu) + self.log.write("OnSetGlobalPopup: Popups enabled globally (no selection needed)\n") + else: + self.scroll.SetGlobalPopupMenu(None) + self.log.write("OnSetGlobalPopup: Popups disabled globally (no selection needed)\n") + + event.Skip() + + + def OnEnableToolTips(self, event): + + if self.enabletooltip.GetValue() == 1: + self.log.write("OnEnableToolTips: File information on tooltips enabled\n") + self.scroll.EnableToolTips(True) + else: + self.log.write("OnEnableToolTips: File information on tooltips disabled\n") + self.scroll.EnableToolTips(False) + + event.Skip() + + + def OnSetZoom(self, event): # wxGlade: MyFrame. + + val = self.textzoom.GetValue().strip() + + try: + val = float(val) + except: + errstr = "Error: a float value is required." + dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + self.textzoom.SetValue("1.4") + return + + + if val < 1.0: + errstr = "Error: zoom factor must be grater than 1.0." + dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + self.textzoom.SetValue("1.4") + return + + self.scroll.SetZoomFactor(val) + + event.Skip() + + def OnSetThumbSize(self, event): + try: + width = int(self.textthumbwidth.GetValue().strip()) + height = int(self.textthumbheight.GetValue().strip()) + except: + errstr = "Error: thumb size must be integers (min 50x50)." + dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return + + width = max(width, 50) + height = max(height, 50) + self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) + self.scroll.SetThumbSize (width, height) + + event.Skip() + + def OnSelChanged(self, event): + + self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.scroll.GetSelection())) + event.Skip() + + + def OnPointed(self, event): + + self.log.write("OnPointed: Thumb pointed: %s\n"%self.scroll.GetPointed()) + event.Skip() + + + def OnDClick(self, event): + + self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.scroll.GetSelection()) + event.Skip() + + + def OnSetFont(self, event): # wxGlade: MyFrame. + + data = wx.FontData() + data.EnableEffects(True) + data.SetInitialFont(self.scroll.GetCaptionFont()) + + dlg = wx.FontDialog(self, data) + + if dlg.ShowModal() == wx.ID_OK: + data = dlg.GetFontData() + font = data.GetChosenFont() + self.scroll.SetCaptionFont(font) + self.scroll.Refresh() + self.log.write("OnSetFont: Caption font changed\n") + + # Don't destroy the dialog until you get everything you need from the + # dialog! + dlg.Destroy() + event.Skip() + + + def OnSetColour(self, event): + + dlg = wx.ColourDialog(self) + + # Ensure the full colour dialog is displayed, + # not the abbreviated version. + dlg.GetColourData().SetChooseFull(True) + + if dlg.ShowModal() == wx.ID_OK: + + # If the user selected OK, then the dialog's wx.ColourData will + # contain valid information. Fetch the data ... + data = dlg.GetColourData() + + # ... then do something with it. The actual colour data will be + # returned as a three-tuple (r, g, b) in this particular case. + colour = data.GetColour().Get() + colour = wx.Colour(colour[0], colour[1], colour[2]) + self.scroll.SetSelectionColour(colour) + self.scroll.Refresh() + + # Once the dialog is destroyed, Mr. wx.ColourData is no longer your + # friend. Don't use it again! + dlg.Destroy() + + + def CreatePopups(self): + + if not hasattr(self, "popupID1"): + self.popupID1 = wx.NewIdRef() + self.popupID2 = wx.NewIdRef() + self.popupID3 = wx.NewIdRef() + self.popupID4 = wx.NewIdRef() + self.popupID5 = wx.NewIdRef() + self.popupID6 = wx.NewIdRef() + self.popupID7 = wx.NewIdRef() + self.popupID8 = wx.NewIdRef() + self.popupID9 = wx.NewIdRef() + self.popupID10 = wx.NewIdRef() + self.popupID11 = wx.NewIdRef() + self.popupID12 = wx.NewIdRef() + + self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1) + self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2) + self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3) + self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4) + self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5) + self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6) + self.Bind(wx.EVT_MENU, self.OnPopupSeven, id=self.popupID7) + self.Bind(wx.EVT_MENU, self.OnPopupEight, id=self.popupID8) + self.Bind(wx.EVT_MENU, self.OnPopupNine, id=self.popupID9) + + menu = wx.Menu() + item = wx.MenuItem(menu, self.popupID1, "One") + img = images.Mondrian.GetImage() + img.Rescale(16, 16) + bmp = img.ConvertToBitmap() + item.SetBitmap(bmp) + menu.Append(item) + + # add some other items + menu.Append(self.popupID2, "Two") + menu.Append(self.popupID3, "Three") + menu.Append(self.popupID4, "Four") + menu.Append(self.popupID5, "Five") + menu.Append(self.popupID6, "Six") + # make a submenu + sm = wx.Menu() + sm.Append(self.popupID8, "Sub Item 1") + sm.Append(self.popupID9, "Sub Item 1") + menu.Append(self.popupID7, "Test Submenu", sm) + + return menu + + + def CreateGlobalPopups(self): + + if not hasattr(self, "popupID10"): + self.popupID10 = wx.NewIdRef() + self.popupID11 = wx.NewIdRef() + self.popupID12 = wx.NewIdRef() + + self.Bind(wx.EVT_MENU, self.OnPopupTen, id=self.popupID10) + self.Bind(wx.EVT_MENU, self.OnPopupEleven, id=self.popupID11) + self.Bind(wx.EVT_MENU, self.OnPopupTwelve, id=self.popupID12) + + menu = wx.Menu() + item = wx.MenuItem(menu, self.popupID10, "Select all") + menu.Append(item) + menu.AppendSeparator() + + item = wx.MenuItem(menu, self.popupID11, "Say Hello!") + img = images.Mondrian.GetImage() + img.Rescale(16, 16) + bmp = img.ConvertToBitmap() + item.SetBitmap(bmp) + menu.Append(item) + menu.AppendSeparator() + + menu.Append(self.popupID12, "Get thumbs count") + + return menu + + + def OnPopupOne(self, event): + self.log.write("OnPopupMenu: Popup One\n") + + + def OnPopupTwo(self, event): + self.log.write("OnPopupMenu: Popup Two\n") + + + def OnPopupThree(self, event): + self.log.write("OnPopupMenu: Popup Three\n") + + + def OnPopupFour(self, event): + self.log.write("OnPopupMenu: Popup Four\n") + + + def OnPopupFive(self, event): + self.log.write("OnPopupMenu: Popup Five\n") + + + def OnPopupSix(self, event): + self.log.write("OnPopupMenu: Popup Six\n") + + + def OnPopupSeven(self, event): + self.log.write("OnPopupMenu: Popup Seven\n") + + + def OnPopupEight(self, event): + self.log.write("OnPopupMenu: Popup Eight\n") + + + def OnPopupNine(self, event): + self.log.write("OnPopupMenu: Popup Nine\n") + + + def OnPopupTen(self, event): + + items = self.scroll.GetItemCount() + self.log.write("Items", items, type(items)) + + for ii in range(items): + self.scroll.SetSelection(ii) + + self.log.write("OnGlobalPopupMenu: all thumbs selected\n") + + event.Skip() + + + def OnPopupEleven(self, event): + + self.log.write("OnGlobalPopupMenu: say hello message...\n") + + msgstr = "Info: let's say hello to wxPython! " + dlg = wx.MessageDialog(self, msgstr, "ScrolledThumbnailDemo Info", + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + event.Skip() + + + def OnPopupTwelve(self, event): + + items = self.scroll.GetItemCount() + self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) + + msgstr = "Info: number of thumbs: %d"%items + dlg = wx.MessageDialog(self, msgstr, "ScrolledThumbnailDemo Info", + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + event.Skip() + + +#--------------------------------------------------------------------------- + + +class TestPanel(wx.Panel): + def __init__(self, parent, log): + self.log = log + wx.Panel.__init__(self, parent, -1) + + b = wx.Button(self, -1, " Test ScrolledThumbnail", (50,50)) + self.Bind(wx.EVT_BUTTON, self.OnButton, b) + + + def OnButton(self, evt): + self.win = ScrolledThumbnailDemo(self, self.log) + self.win.Show(True) + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + + win = TestPanel(nb, log) + return win + +#---------------------------------------------------------------------- + + +overview = wx.lib.agw.scrolledthumbnail.__doc__ + + +if __name__ == '__main__': + import sys,os + import run + run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index f688e670..6ea2c9fe 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -16,8 +16,8 @@ import wx.lib.agw.thumbnailctrl as TC import images -from wx.lib.agw.scrolledthumbnail import EVT_THUMBNAILS_SEL_CHANGED, EVT_THUMBNAILS_POINTED, \ -EVT_THUMBNAILS_DCLICK +from wx.lib.agw.scrolledthumbnail import (EVT_THUMBNAILS_SEL_CHANGED, EVT_THUMBNAILS_POINTED, + EVT_THUMBNAILS_DCLICK) class ThumbnailCtrlDemo(wx.Frame): diff --git a/demo/agw/__demo__.py b/demo/agw/__demo__.py index cc4d18bf..0cb76cc5 100644 --- a/demo/agw/__demo__.py +++ b/demo/agw/__demo__.py @@ -97,7 +97,8 @@ def GetDemos(): 'AGWInfoBar', 'KnobCtrl', 'LabelBook', 'MultiDirDialog', 'PeakMeter', 'PersistentControls', 'PieCtrl', 'PyBusyInfo', 'PyCollapsiblePane', 'PyGauge', 'PyProgress', 'RibbonBar', - 'RulerCtrl', 'ShapedButton', 'ShortcutEditor', 'SpeedMeter', + 'RulerCtrl', 'ScrolledThumbnail', 'ShapedButton', + 'ShortcutEditor', 'SpeedMeter', 'SuperToolTip', 'ThumbnailCtrl', 'ToasterBox', 'UltimateListCtrl', 'XLSGrid', 'ZoomBar'] diff --git a/demo/demodata.py b/demo/demodata.py index 33655571..4a75bda1 100644 --- a/demo/demodata.py +++ b/demo/demodata.py @@ -44,6 +44,7 @@ _treeList = [ 'TreeListCtrl', 'NotificationMessage', 'AddPrivateFont', + 'ScrolledThumbnail', 'SVGImage_Bitmap', 'SVGImage_Render', 'ActivityIndicator', From f2a2eca467e19320ba67240396a6f0fd1d41e6b0 Mon Sep 17 00:00:00 2001 From: eagerm Date: Fri, 23 Oct 2020 17:03:55 -0700 Subject: [PATCH 04/10] Update CHANGES with info about ScrolledThumbnail --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e66dca07..2f7c03fd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -79,6 +79,8 @@ New and improved in this release: was done to workaround a bug in wxMac, but it seems worthwhile enough to keep it around even after the bug was fixed. +* Refactored ScrolledThumbnail out of agw.ThumbnailCtrl so as to be usable + outside of ThumbnailCtrl. From e73d5f49db9053c2b918285eedba26edd380a6ab Mon Sep 17 00:00:00 2001 From: eagerm Date: Tue, 10 Nov 2020 20:12:54 -0800 Subject: [PATCH 05/10] Minor cleanup and fixes from code review demo/agw/ScrolledThumbnail.py: Set frame size to (850, 800). Decrease left side of splitter. Use default font. Remove SetAutoLayout() Fix THUMB_OUTLINE_NONE, etc. not found. demo/agw/Thumbnailctrl.py: Set frame size to (850, 800). Make scroll instance variable, not local. Replace self.TC with self.scroll. Decrease left side of splitter. Use default font. Remove SetAutoLayout(). --- demo/agw/ScrolledThumbnail.py | 20 +++----- demo/agw/ThumbnailCtrl.py | 96 ++++++++++++++++------------------- 2 files changed, 51 insertions(+), 65 deletions(-) diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py index 4c5e03fe..5c0a8187 100644 --- a/demo/agw/ScrolledThumbnail.py +++ b/demo/agw/ScrolledThumbnail.py @@ -8,6 +8,7 @@ import os import sys import time import images +import wx.lib.agw.scrolledthumbnail as ST from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, Thumb, NativeImageHandler, @@ -25,7 +26,7 @@ class ScrolledThumbnailDemo(wx.Frame): def __init__(self, parent, log): - wx.Frame.__init__(self, parent) + wx.Frame.__init__(self, parent, size=(850,800)) self.SetIcon(images.Mondrian.GetIcon()) self.SetTitle("ScrolledThumbnail wxPython Demo ;-)") @@ -105,21 +106,15 @@ class ScrolledThumbnailDemo(wx.Frame): self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - splitter.SplitVertically(self.scroll, self.panel, 400) + splitter.SplitVertically(self.scroll, self.panel, 300) splitter.SetMinimumPaneSize(140) - self.SetMinSize((1000, 1000)) self.CenterOnScreen() def SetProperties(self): self.radiostyle4.SetValue(1) self.showfiles.SetValue(1) - boldFont = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "") - self.zoombutton.SetFont(boldFont) - self.fontbutton.SetFont(boldFont) - self.dirbutton.SetFont(boldFont) - self.colourbutton.SetFont(boldFont) def DoLayout(self): @@ -157,7 +152,6 @@ class ScrolledThumbnailDemo(wx.Frame): optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) - self.panel.SetAutoLayout(True) self.panel.SetSizer(splitsizer) splitsizer.Fit(self.panel) @@ -246,13 +240,13 @@ class ScrolledThumbnailDemo(wx.Frame): pos = self.radios.index(radio) if pos == 0: - self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_NONE) + self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_NONE) elif pos == 1: - self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_FULL) + self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_FULL) elif pos == 2: - self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_RECT) + self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_RECT) elif pos == 3: - self.scroll.SetThumbOutline(self.scroll.THUMB_OUTLINE_IMAGE) + self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_IMAGE) self.scroll.Refresh() diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index 6ea2c9fe..efd8a06d 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -24,7 +24,7 @@ class ThumbnailCtrlDemo(wx.Frame): def __init__(self, parent, log): - wx.Frame.__init__(self, parent) + wx.Frame.__init__(self, parent, size=(850,800)) self.SetIcon(images.Mondrian.GetIcon()) self.SetTitle("ThumbnailCtrl wxPython Demo ;-)") @@ -45,16 +45,15 @@ class ThumbnailCtrlDemo(wx.Frame): self.panel = wx.Panel(splitter, -1) sizer = wx.BoxSizer(wx.HORIZONTAL) - scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.NativeImageHandler) + self.scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.NativeImageHandler) #scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.PILImageHandler) - scroll.ShowFileNames() + self.scroll.ShowFileNames() if os.path.isdir("../bitmaps"): - scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps")) + self.scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps")) else: - scroll.ShowDir(os.getcwd()) + self.scroll.ShowDir(os.getcwd()) - self.TC = scroll self.log = log self.thumbsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Style") @@ -109,14 +108,13 @@ class ThumbnailCtrlDemo(wx.Frame): self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) - self.TC.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) - self.TC.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) - self.TC.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) + self.scroll.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) + self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) + self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - splitter.SplitVertically(scroll, self.panel, 400) + splitter.SplitVertically(self.scroll, self.panel, 300) splitter.SetMinimumPaneSize(140) - self.SetMinSize((1000, 1000)) self.CenterOnScreen() @@ -124,11 +122,6 @@ class ThumbnailCtrlDemo(wx.Frame): self.radiostyle4.SetValue(1) self.showfiles.SetValue(1) - boldFont = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "") - self.zoombutton.SetFont(boldFont) - self.fontbutton.SetFont(boldFont) - self.dirbutton.SetFont(boldFont) - self.colourbutton.SetFont(boldFont) def DoLayout(self): @@ -167,7 +160,6 @@ class ThumbnailCtrlDemo(wx.Frame): optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) - self.panel.SetAutoLayout(True) self.panel.SetSizer(splitsizer) splitsizer.Fit(self.panel) @@ -227,7 +219,7 @@ class ThumbnailCtrlDemo(wx.Frame): # This is done by getting the path data from the dialog - BEFORE # we destroy it. if dlg.ShowModal() == wx.ID_OK: - self.TC.ShowDir(dlg.GetPath()) + self.scroll.ShowDir(dlg.GetPath()) self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) # Only destroy a dialog after you're done with it. @@ -240,15 +232,15 @@ class ThumbnailCtrlDemo(wx.Frame): pos = self.radios.index(radio) if pos == 0: - self.TC.SetThumbOutline(TC.THUMB_OUTLINE_NONE) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_NONE) elif pos == 1: - self.TC.SetThumbOutline(TC.THUMB_OUTLINE_FULL) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_FULL) elif pos == 2: - self.TC.SetThumbOutline(TC.THUMB_OUTLINE_RECT) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_RECT) elif pos == 3: - self.TC.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) - self.TC.Refresh() + self.scroll.Refresh() self.log.write("OnChangeOutline: Outline changed to: %s\n"%self.thumbstyles[pos]) event.Skip() @@ -257,10 +249,10 @@ class ThumbnailCtrlDemo(wx.Frame): def OnHighlight(self, event): # wxGlade: MyFrame. if self.highlight.GetValue() == 1: - self.TC.SetHighlightPointed(True) + self.scroll.SetHighlightPointed(True) self.log.write("OnHighlight: Highlight thumbs on pointing\n") else: - self.TC.SetHighlightPointed(False) + self.scroll.SetHighlightPointed(False) self.log.write("OnHighlight: Don't Highlight thumbs on pointing\n") event.Skip() @@ -269,13 +261,13 @@ class ThumbnailCtrlDemo(wx.Frame): def OnShowFiles(self, event): # wxGlade: MyFrame. if self.showfiles.GetValue() == 1: - self.TC.ShowFileNames(True) + self.scroll.ShowFileNames(True) self.log.write("OnShowFiles: Thumbs file names shown\n") else: - self.TC.ShowFileNames(False) + self.scroll.ShowFileNames(False) self.log.write("OnShowFiles: Thumbs file names not shown\n") - self.TC.Refresh() + self.scroll.Refresh() event.Skip() @@ -283,13 +275,13 @@ class ThumbnailCtrlDemo(wx.Frame): def OnEnableDragging(self, event): if self.enabledragging.GetValue() == 1: - self.TC.EnableDragging(True) + self.scroll.EnableDragging(True) self.log.write("OnEnableDragging: Thumbs drag and drop enabled\n") else: - self.TC.EnableDragging(False) + self.scroll.EnableDragging(False) self.log.write("OnEnableDragging: Thumbs drag and drop disabled\n") - self.TC.Refresh() + self.scroll.Refresh() event.Skip() @@ -298,10 +290,10 @@ class ThumbnailCtrlDemo(wx.Frame): if self.setpopup.GetValue() == 1: menu = self.CreatePopups() - self.TC.SetPopupMenu(menu) + self.scroll.SetPopupMenu(menu) self.log.write("OnSetPopup: Popups enabled on thumbs\n") else: - self.TC.SetPopupMenu(None) + self.scroll.SetPopupMenu(None) self.log.write("OnSetPopup: Popups disabled on thumbs\n") event.Skip() @@ -311,10 +303,10 @@ class ThumbnailCtrlDemo(wx.Frame): if self.setgpopup.GetValue() == 1: menu = self.CreateGlobalPopups() - self.TC.SetGlobalPopupMenu(menu) + self.scroll.SetGlobalPopupMenu(menu) self.log.write("OnSetGlobalPopup: Popups enabled globally (no selection needed)\n") else: - self.TC.SetGlobalPopupMenu(None) + self.scroll.SetGlobalPopupMenu(None) self.log.write("OnSetGlobalPopup: Popups disabled globally (no selection needed)\n") event.Skip() @@ -324,10 +316,10 @@ class ThumbnailCtrlDemo(wx.Frame): if self.showcombo.GetValue() == 1: self.log.write("OnShowComboBox: Directory comboBox shown\n") - self.TC.ShowComboBox(True) + self.scroll.ShowComboBox(True) else: self.log.write("OnShowComboBox: Directory comboBox hidden\n") - self.TC.ShowComboBox(False) + self.scroll.ShowComboBox(False) event.Skip() @@ -336,10 +328,10 @@ class ThumbnailCtrlDemo(wx.Frame): if self.enabletooltip.GetValue() == 1: self.log.write("OnEnableToolTips: File information on tooltips enabled\n") - self.TC.EnableToolTips(True) + self.scroll.EnableToolTips(True) else: self.log.write("OnEnableToolTips: File information on tooltips disabled\n") - self.TC.EnableToolTips(False) + self.scroll.EnableToolTips(False) event.Skip() @@ -369,7 +361,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.textzoom.SetValue("1.4") return - self.TC.SetZoomFactor(val) + self.scroll.SetZoomFactor(val) event.Skip() @@ -388,25 +380,25 @@ class ThumbnailCtrlDemo(wx.Frame): width = max(width, 50) height = max(height, 50) self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) - self.TC.SetThumbSize (width, height) + self.scroll.SetThumbSize (width, height) event.Skip() def OnSelChanged(self, event): - self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.TC.GetSelection())) + self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.scroll.GetSelection())) event.Skip() def OnPointed(self, event): - self.log.write("OnPointed: Thumb pointed: %s\n"%self.TC.GetPointed()) + self.log.write("OnPointed: Thumb pointed: %s\n"%self.scroll.GetPointed()) event.Skip() def OnDClick(self, event): - self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.TC.GetSelection()) + self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.scroll.GetSelection()) event.Skip() @@ -414,15 +406,15 @@ class ThumbnailCtrlDemo(wx.Frame): data = wx.FontData() data.EnableEffects(True) - data.SetInitialFont(self.TC.GetCaptionFont()) + data.SetInitialFont(self.scroll.GetCaptionFont()) dlg = wx.FontDialog(self, data) if dlg.ShowModal() == wx.ID_OK: data = dlg.GetFontData() font = data.GetChosenFont() - self.TC.SetCaptionFont(font) - self.TC.Refresh() + self.scroll.SetCaptionFont(font) + self.scroll.Refresh() self.log.write("OnSetFont: Caption font changed\n") # Don't destroy the dialog until you get everything you need from the @@ -449,8 +441,8 @@ class ThumbnailCtrlDemo(wx.Frame): # returned as a three-tuple (r, g, b) in this particular case. colour = data.GetColour().Get() colour = wx.Colour(colour[0], colour[1], colour[2]) - self.TC.SetSelectionColour(colour) - self.TC.Refresh() + self.scroll.SetSelectionColour(colour) + self.scroll.Refresh() # Once the dialog is destroyed, Mr. wx.ColourData is no longer your # friend. Don't use it again! @@ -573,11 +565,11 @@ class ThumbnailCtrlDemo(wx.Frame): def OnPopupTen(self, event): - items = self.TC.GetItemCount() + items = self.scroll.GetItemCount() self.log.write("Items", items, type(items)) for ii in range(items): - self.TC.SetSelection(ii) + self.scroll.SetSelection(ii) self.log.write("OnGlobalPopupMenu: all thumbs selected\n") @@ -599,7 +591,7 @@ class ThumbnailCtrlDemo(wx.Frame): def OnPopupTwelve(self, event): - items = self.TC.GetItemCount() + items = self.scroll.GetItemCount() self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) msgstr = "Info: number of thumbs: %d"%items From 6fa88e361f7eb3e3089dcd3fc7734ac0ea0f1a4e Mon Sep 17 00:00:00 2001 From: eagerm Date: Tue, 10 Nov 2020 20:20:34 -0800 Subject: [PATCH 06/10] Reduce differences between demo/ScrolledThumbnail and demo/ThumbnailCtrl --- demo/agw/ScrolledThumbnail.py | 33 ++++++++++++++++++--------------- demo/agw/ThumbnailCtrl.py | 21 ++++++++++----------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py index 5c0a8187..611f8388 100644 --- a/demo/agw/ScrolledThumbnail.py +++ b/demo/agw/ScrolledThumbnail.py @@ -8,7 +8,7 @@ import os import sys import time import images -import wx.lib.agw.scrolledthumbnail as ST +import wx.lib.agw.scrolledthumbnail as TC from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, Thumb, NativeImageHandler, @@ -27,14 +27,15 @@ class ScrolledThumbnailDemo(wx.Frame): def __init__(self, parent, log): wx.Frame.__init__(self, parent, size=(850,800)) + self.name = "ScrolledThumbnail" self.SetIcon(images.Mondrian.GetIcon()) - self.SetTitle("ScrolledThumbnail wxPython Demo ;-)") + self.SetTitle(self.name + " wxPython Demo ;-)") self.statusbar = self.CreateStatusBar(2) self.statusbar.SetStatusWidths([-2, -1]) # statusbar fields - statusbar_fields = [("ScrolledThumbnail Demo, Michael Eager @ 23 Oct 2020"), + statusbar_fields = [(self.name + " Demo, Michael Eager @ 10 Nov 2020"), ("Welcome To wxPython!")] for i in range(len(statusbar_fields)): @@ -111,6 +112,7 @@ class ScrolledThumbnailDemo(wx.Frame): splitter.SetMinimumPaneSize(140) self.CenterOnScreen() + def SetProperties(self): self.radiostyle4.SetValue(1) @@ -185,8 +187,8 @@ class ScrolledThumbnailDemo(wx.Frame): def OnAbout(self, event): - msg = "This Is The About Dialog Of The ScrollThumbnail Demo.\n\n" + \ - "Author: Michael Eager @ 23 Oct 2020\n\n" + \ + msg = "This Is The About Dialog Of The " + self.name + " Demo.\n\n" + \ + "Author: Michael Eager @ 10 Nov 2020\n\n" + \ "Adapted from the ThumbnailCtrl Demo\n" + \ "By Andrea Gavana @ 10 Dec 2005\n\n" + \ "Please Report Any Bug/Requests Of Improvements\n" + \ @@ -194,7 +196,7 @@ class ScrolledThumbnailDemo(wx.Frame): "eager@eagercon.com\n\n" + \ "Welcome To wxPython " + wx.VERSION_STRING + "!!" - dlg = wx.MessageDialog(self, msg, "ScrolledThumbnail Demo", + dlg = wx.MessageDialog(self, msg, self.name + " Demo", wx.OK | wx.ICON_INFORMATION) dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) @@ -240,13 +242,13 @@ class ScrolledThumbnailDemo(wx.Frame): pos = self.radios.index(radio) if pos == 0: - self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_NONE) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_NONE) elif pos == 1: - self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_FULL) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_FULL) elif pos == 2: - self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_RECT) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_RECT) elif pos == 3: - self.scroll.SetThumbOutline(ST.THUMB_OUTLINE_IMAGE) + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) self.scroll.Refresh() @@ -340,7 +342,7 @@ class ScrolledThumbnailDemo(wx.Frame): val = float(val) except: errstr = "Error: a float value is required." - dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -350,7 +352,7 @@ class ScrolledThumbnailDemo(wx.Frame): if val < 1.0: errstr = "Error: zoom factor must be grater than 1.0." - dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -367,7 +369,7 @@ class ScrolledThumbnailDemo(wx.Frame): height = int(self.textthumbheight.GetValue().strip()) except: errstr = "Error: thumb size must be integers (min 50x50)." - dlg = wx.MessageDialog(self, errstr, "ScrolledThumbnailDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -577,7 +579,7 @@ class ScrolledThumbnailDemo(wx.Frame): self.log.write("OnGlobalPopupMenu: say hello message...\n") msgstr = "Info: let's say hello to wxPython! " - dlg = wx.MessageDialog(self, msgstr, "ScrolledThumbnailDemo Info", + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() @@ -591,7 +593,7 @@ class ScrolledThumbnailDemo(wx.Frame): self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) msgstr = "Info: number of thumbs: %d"%items - dlg = wx.MessageDialog(self, msgstr, "ScrolledThumbnailDemo Info", + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() @@ -599,6 +601,7 @@ class ScrolledThumbnailDemo(wx.Frame): event.Skip() + #--------------------------------------------------------------------------- diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index efd8a06d..d37a9d74 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -25,14 +25,15 @@ class ThumbnailCtrlDemo(wx.Frame): def __init__(self, parent, log): wx.Frame.__init__(self, parent, size=(850,800)) + self.name = "ThumbnailCtrl" self.SetIcon(images.Mondrian.GetIcon()) - self.SetTitle("ThumbnailCtrl wxPython Demo ;-)") + self.SetTitle(self.name + " wxPython Demo ;-)") self.statusbar = self.CreateStatusBar(2) self.statusbar.SetStatusWidths([-2, -1]) # statusbar fields - statusbar_fields = [("ThumbnailCtrl Demo, Michael Eager @ 15 Oct 2020"), + statusbar_fields = [(self.name + " Demo, Michael Eager @ 10 Nov 2020"), ("Welcome To wxPython!")] for i in range(len(statusbar_fields)): @@ -193,7 +194,7 @@ class ThumbnailCtrlDemo(wx.Frame): def OnAbout(self, event): - msg = "This Is The About Dialog Of The ThumbnailCtrl Demo.\n\n" + \ + msg = "This Is The About Dialog Of The " + self.name + " Demo.\n\n" + \ "Author: Andrea Gavana @ 10 Dec 2005\n\n" + \ "Modified: Michael Eager @ 15 Oct 2020\n\n" + \ "Please Report Any Bug/Requests Of Improvements\n" + \ @@ -201,7 +202,7 @@ class ThumbnailCtrlDemo(wx.Frame): "eager@eagercon.com\n\n" + \ "Welcome To wxPython " + wx.VERSION_STRING + "!!" - dlg = wx.MessageDialog(self, msg, "ThumbnailCtrl Demo", + dlg = wx.MessageDialog(self, msg, self.name + " Demo", wx.OK | wx.ICON_INFORMATION) dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) @@ -344,7 +345,7 @@ class ThumbnailCtrlDemo(wx.Frame): val = float(val) except: errstr = "Error: a float value is required." - dlg = wx.MessageDialog(self, errstr, "ThumbnailCtrlDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -354,7 +355,7 @@ class ThumbnailCtrlDemo(wx.Frame): if val < 1.0: errstr = "Error: zoom factor must be grater than 1.0." - dlg = wx.MessageDialog(self, errstr, "ThumbnailCtrlDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -371,7 +372,7 @@ class ThumbnailCtrlDemo(wx.Frame): height = int(self.textthumbheight.GetValue().strip()) except: errstr = "Error: thumb size must be integers (min 50x50)." - dlg = wx.MessageDialog(self, errstr, "ThumbnailCtrlDemo Error", + dlg = wx.MessageDialog(self, errstr, self.name + " Error", wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() @@ -581,7 +582,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.log.write("OnGlobalPopupMenu: say hello message...\n") msgstr = "Info: let's say hello to wxPython! " - dlg = wx.MessageDialog(self, msgstr, "ThumbnailCtrlDemo Info", + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() @@ -595,7 +596,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) msgstr = "Info: number of thumbs: %d"%items - dlg = wx.MessageDialog(self, msgstr, "ThumbnailCtrlDemo Info", + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() @@ -646,5 +647,3 @@ if __name__ == '__main__': import sys,os import run run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) - - From d6727e5ce78ef6f97040ebc1c43380ec88966b31 Mon Sep 17 00:00:00 2001 From: eagerm Date: Wed, 11 Nov 2020 12:38:54 -0800 Subject: [PATCH 07/10] Create ThumbDemoConfig class class ThumbDemoConfig contains the user interface and controls used by both ScrolledThumbnail and ThumbnailCtrl. Functions which are different between the two demos are overwridden by each demo. --- demo/agw/ScrolledThumbnail.py | 91 ++++++++++++++++---------- demo/agw/ThumbnailCtrl.py | 116 ++++++++++++++++++++++------------ 2 files changed, 135 insertions(+), 72 deletions(-) diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py index 611f8388..c8f1aede 100644 --- a/demo/agw/ScrolledThumbnail.py +++ b/demo/agw/ScrolledThumbnail.py @@ -22,12 +22,13 @@ except: sys.path.append(os.path.split(dirName)[0]) -class ScrolledThumbnailDemo(wx.Frame): +class ThumbDemoConfig(wx.Frame): - def __init__(self, parent, log): + def __init__(self, parent, log, name, about): wx.Frame.__init__(self, parent, size=(850,800)) - self.name = "ScrolledThumbnail" + self.name = name + self.about = about self.SetIcon(images.Mondrian.GetIcon()) self.SetTitle(self.name + " wxPython Demo ;-)") @@ -43,13 +44,13 @@ class ScrolledThumbnailDemo(wx.Frame): self.SetMenuBar(self.CreateMenuBar()) - splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | + self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) - self.panel = wx.Panel(splitter, -1) + self.panel = wx.Panel(self.splitter, -1) sizer = wx.BoxSizer(wx.HORIZONTAL) - self.scroll = ScrolledThumbnail(splitter, -1, size=(400,300)) + self.SetScroll() self.log = log @@ -67,6 +68,7 @@ class ScrolledThumbnailDemo(wx.Frame): self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") + self.DoComboCheckbox() self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") @@ -96,6 +98,7 @@ class ScrolledThumbnailDemo(wx.Frame): self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) + self.DoBindCombo() self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) @@ -107,9 +110,9 @@ class ScrolledThumbnailDemo(wx.Frame): self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - splitter.SplitVertically(self.scroll, self.panel, 300) + self.splitter.SplitVertically(self.scroll, self.panel, 300) - splitter.SetMinimumPaneSize(140) + self.splitter.SetMinimumPaneSize(140) self.CenterOnScreen() @@ -142,6 +145,7 @@ class ScrolledThumbnailDemo(wx.Frame): customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + self.DoAddCombo(customsizer) customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) @@ -187,16 +191,7 @@ class ScrolledThumbnailDemo(wx.Frame): def OnAbout(self, event): - msg = "This Is The About Dialog Of The " + self.name + " Demo.\n\n" + \ - "Author: Michael Eager @ 10 Nov 2020\n\n" + \ - "Adapted from the ThumbnailCtrl Demo\n" + \ - "By Andrea Gavana @ 10 Dec 2005\n\n" + \ - "Please Report Any Bug/Requests Of Improvements\n" + \ - "To Me At The Following Addresses:\n" + \ - "eager@eagercon.com\n\n" + \ - "Welcome To wxPython " + wx.VERSION_STRING + "!!" - - dlg = wx.MessageDialog(self, msg, self.name + " Demo", + dlg = wx.MessageDialog(self, self.about, self.name + " Demo", wx.OK | wx.ICON_INFORMATION) dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) @@ -221,21 +216,6 @@ class ScrolledThumbnailDemo(wx.Frame): dlg.Destroy() - def ShowDir(self, dir): - files = os.listdir(dir) - thumbs = [] - for f in files: - if os.path.splitext(f)[1] in [".jpg", ".gif", ".png"]: - statinfo = os.stat(os.path.join(dir, f)) - size = statinfo.st_size - modtime = statinfo.st_mtime - TIME_FMT = '%d %b %Y, %H:%M:%S' - lastmod = time.strftime(TIME_FMT, time.localtime(modtime)) - thumbs.append(Thumb(dir, f, caption=f, size=size, lastmod=lastmod, - imagehandler=NativeImageHandler)) - self.scroll.ShowThumbs(thumbs) - - def OnChangeOutline(self, event): # wxGlade: MyFrame. radio = event.GetEventObject() @@ -601,6 +581,51 @@ class ScrolledThumbnailDemo(wx.Frame): event.Skip() + def DoComboCheckbox(self): + pass + + def DoBindCombo(self): + pass + + def DoAddCombo(self, customsizer): + pass + +class ScrolledThumbnailDemo (ThumbDemoConfig): + + def __init__(self, parent, log): + + name = "ScrolledThumbnail" + + msg = "This Is The About Dialog Of The " + name + " Demo.\n\n" + \ + "Author: Michael Eager @ 10 Nov 2020\n\n" + \ + "Adapted from the ThumbnailCtrl Demo\n" + \ + "By Andrea Gavana @ 10 Dec 2005\n\n" + \ + "Please Report Any Bug/Requests Of Improvements\n" + \ + "To Me At The Following Addresses:\n" + \ + "eager@eagercon.com\n\n" + \ + "Welcome To wxPython " + wx.VERSION_STRING + "!!" + + super().__init__ (parent, log, name=name, about=msg) + + + def SetScroll(self): + self.scroll = ScrolledThumbnail(self.splitter, -1, size=(400,300)) + + + def ShowDir(self, dir): + files = os.listdir(dir) + thumbs = [] + for f in files: + if os.path.splitext(f)[1] in [".jpg", ".gif", ".png"]: + statinfo = os.stat(os.path.join(dir, f)) + size = statinfo.st_size + modtime = statinfo.st_mtime + TIME_FMT = '%d %b %Y, %H:%M:%S' + lastmod = time.strftime(TIME_FMT, time.localtime(modtime)) + thumbs.append(Thumb(dir, f, caption=f, size=size, lastmod=lastmod, + imagehandler=NativeImageHandler)) + self.scroll.ShowThumbs(thumbs) + #--------------------------------------------------------------------------- diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index d37a9d74..79759578 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -20,12 +20,13 @@ from wx.lib.agw.scrolledthumbnail import (EVT_THUMBNAILS_SEL_CHANGED, EVT_THUMBN EVT_THUMBNAILS_DCLICK) -class ThumbnailCtrlDemo(wx.Frame): +class ThumbDemoConfig(wx.Frame): - def __init__(self, parent, log): + def __init__(self, parent, log, name, about): wx.Frame.__init__(self, parent, size=(850,800)) - self.name = "ThumbnailCtrl" + self.name = name + self.about = about self.SetIcon(images.Mondrian.GetIcon()) self.SetTitle(self.name + " wxPython Demo ;-)") @@ -41,19 +42,13 @@ class ThumbnailCtrlDemo(wx.Frame): self.SetMenuBar(self.CreateMenuBar()) - splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | + self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) - self.panel = wx.Panel(splitter, -1) + self.panel = wx.Panel(self.splitter, -1) sizer = wx.BoxSizer(wx.HORIZONTAL) - self.scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.NativeImageHandler) - #scroll = TC.ThumbnailCtrl(splitter, -1, imagehandler=TC.PILImageHandler) - self.scroll.ShowFileNames() - if os.path.isdir("../bitmaps"): - self.scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps")) - else: - self.scroll.ShowDir(os.getcwd()) + self.SetScroll() self.log = log @@ -71,7 +66,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") - self.showcombo = wx.CheckBox(self.panel, -1, "Show folder combobox") + self.DoComboCheckbox() self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") @@ -101,7 +96,7 @@ class ThumbnailCtrlDemo(wx.Frame): self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) - self.Bind(wx.EVT_CHECKBOX, self.OnShowComboBox, self.showcombo) + self.DoBindCombo() self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) @@ -113,9 +108,9 @@ class ThumbnailCtrlDemo(wx.Frame): self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - splitter.SplitVertically(self.scroll, self.panel, 300) + self.splitter.SplitVertically(self.scroll, self.panel, 300) - splitter.SetMinimumPaneSize(140) + self.splitter.SetMinimumPaneSize(140) self.CenterOnScreen() @@ -148,7 +143,7 @@ class ThumbnailCtrlDemo(wx.Frame): customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.showcombo, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + self.DoAddCombo(customsizer) customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) @@ -194,15 +189,7 @@ class ThumbnailCtrlDemo(wx.Frame): def OnAbout(self, event): - msg = "This Is The About Dialog Of The " + self.name + " Demo.\n\n" + \ - "Author: Andrea Gavana @ 10 Dec 2005\n\n" + \ - "Modified: Michael Eager @ 15 Oct 2020\n\n" + \ - "Please Report Any Bug/Requests Of Improvements\n" + \ - "To Me At The Following Addresses:\n\n" + \ - "eager@eagercon.com\n\n" + \ - "Welcome To wxPython " + wx.VERSION_STRING + "!!" - - dlg = wx.MessageDialog(self, msg, self.name + " Demo", + dlg = wx.MessageDialog(self, self.about, self.name + " Demo", wx.OK | wx.ICON_INFORMATION) dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) @@ -220,7 +207,7 @@ class ThumbnailCtrlDemo(wx.Frame): # This is done by getting the path data from the dialog - BEFORE # we destroy it. if dlg.ShowModal() == wx.ID_OK: - self.scroll.ShowDir(dlg.GetPath()) + self.ShowDir(dlg.GetPath()) self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) # Only destroy a dialog after you're done with it. @@ -313,18 +300,6 @@ class ThumbnailCtrlDemo(wx.Frame): event.Skip() - def OnShowComboBox(self, event): - - if self.showcombo.GetValue() == 1: - self.log.write("OnShowComboBox: Directory comboBox shown\n") - self.scroll.ShowComboBox(True) - else: - self.log.write("OnShowComboBox: Directory comboBox hidden\n") - self.scroll.ShowComboBox(False) - - event.Skip() - - def OnEnableToolTips(self, event): if self.enabletooltip.GetValue() == 1: @@ -604,6 +579,69 @@ class ThumbnailCtrlDemo(wx.Frame): event.Skip() + def DoComboCheckbox(self): + pass + + def DoBindCombo(self): + pass + + def DoAddCombo(self, customsizer): + pass + +class ThumbnailCtrlDemo(ThumbDemoConfig): + + def __init__(self, parent, log): + + name = "ThumbnailCtrl" + + msg = "This Is The About Dialog Of The " + name + " Demo.\n\n" + \ + "Author: Andrea Gavana @ 10 Dec 2005\n\n" + \ + "Modified: Michael Eager @ 15 Oct 2020\n\n" + \ + "Please Report Any Bug/Requests Of Improvements\n" + \ + "To Me At The Following Addresses:\n\n" + \ + "eager@eagercon.com\n\n" + \ + "Welcome To wxPython " + wx.VERSION_STRING + "!!" + + super().__init__ (parent, log, name=name, about=msg) + + + def SetScroll(self): + + self.scroll = TC.ThumbnailCtrl(self.splitter, -1, imagehandler=TC.NativeImageHandler) + #scroll = TC.ThumbnailCtrl(self.splitter, -1, imagehandler=TC.PILImageHandler) + + self.scroll.ShowFileNames() + if os.path.isdir("../bitmaps"): + self.scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps")) + else: + self.scroll.ShowDir(os.getcwd()) + + + def DoComboCheckbox(self): + self.showcombo = wx.CheckBox(self.panel, -1, "Show folder combobox") + + + def DoBindCombo(self): + self.Bind(wx.EVT_CHECKBOX, self.OnShowComboBox, self.showcombo) + + def DoAddCombo(self, customsizer): + customsizer.Add(self.showcombo, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + + def ShowDir(self, dir): + self.scroll.ShowDir(dir) + + + def OnShowComboBox(self, event): + + if self.showcombo.GetValue() == 1: + self.log.write("OnShowComboBox: Directory comboBox shown\n") + self.scroll.ShowComboBox(True) + else: + self.log.write("OnShowComboBox: Directory comboBox hidden\n") + self.scroll.ShowComboBox(False) + + event.Skip() + #--------------------------------------------------------------------------- From 4c6b7254790d5f0414981d60435e6c390c3a988f Mon Sep 17 00:00:00 2001 From: eagerm Date: Thu, 12 Nov 2020 09:40:00 -0800 Subject: [PATCH 08/10] Create ThumbDemoConfig.py from class in ScrolledThumbnail & ThumbnailCtrl Add import statements to ScrolledThumbnail and ThumbnailCtrl. Remove unneeded imports. --- demo/agw/ScrolledThumbnail.py | 575 +-------------------------------- demo/agw/ThumbDemoConfig.py | 581 ++++++++++++++++++++++++++++++++++ demo/agw/ThumbnailCtrl.py | 570 +-------------------------------- 3 files changed, 585 insertions(+), 1141 deletions(-) create mode 100644 demo/agw/ThumbDemoConfig.py diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py index c8f1aede..578fa87d 100644 --- a/demo/agw/ScrolledThumbnail.py +++ b/demo/agw/ScrolledThumbnail.py @@ -7,14 +7,11 @@ import wx import os import sys import time -import images import wx.lib.agw.scrolledthumbnail as TC from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, Thumb, - NativeImageHandler, - EVT_THUMBNAILS_SEL_CHANGED, - EVT_THUMBNAILS_POINTED, - EVT_THUMBNAILS_DCLICK) + NativeImageHandler) + try: dirName = os.path.dirname(os.path.abspath(__file__)) except: @@ -22,573 +19,7 @@ except: sys.path.append(os.path.split(dirName)[0]) -class ThumbDemoConfig(wx.Frame): - - def __init__(self, parent, log, name, about): - - wx.Frame.__init__(self, parent, size=(850,800)) - self.name = name - self.about = about - - self.SetIcon(images.Mondrian.GetIcon()) - self.SetTitle(self.name + " wxPython Demo ;-)") - - self.statusbar = self.CreateStatusBar(2) - self.statusbar.SetStatusWidths([-2, -1]) - # statusbar fields - statusbar_fields = [(self.name + " Demo, Michael Eager @ 10 Nov 2020"), - ("Welcome To wxPython!")] - - for i in range(len(statusbar_fields)): - self.statusbar.SetStatusText(statusbar_fields[i], i) - - self.SetMenuBar(self.CreateMenuBar()) - - self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | - wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) - self.panel = wx.Panel(self.splitter, -1) - - sizer = wx.BoxSizer(wx.HORIZONTAL) - - self.SetScroll() - - self.log = log - - self.thumbsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Style") - self.customsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Customization") - self.optionsizer_staticbox = wx.StaticBox(self.panel, -1, "More Options") - self.dirsizer_staticbox = wx.StaticBox(self.panel, -1, "Directory Selection") - self.dirbutton = wx.Button(self.panel, -1, "Change Directory") - self.radiostyle1 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_NONE", style=wx.RB_GROUP) - self.radiostyle2 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_FULL") - self.radiostyle3 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_RECT") - self.radiostyle4 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_IMAGE") - self.highlight = wx.CheckBox(self.panel, -1, "Highlight on pointing") - self.showfiles = wx.CheckBox(self.panel, -1, "Show file names") - self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") - self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") - self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") - self.DoComboCheckbox() - self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") - self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") - self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") - self.textthumbwidth = wx.TextCtrl(self.panel, -1, "96") - self.textthumbheight = wx.TextCtrl(self.panel, -1, "80") - self.thumbsizebutton = wx.Button(self.panel, -1, "Set thumbnail size (WxH)") - self.fontbutton = wx.Button(self.panel, -1, "Set caption font") - self.colourbutton = wx.Button(self.panel, -1, "Set selection colour") - - self.radios = [self.radiostyle1, self.radiostyle2, self.radiostyle3, - self.radiostyle4] - self.thumbstyles = ["THUMB_OUTLINE_NONE", "THUMB_OUTLINE_FULL", "THUMB_OUTLINE_RECT", - "THUMB_OUTLINE_IMAGE"] - - self.SetProperties() - self.DoLayout() - - self.panel.SetSizer(sizer) - sizer.Layout() - - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle4) - self.Bind(wx.EVT_CHECKBOX, self.OnHighlight, self.highlight) - self.Bind(wx.EVT_CHECKBOX, self.OnShowFiles, self.showfiles) - self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) - self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) - self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) - self.DoBindCombo() - self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) - self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) - self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) - self.Bind(wx.EVT_BUTTON, self.OnSetFont, self.fontbutton) - self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) - self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) - - self.scroll.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) - self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) - self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - - self.splitter.SplitVertically(self.scroll, self.panel, 300) - - self.splitter.SetMinimumPaneSize(140) - self.CenterOnScreen() - - - def SetProperties(self): - - self.radiostyle4.SetValue(1) - self.showfiles.SetValue(1) - - - def DoLayout(self): - - splitsizer = wx.BoxSizer(wx.VERTICAL) - optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) - zoomsizer = wx.BoxSizer(wx.HORIZONTAL) - thumbsizesizer = wx.BoxSizer(wx.HORIZONTAL) - customsizer = wx.StaticBoxSizer(self.customsizer_staticbox, wx.VERTICAL) - thumbsizer = wx.StaticBoxSizer(self.thumbsizer_staticbox, wx.VERTICAL) - radiosizer = wx.BoxSizer(wx.VERTICAL) - dirsizer = wx.StaticBoxSizer(self.dirsizer_staticbox, wx.HORIZONTAL) - dirsizer.Add(self.dirbutton, 0, wx.LEFT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(dirsizer, 0, wx.EXPAND|wx.TOP|wx.LEFT, 5) - radiosizer.Add(self.radiostyle1, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle2, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle3, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle4, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - thumbsizer.Add(radiosizer, 1, wx.EXPAND, 0) - splitsizer.Add(thumbsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) - customsizer.Add(self.highlight, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.showfiles, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - self.DoAddCombo(customsizer) - customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) - zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.textthumbheight, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.thumbsizebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - optionsizer.Add(zoomsizer, 1, wx.EXPAND, 0) - optionsizer.Add(thumbsizesizer, 1, wx.EXPAND, 0) - optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) - optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) - self.panel.SetSizer(splitsizer) - splitsizer.Fit(self.panel) - - - def CreateMenuBar(self): - - file_menu = wx.Menu() - - AS_EXIT = wx.NewIdRef() - file_menu.Append(AS_EXIT, "&Exit") - self.Bind(wx.EVT_MENU, self.OnClose, id=AS_EXIT) - - help_menu = wx.Menu() - - AS_ABOUT = wx.NewIdRef() - help_menu.Append(AS_ABOUT, "&About...") - self.Bind(wx.EVT_MENU, self.OnAbout, id=AS_ABOUT) - - menu_bar = wx.MenuBar() - - menu_bar.Append(file_menu, "&File") - menu_bar.Append(help_menu, "&Help") - - return menu_bar - - - def OnClose(self, event): - - self.Destroy() - - - def OnAbout(self, event): - - dlg = wx.MessageDialog(self, self.about, self.name + " Demo", - wx.OK | wx.ICON_INFORMATION) - - dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) - dlg.ShowModal() - dlg.Destroy() - - - def OnSetDirectory(self, event): - - dlg = wx.DirDialog(self, "Choose a directory with images:", - defaultPath=os.getcwd(), - style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) - - # If the user selects OK, then we process the dialog's data. - # This is done by getting the path data from the dialog - BEFORE - # we destroy it. - if dlg.ShowModal() == wx.ID_OK: - self.ShowDir(dlg.GetPath()) - self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) - - # Only destroy a dialog after you're done with it. - dlg.Destroy() - - - def OnChangeOutline(self, event): # wxGlade: MyFrame. - - radio = event.GetEventObject() - pos = self.radios.index(radio) - - if pos == 0: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_NONE) - elif pos == 1: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_FULL) - elif pos == 2: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_RECT) - elif pos == 3: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) - - self.scroll.Refresh() - - self.log.write("OnChangeOutline: Outline changed to: %s\n"%self.thumbstyles[pos]) - event.Skip() - - - def OnHighlight(self, event): # wxGlade: MyFrame. - - if self.highlight.GetValue() == 1: - self.scroll.SetHighlightPointed(True) - self.log.write("OnHighlight: Highlight thumbs on pointing\n") - else: - self.scroll.SetHighlightPointed(False) - self.log.write("OnHighlight: Don't Highlight thumbs on pointing\n") - - event.Skip() - - - def OnShowFiles(self, event): # wxGlade: MyFrame. - - if self.showfiles.GetValue() == 1: - self.scroll.ShowFileNames(True) - self.log.write("OnShowFiles: Thumbs file names shown\n") - else: - self.scroll.ShowFileNames(False) - self.log.write("OnShowFiles: Thumbs file names not shown\n") - - self.scroll.Refresh() - - event.Skip() - - - def OnEnableDragging(self, event): - - if self.enabledragging.GetValue() == 1: - self.scroll.EnableDragging(True) - self.log.write("OnEnableDragging: Thumbs drag and drop enabled\n") - else: - self.scroll.EnableDragging(False) - self.log.write("OnEnableDragging: Thumbs drag and drop disabled\n") - - self.scroll.Refresh() - - event.Skip() - - - def OnSetPopup(self, event): # wxGlade: MyFrame. - - if self.setpopup.GetValue() == 1: - menu = self.CreatePopups() - self.scroll.SetPopupMenu(menu) - self.log.write("OnSetPopup: Popups enabled on thumbs\n") - else: - self.scroll.SetPopupMenu(None) - self.log.write("OnSetPopup: Popups disabled on thumbs\n") - - event.Skip() - - - def OnSetGlobalPopup(self, event): - - if self.setgpopup.GetValue() == 1: - menu = self.CreateGlobalPopups() - self.scroll.SetGlobalPopupMenu(menu) - self.log.write("OnSetGlobalPopup: Popups enabled globally (no selection needed)\n") - else: - self.scroll.SetGlobalPopupMenu(None) - self.log.write("OnSetGlobalPopup: Popups disabled globally (no selection needed)\n") - - event.Skip() - - - def OnEnableToolTips(self, event): - - if self.enabletooltip.GetValue() == 1: - self.log.write("OnEnableToolTips: File information on tooltips enabled\n") - self.scroll.EnableToolTips(True) - else: - self.log.write("OnEnableToolTips: File information on tooltips disabled\n") - self.scroll.EnableToolTips(False) - - event.Skip() - - - def OnSetZoom(self, event): # wxGlade: MyFrame. - - val = self.textzoom.GetValue().strip() - - try: - val = float(val) - except: - errstr = "Error: a float value is required." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - self.textzoom.SetValue("1.4") - return - - - if val < 1.0: - errstr = "Error: zoom factor must be grater than 1.0." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - self.textzoom.SetValue("1.4") - return - - self.scroll.SetZoomFactor(val) - - event.Skip() - - def OnSetThumbSize(self, event): - try: - width = int(self.textthumbwidth.GetValue().strip()) - height = int(self.textthumbheight.GetValue().strip()) - except: - errstr = "Error: thumb size must be integers (min 50x50)." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - return - - width = max(width, 50) - height = max(height, 50) - self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) - self.scroll.SetThumbSize (width, height) - - event.Skip() - - def OnSelChanged(self, event): - - self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.scroll.GetSelection())) - event.Skip() - - - def OnPointed(self, event): - - self.log.write("OnPointed: Thumb pointed: %s\n"%self.scroll.GetPointed()) - event.Skip() - - - def OnDClick(self, event): - - self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.scroll.GetSelection()) - event.Skip() - - - def OnSetFont(self, event): # wxGlade: MyFrame. - - data = wx.FontData() - data.EnableEffects(True) - data.SetInitialFont(self.scroll.GetCaptionFont()) - - dlg = wx.FontDialog(self, data) - - if dlg.ShowModal() == wx.ID_OK: - data = dlg.GetFontData() - font = data.GetChosenFont() - self.scroll.SetCaptionFont(font) - self.scroll.Refresh() - self.log.write("OnSetFont: Caption font changed\n") - - # Don't destroy the dialog until you get everything you need from the - # dialog! - dlg.Destroy() - event.Skip() - - - def OnSetColour(self, event): - - dlg = wx.ColourDialog(self) - - # Ensure the full colour dialog is displayed, - # not the abbreviated version. - dlg.GetColourData().SetChooseFull(True) - - if dlg.ShowModal() == wx.ID_OK: - - # If the user selected OK, then the dialog's wx.ColourData will - # contain valid information. Fetch the data ... - data = dlg.GetColourData() - - # ... then do something with it. The actual colour data will be - # returned as a three-tuple (r, g, b) in this particular case. - colour = data.GetColour().Get() - colour = wx.Colour(colour[0], colour[1], colour[2]) - self.scroll.SetSelectionColour(colour) - self.scroll.Refresh() - - # Once the dialog is destroyed, Mr. wx.ColourData is no longer your - # friend. Don't use it again! - dlg.Destroy() - - - def CreatePopups(self): - - if not hasattr(self, "popupID1"): - self.popupID1 = wx.NewIdRef() - self.popupID2 = wx.NewIdRef() - self.popupID3 = wx.NewIdRef() - self.popupID4 = wx.NewIdRef() - self.popupID5 = wx.NewIdRef() - self.popupID6 = wx.NewIdRef() - self.popupID7 = wx.NewIdRef() - self.popupID8 = wx.NewIdRef() - self.popupID9 = wx.NewIdRef() - self.popupID10 = wx.NewIdRef() - self.popupID11 = wx.NewIdRef() - self.popupID12 = wx.NewIdRef() - - self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1) - self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2) - self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3) - self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4) - self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5) - self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6) - self.Bind(wx.EVT_MENU, self.OnPopupSeven, id=self.popupID7) - self.Bind(wx.EVT_MENU, self.OnPopupEight, id=self.popupID8) - self.Bind(wx.EVT_MENU, self.OnPopupNine, id=self.popupID9) - - menu = wx.Menu() - item = wx.MenuItem(menu, self.popupID1, "One") - img = images.Mondrian.GetImage() - img.Rescale(16, 16) - bmp = img.ConvertToBitmap() - item.SetBitmap(bmp) - menu.Append(item) - - # add some other items - menu.Append(self.popupID2, "Two") - menu.Append(self.popupID3, "Three") - menu.Append(self.popupID4, "Four") - menu.Append(self.popupID5, "Five") - menu.Append(self.popupID6, "Six") - # make a submenu - sm = wx.Menu() - sm.Append(self.popupID8, "Sub Item 1") - sm.Append(self.popupID9, "Sub Item 1") - menu.Append(self.popupID7, "Test Submenu", sm) - - return menu - - - def CreateGlobalPopups(self): - - if not hasattr(self, "popupID10"): - self.popupID10 = wx.NewIdRef() - self.popupID11 = wx.NewIdRef() - self.popupID12 = wx.NewIdRef() - - self.Bind(wx.EVT_MENU, self.OnPopupTen, id=self.popupID10) - self.Bind(wx.EVT_MENU, self.OnPopupEleven, id=self.popupID11) - self.Bind(wx.EVT_MENU, self.OnPopupTwelve, id=self.popupID12) - - menu = wx.Menu() - item = wx.MenuItem(menu, self.popupID10, "Select all") - menu.Append(item) - menu.AppendSeparator() - - item = wx.MenuItem(menu, self.popupID11, "Say Hello!") - img = images.Mondrian.GetImage() - img.Rescale(16, 16) - bmp = img.ConvertToBitmap() - item.SetBitmap(bmp) - menu.Append(item) - menu.AppendSeparator() - - menu.Append(self.popupID12, "Get thumbs count") - - return menu - - - def OnPopupOne(self, event): - self.log.write("OnPopupMenu: Popup One\n") - - - def OnPopupTwo(self, event): - self.log.write("OnPopupMenu: Popup Two\n") - - - def OnPopupThree(self, event): - self.log.write("OnPopupMenu: Popup Three\n") - - - def OnPopupFour(self, event): - self.log.write("OnPopupMenu: Popup Four\n") - - - def OnPopupFive(self, event): - self.log.write("OnPopupMenu: Popup Five\n") - - - def OnPopupSix(self, event): - self.log.write("OnPopupMenu: Popup Six\n") - - - def OnPopupSeven(self, event): - self.log.write("OnPopupMenu: Popup Seven\n") - - - def OnPopupEight(self, event): - self.log.write("OnPopupMenu: Popup Eight\n") - - - def OnPopupNine(self, event): - self.log.write("OnPopupMenu: Popup Nine\n") - - - def OnPopupTen(self, event): - - items = self.scroll.GetItemCount() - self.log.write("Items", items, type(items)) - - for ii in range(items): - self.scroll.SetSelection(ii) - - self.log.write("OnGlobalPopupMenu: all thumbs selected\n") - - event.Skip() - - - def OnPopupEleven(self, event): - - self.log.write("OnGlobalPopupMenu: say hello message...\n") - - msgstr = "Info: let's say hello to wxPython! " - dlg = wx.MessageDialog(self, msgstr, self.name + " Info", - wx.OK | wx.ICON_INFORMATION) - dlg.ShowModal() - dlg.Destroy() - - event.Skip() - - - def OnPopupTwelve(self, event): - - items = self.scroll.GetItemCount() - self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) - - msgstr = "Info: number of thumbs: %d"%items - dlg = wx.MessageDialog(self, msgstr, self.name + " Info", - wx.OK | wx.ICON_INFORMATION) - dlg.ShowModal() - dlg.Destroy() - - event.Skip() - - - def DoComboCheckbox(self): - pass - - def DoBindCombo(self): - pass - - def DoAddCombo(self, customsizer): - pass +from ThumbDemoConfig import ThumbDemoConfig class ScrolledThumbnailDemo (ThumbDemoConfig): diff --git a/demo/agw/ThumbDemoConfig.py b/demo/agw/ThumbDemoConfig.py new file mode 100644 index 00000000..e7dc2a2a --- /dev/null +++ b/demo/agw/ThumbDemoConfig.py @@ -0,0 +1,581 @@ +#!/usr/bin/env python + +import wx +import os +import images + +from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, + Thumb, + NativeImageHandler, + EVT_THUMBNAILS_SEL_CHANGED, + EVT_THUMBNAILS_POINTED, + EVT_THUMBNAILS_DCLICK) + +class ThumbDemoConfig(wx.Frame): + + def __init__(self, parent, log, name, about): + + wx.Frame.__init__(self, parent, size=(850,800)) + self.name = name + self.about = about + + self.SetIcon(images.Mondrian.GetIcon()) + self.SetTitle(self.name + " wxPython Demo ;-)") + + self.statusbar = self.CreateStatusBar(2) + self.statusbar.SetStatusWidths([-2, -1]) + # statusbar fields + statusbar_fields = [(self.name + " Demo, Michael Eager @ 10 Nov 2020"), + ("Welcome To wxPython!")] + + for i in range(len(statusbar_fields)): + self.statusbar.SetStatusText(statusbar_fields[i], i) + + self.SetMenuBar(self.CreateMenuBar()) + + self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | + wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) + self.panel = wx.Panel(self.splitter, -1) + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + self.SetScroll() + + self.log = log + + self.thumbsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Style") + self.customsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Customization") + self.optionsizer_staticbox = wx.StaticBox(self.panel, -1, "More Options") + self.dirsizer_staticbox = wx.StaticBox(self.panel, -1, "Directory Selection") + self.dirbutton = wx.Button(self.panel, -1, "Change Directory") + self.radiostyle1 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_NONE", style=wx.RB_GROUP) + self.radiostyle2 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_FULL") + self.radiostyle3 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_RECT") + self.radiostyle4 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_IMAGE") + self.highlight = wx.CheckBox(self.panel, -1, "Highlight on pointing") + self.showfiles = wx.CheckBox(self.panel, -1, "Show file names") + self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") + self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") + self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") + self.DoComboCheckbox() + self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") + self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") + self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") + self.textthumbwidth = wx.TextCtrl(self.panel, -1, "96") + self.textthumbheight = wx.TextCtrl(self.panel, -1, "80") + self.thumbsizebutton = wx.Button(self.panel, -1, "Set thumbnail size (WxH)") + self.fontbutton = wx.Button(self.panel, -1, "Set caption font") + self.colourbutton = wx.Button(self.panel, -1, "Set selection colour") + + self.radios = [self.radiostyle1, self.radiostyle2, self.radiostyle3, + self.radiostyle4] + self.thumbstyles = ["THUMB_OUTLINE_NONE", "THUMB_OUTLINE_FULL", "THUMB_OUTLINE_RECT", + "THUMB_OUTLINE_IMAGE"] + + self.SetProperties() + self.DoLayout() + + self.panel.SetSizer(sizer) + sizer.Layout() + + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle4) + self.Bind(wx.EVT_CHECKBOX, self.OnHighlight, self.highlight) + self.Bind(wx.EVT_CHECKBOX, self.OnShowFiles, self.showfiles) + self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) + self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) + self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) + self.DoBindCombo() + self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) + self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) + self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) + self.Bind(wx.EVT_BUTTON, self.OnSetFont, self.fontbutton) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) + self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) + + self.scroll.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) + self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) + self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) + + self.splitter.SplitVertically(self.scroll, self.panel, 300) + + self.splitter.SetMinimumPaneSize(140) + self.CenterOnScreen() + + + def SetProperties(self): + + self.radiostyle4.SetValue(1) + self.showfiles.SetValue(1) + + + def DoLayout(self): + + splitsizer = wx.BoxSizer(wx.VERTICAL) + optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) + zoomsizer = wx.BoxSizer(wx.HORIZONTAL) + thumbsizesizer = wx.BoxSizer(wx.HORIZONTAL) + customsizer = wx.StaticBoxSizer(self.customsizer_staticbox, wx.VERTICAL) + thumbsizer = wx.StaticBoxSizer(self.thumbsizer_staticbox, wx.VERTICAL) + radiosizer = wx.BoxSizer(wx.VERTICAL) + dirsizer = wx.StaticBoxSizer(self.dirsizer_staticbox, wx.HORIZONTAL) + dirsizer.Add(self.dirbutton, 0, wx.LEFT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(dirsizer, 0, wx.EXPAND|wx.TOP|wx.LEFT, 5) + radiosizer.Add(self.radiostyle1, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle2, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle3, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) + radiosizer.Add(self.radiostyle4, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + thumbsizer.Add(radiosizer, 1, wx.EXPAND, 0) + splitsizer.Add(thumbsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + customsizer.Add(self.highlight, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.showfiles, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + self.DoAddCombo(customsizer) + customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.textthumbheight, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + thumbsizesizer.Add(self.thumbsizebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) + optionsizer.Add(zoomsizer, 1, wx.EXPAND, 0) + optionsizer.Add(thumbsizesizer, 1, wx.EXPAND, 0) + optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) + optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) + splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) + self.panel.SetSizer(splitsizer) + splitsizer.Fit(self.panel) + + + def CreateMenuBar(self): + + file_menu = wx.Menu() + + AS_EXIT = wx.NewIdRef() + file_menu.Append(AS_EXIT, "&Exit") + self.Bind(wx.EVT_MENU, self.OnClose, id=AS_EXIT) + + help_menu = wx.Menu() + + AS_ABOUT = wx.NewIdRef() + help_menu.Append(AS_ABOUT, "&About...") + self.Bind(wx.EVT_MENU, self.OnAbout, id=AS_ABOUT) + + menu_bar = wx.MenuBar() + + menu_bar.Append(file_menu, "&File") + menu_bar.Append(help_menu, "&Help") + + return menu_bar + + + def OnClose(self, event): + + self.Destroy() + + + def OnAbout(self, event): + + dlg = wx.MessageDialog(self, self.about, self.name + " Demo", + wx.OK | wx.ICON_INFORMATION) + + dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) + dlg.ShowModal() + dlg.Destroy() + + + def OnSetDirectory(self, event): + + dlg = wx.DirDialog(self, "Choose a directory with images:", + defaultPath=os.getcwd(), + style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) + + # If the user selects OK, then we process the dialog's data. + # This is done by getting the path data from the dialog - BEFORE + # we destroy it. + if dlg.ShowModal() == wx.ID_OK: + self.ShowDir(dlg.GetPath()) + self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) + + # Only destroy a dialog after you're done with it. + dlg.Destroy() + + + def OnChangeOutline(self, event): # wxGlade: MyFrame. + + radio = event.GetEventObject() + pos = self.radios.index(radio) + + if pos == 0: + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_NONE) + elif pos == 1: + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_FULL) + elif pos == 2: + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_RECT) + elif pos == 3: + self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) + + self.scroll.Refresh() + + self.log.write("OnChangeOutline: Outline changed to: %s\n"%self.thumbstyles[pos]) + event.Skip() + + + def OnHighlight(self, event): # wxGlade: MyFrame. + + if self.highlight.GetValue() == 1: + self.scroll.SetHighlightPointed(True) + self.log.write("OnHighlight: Highlight thumbs on pointing\n") + else: + self.scroll.SetHighlightPointed(False) + self.log.write("OnHighlight: Don't Highlight thumbs on pointing\n") + + event.Skip() + + + def OnShowFiles(self, event): # wxGlade: MyFrame. + + if self.showfiles.GetValue() == 1: + self.scroll.ShowFileNames(True) + self.log.write("OnShowFiles: Thumbs file names shown\n") + else: + self.scroll.ShowFileNames(False) + self.log.write("OnShowFiles: Thumbs file names not shown\n") + + self.scroll.Refresh() + + event.Skip() + + + def OnEnableDragging(self, event): + + if self.enabledragging.GetValue() == 1: + self.scroll.EnableDragging(True) + self.log.write("OnEnableDragging: Thumbs drag and drop enabled\n") + else: + self.scroll.EnableDragging(False) + self.log.write("OnEnableDragging: Thumbs drag and drop disabled\n") + + self.scroll.Refresh() + + event.Skip() + + + def OnSetPopup(self, event): # wxGlade: MyFrame. + + if self.setpopup.GetValue() == 1: + menu = self.CreatePopups() + self.scroll.SetPopupMenu(menu) + self.log.write("OnSetPopup: Popups enabled on thumbs\n") + else: + self.scroll.SetPopupMenu(None) + self.log.write("OnSetPopup: Popups disabled on thumbs\n") + + event.Skip() + + + def OnSetGlobalPopup(self, event): + + if self.setgpopup.GetValue() == 1: + menu = self.CreateGlobalPopups() + self.scroll.SetGlobalPopupMenu(menu) + self.log.write("OnSetGlobalPopup: Popups enabled globally (no selection needed)\n") + else: + self.scroll.SetGlobalPopupMenu(None) + self.log.write("OnSetGlobalPopup: Popups disabled globally (no selection needed)\n") + + event.Skip() + + + def OnEnableToolTips(self, event): + + if self.enabletooltip.GetValue() == 1: + self.log.write("OnEnableToolTips: File information on tooltips enabled\n") + self.scroll.EnableToolTips(True) + else: + self.log.write("OnEnableToolTips: File information on tooltips disabled\n") + self.scroll.EnableToolTips(False) + + event.Skip() + + + def OnSetZoom(self, event): # wxGlade: MyFrame. + + val = self.textzoom.GetValue().strip() + + try: + val = float(val) + except: + errstr = "Error: a float value is required." + dlg = wx.MessageDialog(self, errstr, self.name + " Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + self.textzoom.SetValue("1.4") + return + + + if val < 1.0: + errstr = "Error: zoom factor must be grater than 1.0." + dlg = wx.MessageDialog(self, errstr, self.name + " Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + self.textzoom.SetValue("1.4") + return + + self.scroll.SetZoomFactor(val) + + event.Skip() + + def OnSetThumbSize(self, event): + try: + width = int(self.textthumbwidth.GetValue().strip()) + height = int(self.textthumbheight.GetValue().strip()) + except: + errstr = "Error: thumb size must be integers (min 50x50)." + dlg = wx.MessageDialog(self, errstr, self.name + " Error", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return + + width = max(width, 50) + height = max(height, 50) + self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) + self.scroll.SetThumbSize (width, height) + + event.Skip() + + def OnSelChanged(self, event): + + self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.scroll.GetSelection())) + event.Skip() + + + def OnPointed(self, event): + + self.log.write("OnPointed: Thumb pointed: %s\n"%self.scroll.GetPointed()) + event.Skip() + + + def OnDClick(self, event): + + self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.scroll.GetSelection()) + event.Skip() + + + def OnSetFont(self, event): # wxGlade: MyFrame. + + data = wx.FontData() + data.EnableEffects(True) + data.SetInitialFont(self.scroll.GetCaptionFont()) + + dlg = wx.FontDialog(self, data) + + if dlg.ShowModal() == wx.ID_OK: + data = dlg.GetFontData() + font = data.GetChosenFont() + self.scroll.SetCaptionFont(font) + self.scroll.Refresh() + self.log.write("OnSetFont: Caption font changed\n") + + # Don't destroy the dialog until you get everything you need from the + # dialog! + dlg.Destroy() + event.Skip() + + + def OnSetColour(self, event): + + dlg = wx.ColourDialog(self) + + # Ensure the full colour dialog is displayed, + # not the abbreviated version. + dlg.GetColourData().SetChooseFull(True) + + if dlg.ShowModal() == wx.ID_OK: + + # If the user selected OK, then the dialog's wx.ColourData will + # contain valid information. Fetch the data ... + data = dlg.GetColourData() + + # ... then do something with it. The actual colour data will be + # returned as a three-tuple (r, g, b) in this particular case. + colour = data.GetColour().Get() + colour = wx.Colour(colour[0], colour[1], colour[2]) + self.scroll.SetSelectionColour(colour) + self.scroll.Refresh() + + # Once the dialog is destroyed, Mr. wx.ColourData is no longer your + # friend. Don't use it again! + dlg.Destroy() + + + def CreatePopups(self): + + if not hasattr(self, "popupID1"): + self.popupID1 = wx.NewIdRef() + self.popupID2 = wx.NewIdRef() + self.popupID3 = wx.NewIdRef() + self.popupID4 = wx.NewIdRef() + self.popupID5 = wx.NewIdRef() + self.popupID6 = wx.NewIdRef() + self.popupID7 = wx.NewIdRef() + self.popupID8 = wx.NewIdRef() + self.popupID9 = wx.NewIdRef() + self.popupID10 = wx.NewIdRef() + self.popupID11 = wx.NewIdRef() + self.popupID12 = wx.NewIdRef() + + self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1) + self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2) + self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3) + self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4) + self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5) + self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6) + self.Bind(wx.EVT_MENU, self.OnPopupSeven, id=self.popupID7) + self.Bind(wx.EVT_MENU, self.OnPopupEight, id=self.popupID8) + self.Bind(wx.EVT_MENU, self.OnPopupNine, id=self.popupID9) + + menu = wx.Menu() + item = wx.MenuItem(menu, self.popupID1, "One") + img = images.Mondrian.GetImage() + img.Rescale(16, 16) + bmp = img.ConvertToBitmap() + item.SetBitmap(bmp) + menu.Append(item) + + # add some other items + menu.Append(self.popupID2, "Two") + menu.Append(self.popupID3, "Three") + menu.Append(self.popupID4, "Four") + menu.Append(self.popupID5, "Five") + menu.Append(self.popupID6, "Six") + # make a submenu + sm = wx.Menu() + sm.Append(self.popupID8, "Sub Item 1") + sm.Append(self.popupID9, "Sub Item 1") + menu.Append(self.popupID7, "Test Submenu", sm) + + return menu + + + def CreateGlobalPopups(self): + + if not hasattr(self, "popupID10"): + self.popupID10 = wx.NewIdRef() + self.popupID11 = wx.NewIdRef() + self.popupID12 = wx.NewIdRef() + + self.Bind(wx.EVT_MENU, self.OnPopupTen, id=self.popupID10) + self.Bind(wx.EVT_MENU, self.OnPopupEleven, id=self.popupID11) + self.Bind(wx.EVT_MENU, self.OnPopupTwelve, id=self.popupID12) + + menu = wx.Menu() + item = wx.MenuItem(menu, self.popupID10, "Select all") + menu.Append(item) + menu.AppendSeparator() + + item = wx.MenuItem(menu, self.popupID11, "Say Hello!") + img = images.Mondrian.GetImage() + img.Rescale(16, 16) + bmp = img.ConvertToBitmap() + item.SetBitmap(bmp) + menu.Append(item) + menu.AppendSeparator() + + menu.Append(self.popupID12, "Get thumbs count") + + return menu + + + def OnPopupOne(self, event): + self.log.write("OnPopupMenu: Popup One\n") + + + def OnPopupTwo(self, event): + self.log.write("OnPopupMenu: Popup Two\n") + + + def OnPopupThree(self, event): + self.log.write("OnPopupMenu: Popup Three\n") + + + def OnPopupFour(self, event): + self.log.write("OnPopupMenu: Popup Four\n") + + + def OnPopupFive(self, event): + self.log.write("OnPopupMenu: Popup Five\n") + + + def OnPopupSix(self, event): + self.log.write("OnPopupMenu: Popup Six\n") + + + def OnPopupSeven(self, event): + self.log.write("OnPopupMenu: Popup Seven\n") + + + def OnPopupEight(self, event): + self.log.write("OnPopupMenu: Popup Eight\n") + + + def OnPopupNine(self, event): + self.log.write("OnPopupMenu: Popup Nine\n") + + + def OnPopupTen(self, event): + + items = self.scroll.GetItemCount() + self.log.write("Items", items, type(items)) + + for ii in range(items): + self.scroll.SetSelection(ii) + + self.log.write("OnGlobalPopupMenu: all thumbs selected\n") + + event.Skip() + + + def OnPopupEleven(self, event): + + self.log.write("OnGlobalPopupMenu: say hello message...\n") + + msgstr = "Info: let's say hello to wxPython! " + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + event.Skip() + + + def OnPopupTwelve(self, event): + + items = self.scroll.GetItemCount() + self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) + + msgstr = "Info: number of thumbs: %d"%items + dlg = wx.MessageDialog(self, msgstr, self.name + " Info", + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + event.Skip() + + + def DoComboCheckbox(self): + pass + + def DoBindCombo(self): + pass + + def DoAddCombo(self, customsizer): + pass + diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index 79759578..40a657ed 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -14,579 +14,11 @@ sys.path.append(os.path.split(dirName)[0]) import wx.lib.agw.thumbnailctrl as TC -import images - from wx.lib.agw.scrolledthumbnail import (EVT_THUMBNAILS_SEL_CHANGED, EVT_THUMBNAILS_POINTED, EVT_THUMBNAILS_DCLICK) +from ThumbDemoConfig import ThumbDemoConfig -class ThumbDemoConfig(wx.Frame): - - def __init__(self, parent, log, name, about): - - wx.Frame.__init__(self, parent, size=(850,800)) - self.name = name - self.about = about - - self.SetIcon(images.Mondrian.GetIcon()) - self.SetTitle(self.name + " wxPython Demo ;-)") - - self.statusbar = self.CreateStatusBar(2) - self.statusbar.SetStatusWidths([-2, -1]) - # statusbar fields - statusbar_fields = [(self.name + " Demo, Michael Eager @ 10 Nov 2020"), - ("Welcome To wxPython!")] - - for i in range(len(statusbar_fields)): - self.statusbar.SetStatusText(statusbar_fields[i], i) - - self.SetMenuBar(self.CreateMenuBar()) - - self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | - wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) - self.panel = wx.Panel(self.splitter, -1) - - sizer = wx.BoxSizer(wx.HORIZONTAL) - - self.SetScroll() - - self.log = log - - self.thumbsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Style") - self.customsizer_staticbox = wx.StaticBox(self.panel, -1, "Thumb Customization") - self.optionsizer_staticbox = wx.StaticBox(self.panel, -1, "More Options") - self.dirsizer_staticbox = wx.StaticBox(self.panel, -1, "Directory Selection") - self.dirbutton = wx.Button(self.panel, -1, "Change Directory") - self.radiostyle1 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_NONE", style=wx.RB_GROUP) - self.radiostyle2 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_FULL") - self.radiostyle3 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_RECT") - self.radiostyle4 = wx.RadioButton(self.panel, -1, "THUMB_OUTLINE_IMAGE") - self.highlight = wx.CheckBox(self.panel, -1, "Highlight on pointing") - self.showfiles = wx.CheckBox(self.panel, -1, "Show file names") - self.enabledragging = wx.CheckBox(self.panel, -1, "Enable drag and drop") - self.setpopup = wx.CheckBox(self.panel, -1, "Set popup menu on thumbs") - self.setgpopup = wx.CheckBox(self.panel, -1, "Set global popup menu") - self.DoComboCheckbox() - self.enabletooltip = wx.CheckBox(self.panel, -1, "Enable thumb tooltips") - self.textzoom = wx.TextCtrl(self.panel, -1, "1.4") - self.zoombutton = wx.Button(self.panel, -1, "Set zoom factor") - self.textthumbwidth = wx.TextCtrl(self.panel, -1, "96") - self.textthumbheight = wx.TextCtrl(self.panel, -1, "80") - self.thumbsizebutton = wx.Button(self.panel, -1, "Set thumbnail size (WxH)") - self.fontbutton = wx.Button(self.panel, -1, "Set caption font") - self.colourbutton = wx.Button(self.panel, -1, "Set selection colour") - - self.radios = [self.radiostyle1, self.radiostyle2, self.radiostyle3, - self.radiostyle4] - self.thumbstyles = ["THUMB_OUTLINE_NONE", "THUMB_OUTLINE_FULL", "THUMB_OUTLINE_RECT", - "THUMB_OUTLINE_IMAGE"] - - self.SetProperties() - self.DoLayout() - - self.panel.SetSizer(sizer) - sizer.Layout() - - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle4) - self.Bind(wx.EVT_CHECKBOX, self.OnHighlight, self.highlight) - self.Bind(wx.EVT_CHECKBOX, self.OnShowFiles, self.showfiles) - self.Bind(wx.EVT_CHECKBOX, self.OnEnableDragging, self.enabledragging) - self.Bind(wx.EVT_CHECKBOX, self.OnSetPopup, self.setpopup) - self.Bind(wx.EVT_CHECKBOX, self.OnSetGlobalPopup, self.setgpopup) - self.DoBindCombo() - self.Bind(wx.EVT_CHECKBOX, self.OnEnableToolTips, self.enabletooltip) - self.Bind(wx.EVT_BUTTON, self.OnSetZoom, self.zoombutton) - self.Bind(wx.EVT_BUTTON, self.OnSetThumbSize, self.thumbsizebutton) - self.Bind(wx.EVT_BUTTON, self.OnSetFont, self.fontbutton) - self.Bind(wx.EVT_BUTTON, self.OnSetColour, self.colourbutton) - self.Bind(wx.EVT_BUTTON, self.OnSetDirectory, self.dirbutton) - - self.scroll.Bind(EVT_THUMBNAILS_SEL_CHANGED, self.OnSelChanged) - self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) - self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) - - self.splitter.SplitVertically(self.scroll, self.panel, 300) - - self.splitter.SetMinimumPaneSize(140) - self.CenterOnScreen() - - - def SetProperties(self): - - self.radiostyle4.SetValue(1) - self.showfiles.SetValue(1) - - - def DoLayout(self): - - splitsizer = wx.BoxSizer(wx.VERTICAL) - optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) - zoomsizer = wx.BoxSizer(wx.HORIZONTAL) - thumbsizesizer = wx.BoxSizer(wx.HORIZONTAL) - customsizer = wx.StaticBoxSizer(self.customsizer_staticbox, wx.VERTICAL) - thumbsizer = wx.StaticBoxSizer(self.thumbsizer_staticbox, wx.VERTICAL) - radiosizer = wx.BoxSizer(wx.VERTICAL) - dirsizer = wx.StaticBoxSizer(self.dirsizer_staticbox, wx.HORIZONTAL) - dirsizer.Add(self.dirbutton, 0, wx.LEFT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(dirsizer, 0, wx.EXPAND|wx.TOP|wx.LEFT, 5) - radiosizer.Add(self.radiostyle1, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle2, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle3, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) - radiosizer.Add(self.radiostyle4, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - thumbsizer.Add(radiosizer, 1, wx.EXPAND, 0) - splitsizer.Add(thumbsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) - customsizer.Add(self.highlight, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.showfiles, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.setpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - customsizer.Add(self.setgpopup, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - self.DoAddCombo(customsizer) - customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) - zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.textthumbheight, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - thumbsizesizer.Add(self.thumbsizebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) - optionsizer.Add(zoomsizer, 1, wx.EXPAND, 0) - optionsizer.Add(thumbsizesizer, 1, wx.EXPAND, 0) - optionsizer.Add(self.fontbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 3) - optionsizer.Add(self.colourbutton, 0, wx.TOP|wx.LEFT|wx.ADJUST_MINSIZE, 3) - splitsizer.Add(optionsizer, 0, wx.EXPAND | wx.TOP|wx.LEFT, 5) - self.panel.SetSizer(splitsizer) - splitsizer.Fit(self.panel) - - - def CreateMenuBar(self): - - file_menu = wx.Menu() - - AS_EXIT = wx.NewIdRef() - file_menu.Append(AS_EXIT, "&Exit") - self.Bind(wx.EVT_MENU, self.OnClose, id=AS_EXIT) - - help_menu = wx.Menu() - - AS_ABOUT = wx.NewIdRef() - help_menu.Append(AS_ABOUT, "&About...") - self.Bind(wx.EVT_MENU, self.OnAbout, id=AS_ABOUT) - - menu_bar = wx.MenuBar() - - menu_bar.Append(file_menu, "&File") - menu_bar.Append(help_menu, "&Help") - - return menu_bar - - - def OnClose(self, event): - - self.Destroy() - - - def OnAbout(self, event): - - dlg = wx.MessageDialog(self, self.about, self.name + " Demo", - wx.OK | wx.ICON_INFORMATION) - - dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False)) - dlg.ShowModal() - dlg.Destroy() - - - def OnSetDirectory(self, event): - - dlg = wx.DirDialog(self, "Choose a directory with images:", - defaultPath=os.getcwd(), - style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON) - - # If the user selects OK, then we process the dialog's data. - # This is done by getting the path data from the dialog - BEFORE - # we destroy it. - if dlg.ShowModal() == wx.ID_OK: - self.ShowDir(dlg.GetPath()) - self.log.write("OnSetDirectory: directory changed to: %s\n"%dlg.GetPath()) - - # Only destroy a dialog after you're done with it. - dlg.Destroy() - - - def OnChangeOutline(self, event): # wxGlade: MyFrame. - - radio = event.GetEventObject() - pos = self.radios.index(radio) - - if pos == 0: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_NONE) - elif pos == 1: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_FULL) - elif pos == 2: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_RECT) - elif pos == 3: - self.scroll.SetThumbOutline(TC.THUMB_OUTLINE_IMAGE) - - self.scroll.Refresh() - - self.log.write("OnChangeOutline: Outline changed to: %s\n"%self.thumbstyles[pos]) - event.Skip() - - - def OnHighlight(self, event): # wxGlade: MyFrame. - - if self.highlight.GetValue() == 1: - self.scroll.SetHighlightPointed(True) - self.log.write("OnHighlight: Highlight thumbs on pointing\n") - else: - self.scroll.SetHighlightPointed(False) - self.log.write("OnHighlight: Don't Highlight thumbs on pointing\n") - - event.Skip() - - - def OnShowFiles(self, event): # wxGlade: MyFrame. - - if self.showfiles.GetValue() == 1: - self.scroll.ShowFileNames(True) - self.log.write("OnShowFiles: Thumbs file names shown\n") - else: - self.scroll.ShowFileNames(False) - self.log.write("OnShowFiles: Thumbs file names not shown\n") - - self.scroll.Refresh() - - event.Skip() - - - def OnEnableDragging(self, event): - - if self.enabledragging.GetValue() == 1: - self.scroll.EnableDragging(True) - self.log.write("OnEnableDragging: Thumbs drag and drop enabled\n") - else: - self.scroll.EnableDragging(False) - self.log.write("OnEnableDragging: Thumbs drag and drop disabled\n") - - self.scroll.Refresh() - - event.Skip() - - - def OnSetPopup(self, event): # wxGlade: MyFrame. - - if self.setpopup.GetValue() == 1: - menu = self.CreatePopups() - self.scroll.SetPopupMenu(menu) - self.log.write("OnSetPopup: Popups enabled on thumbs\n") - else: - self.scroll.SetPopupMenu(None) - self.log.write("OnSetPopup: Popups disabled on thumbs\n") - - event.Skip() - - - def OnSetGlobalPopup(self, event): - - if self.setgpopup.GetValue() == 1: - menu = self.CreateGlobalPopups() - self.scroll.SetGlobalPopupMenu(menu) - self.log.write("OnSetGlobalPopup: Popups enabled globally (no selection needed)\n") - else: - self.scroll.SetGlobalPopupMenu(None) - self.log.write("OnSetGlobalPopup: Popups disabled globally (no selection needed)\n") - - event.Skip() - - - def OnEnableToolTips(self, event): - - if self.enabletooltip.GetValue() == 1: - self.log.write("OnEnableToolTips: File information on tooltips enabled\n") - self.scroll.EnableToolTips(True) - else: - self.log.write("OnEnableToolTips: File information on tooltips disabled\n") - self.scroll.EnableToolTips(False) - - event.Skip() - - - def OnSetZoom(self, event): # wxGlade: MyFrame. - - val = self.textzoom.GetValue().strip() - - try: - val = float(val) - except: - errstr = "Error: a float value is required." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - self.textzoom.SetValue("1.4") - return - - - if val < 1.0: - errstr = "Error: zoom factor must be grater than 1.0." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - self.textzoom.SetValue("1.4") - return - - self.scroll.SetZoomFactor(val) - - event.Skip() - - def OnSetThumbSize(self, event): - try: - width = int(self.textthumbwidth.GetValue().strip()) - height = int(self.textthumbheight.GetValue().strip()) - except: - errstr = "Error: thumb size must be integers (min 50x50)." - dlg = wx.MessageDialog(self, errstr, self.name + " Error", - wx.OK | wx.ICON_ERROR) - dlg.ShowModal() - dlg.Destroy() - return - - width = max(width, 50) - height = max(height, 50) - self.log.write("OnSetThumbSize: (%s, %s)\n" % (width, height)) - self.scroll.SetThumbSize (width, height) - - event.Skip() - - def OnSelChanged(self, event): - - self.log.write("OnSelChanged: Thumb selected: %s\n"%str(self.scroll.GetSelection())) - event.Skip() - - - def OnPointed(self, event): - - self.log.write("OnPointed: Thumb pointed: %s\n"%self.scroll.GetPointed()) - event.Skip() - - - def OnDClick(self, event): - - self.log.write("OnDClick: Thumb double-clicked: %s\n"%self.scroll.GetSelection()) - event.Skip() - - - def OnSetFont(self, event): # wxGlade: MyFrame. - - data = wx.FontData() - data.EnableEffects(True) - data.SetInitialFont(self.scroll.GetCaptionFont()) - - dlg = wx.FontDialog(self, data) - - if dlg.ShowModal() == wx.ID_OK: - data = dlg.GetFontData() - font = data.GetChosenFont() - self.scroll.SetCaptionFont(font) - self.scroll.Refresh() - self.log.write("OnSetFont: Caption font changed\n") - - # Don't destroy the dialog until you get everything you need from the - # dialog! - dlg.Destroy() - event.Skip() - - - def OnSetColour(self, event): - - dlg = wx.ColourDialog(self) - - # Ensure the full colour dialog is displayed, - # not the abbreviated version. - dlg.GetColourData().SetChooseFull(True) - - if dlg.ShowModal() == wx.ID_OK: - - # If the user selected OK, then the dialog's wx.ColourData will - # contain valid information. Fetch the data ... - data = dlg.GetColourData() - - # ... then do something with it. The actual colour data will be - # returned as a three-tuple (r, g, b) in this particular case. - colour = data.GetColour().Get() - colour = wx.Colour(colour[0], colour[1], colour[2]) - self.scroll.SetSelectionColour(colour) - self.scroll.Refresh() - - # Once the dialog is destroyed, Mr. wx.ColourData is no longer your - # friend. Don't use it again! - dlg.Destroy() - - - def CreatePopups(self): - - if not hasattr(self, "popupID1"): - self.popupID1 = wx.NewIdRef() - self.popupID2 = wx.NewIdRef() - self.popupID3 = wx.NewIdRef() - self.popupID4 = wx.NewIdRef() - self.popupID5 = wx.NewIdRef() - self.popupID6 = wx.NewIdRef() - self.popupID7 = wx.NewIdRef() - self.popupID8 = wx.NewIdRef() - self.popupID9 = wx.NewIdRef() - self.popupID10 = wx.NewIdRef() - self.popupID11 = wx.NewIdRef() - self.popupID12 = wx.NewIdRef() - - self.Bind(wx.EVT_MENU, self.OnPopupOne, id=self.popupID1) - self.Bind(wx.EVT_MENU, self.OnPopupTwo, id=self.popupID2) - self.Bind(wx.EVT_MENU, self.OnPopupThree, id=self.popupID3) - self.Bind(wx.EVT_MENU, self.OnPopupFour, id=self.popupID4) - self.Bind(wx.EVT_MENU, self.OnPopupFive, id=self.popupID5) - self.Bind(wx.EVT_MENU, self.OnPopupSix, id=self.popupID6) - self.Bind(wx.EVT_MENU, self.OnPopupSeven, id=self.popupID7) - self.Bind(wx.EVT_MENU, self.OnPopupEight, id=self.popupID8) - self.Bind(wx.EVT_MENU, self.OnPopupNine, id=self.popupID9) - - menu = wx.Menu() - item = wx.MenuItem(menu, self.popupID1, "One") - img = images.Mondrian.GetImage() - img.Rescale(16, 16) - bmp = img.ConvertToBitmap() - item.SetBitmap(bmp) - menu.Append(item) - - # add some other items - menu.Append(self.popupID2, "Two") - menu.Append(self.popupID3, "Three") - menu.Append(self.popupID4, "Four") - menu.Append(self.popupID5, "Five") - menu.Append(self.popupID6, "Six") - # make a submenu - sm = wx.Menu() - sm.Append(self.popupID8, "Sub Item 1") - sm.Append(self.popupID9, "Sub Item 1") - menu.Append(self.popupID7, "Test Submenu", sm) - - return menu - - - def CreateGlobalPopups(self): - - if not hasattr(self, "popupID10"): - self.popupID10 = wx.NewIdRef() - self.popupID11 = wx.NewIdRef() - self.popupID12 = wx.NewIdRef() - - self.Bind(wx.EVT_MENU, self.OnPopupTen, id=self.popupID10) - self.Bind(wx.EVT_MENU, self.OnPopupEleven, id=self.popupID11) - self.Bind(wx.EVT_MENU, self.OnPopupTwelve, id=self.popupID12) - - menu = wx.Menu() - item = wx.MenuItem(menu, self.popupID10, "Select all") - menu.Append(item) - menu.AppendSeparator() - - item = wx.MenuItem(menu, self.popupID11, "Say Hello!") - img = images.Mondrian.GetImage() - img.Rescale(16, 16) - bmp = img.ConvertToBitmap() - item.SetBitmap(bmp) - menu.Append(item) - menu.AppendSeparator() - - menu.Append(self.popupID12, "Get thumbs count") - - return menu - - - def OnPopupOne(self, event): - self.log.write("OnPopupMenu: Popup One\n") - - - def OnPopupTwo(self, event): - self.log.write("OnPopupMenu: Popup Two\n") - - - def OnPopupThree(self, event): - self.log.write("OnPopupMenu: Popup Three\n") - - - def OnPopupFour(self, event): - self.log.write("OnPopupMenu: Popup Four\n") - - - def OnPopupFive(self, event): - self.log.write("OnPopupMenu: Popup Five\n") - - - def OnPopupSix(self, event): - self.log.write("OnPopupMenu: Popup Six\n") - - - def OnPopupSeven(self, event): - self.log.write("OnPopupMenu: Popup Seven\n") - - - def OnPopupEight(self, event): - self.log.write("OnPopupMenu: Popup Eight\n") - - - def OnPopupNine(self, event): - self.log.write("OnPopupMenu: Popup Nine\n") - - - def OnPopupTen(self, event): - - items = self.scroll.GetItemCount() - self.log.write("Items", items, type(items)) - - for ii in range(items): - self.scroll.SetSelection(ii) - - self.log.write("OnGlobalPopupMenu: all thumbs selected\n") - - event.Skip() - - - def OnPopupEleven(self, event): - - self.log.write("OnGlobalPopupMenu: say hello message...\n") - - msgstr = "Info: let's say hello to wxPython! " - dlg = wx.MessageDialog(self, msgstr, self.name + " Info", - wx.OK | wx.ICON_INFORMATION) - dlg.ShowModal() - dlg.Destroy() - - event.Skip() - - - def OnPopupTwelve(self, event): - - items = self.scroll.GetItemCount() - self.log.write("OnGlobalPopupMenu: number of thumbs: %d\n"%items) - - msgstr = "Info: number of thumbs: %d"%items - dlg = wx.MessageDialog(self, msgstr, self.name + " Info", - wx.OK | wx.ICON_INFORMATION) - dlg.ShowModal() - dlg.Destroy() - - event.Skip() - - - def DoComboCheckbox(self): - pass - - def DoBindCombo(self): - pass - - def DoAddCombo(self, customsizer): - pass class ThumbnailCtrlDemo(ThumbDemoConfig): From 4bef428bce46c4cf6828517abf127f5e890bb912 Mon Sep 17 00:00:00 2001 From: eagerm Date: Mon, 16 Nov 2020 09:23:16 -0800 Subject: [PATCH 09/10] Add documentation to demo programs. --- demo/agw/ScrolledThumbnail.py | 35 ++++++++++++++++++++++++++++++ demo/agw/ThumbDemoConfig.py | 23 ++++++++++++++++++++ demo/agw/ThumbnailCtrl.py | 40 ++++++++++++++++++++++++++++++----- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/demo/agw/ScrolledThumbnail.py b/demo/agw/ScrolledThumbnail.py index 578fa87d..3bfb39ac 100644 --- a/demo/agw/ScrolledThumbnail.py +++ b/demo/agw/ScrolledThumbnail.py @@ -22,6 +22,35 @@ sys.path.append(os.path.split(dirName)[0]) from ThumbDemoConfig import ThumbDemoConfig class ScrolledThumbnailDemo (ThumbDemoConfig): + """Demo program for ScrolledThumbnail widget. + + :class:`ScrolledThumbnail` provides a scrollable window containing + thumbnail images. These thumbnails are provided to the widget in + an array of :class:`Thumb` objects. The images can be selected, + resized, or rotated. Optionally, tooltips can provide information + about the image file or popups can be displayed when a thumbnail is + selected. + + The included :class:`Thumb` supports image files like JPEG, GIF, or + PNG, using either native Python or PIL functions. This class can + be extended by the user to support other types, for example, to + provide a thumbnail of a TXT or PDF file. + + :class:`ScrolledThumbnail` is based on :class:`ThumbnailCtrl`, with + the difference that the latter is essentially an image browser + application, performing file operations as well as filtering and + sorting image files. :class:`ScrolledThumbnail` contains only the + support for displaying thumbnail images in a scrolling window, with + generating thumbnails, reading files, and other operations which + are not related to displaying the thumbnail to either the user of + the class (e.g., this demo), or to support classes such as + :class:`Thumb`. + + For full documentation, see the comments in agw/scrolledthumbnail.py. + + This class extends the common code in ThumbDemoConfig to work with + the :class:`ScrolledThumbnail` widget. + """ def __init__(self, parent, log): @@ -39,10 +68,16 @@ class ScrolledThumbnailDemo (ThumbDemoConfig): super().__init__ (parent, log, name=name, about=msg) + # Create a ScrolledThumbnail panel in the left side of the splitter. def SetScroll(self): self.scroll = ScrolledThumbnail(self.splitter, -1, size=(400,300)) + # Display a directory of images in the ScrolledThumbnail window. + # Read each file name, filter by desired type (jpg, gif, png) and + # create a Thumb (including specifying image support class). Add + # this to the array 'thumbs' and pass to ScrolledThumbnail widget + # for display. def ShowDir(self, dir): files = os.listdir(dir) thumbs = [] diff --git a/demo/agw/ThumbDemoConfig.py b/demo/agw/ThumbDemoConfig.py index e7dc2a2a..69ec4aae 100644 --- a/demo/agw/ThumbDemoConfig.py +++ b/demo/agw/ThumbDemoConfig.py @@ -12,6 +12,23 @@ from wx.lib.agw.scrolledthumbnail import (ScrolledThumbnail, EVT_THUMBNAILS_DCLICK) class ThumbDemoConfig(wx.Frame): + """ScrolledThumbnail or ThumbnailCtrl demo common code + + This class contains code common to both the ScrolledThumbnail and + the ThumbnailCtrl demos. It is extended by both of these demos to + address the differences in invoking :class:`ScrolledThumbnail` + or :class:`ThumbnailCtrl` widgets. + + This class creates a SplitterWindow with the left half containing + the widget being demoed and the right half containing a number of + controls which set or change operation of the widget. In most + this simply involves passing the user-specified value to the + widget. + + For information about what setting does, as well as other settings, + set the documentation for :class:`ScrolledThumbnail` or + :class:`ThumbnailCtrl`. + """ def __init__(self, parent, log, name, about): @@ -33,12 +50,15 @@ class ThumbDemoConfig(wx.Frame): self.SetMenuBar(self.CreateMenuBar()) + # Create SplitterWindow with panels for widget and controls. self.splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) self.panel = wx.Panel(self.splitter, -1) sizer = wx.BoxSizer(wx.HORIZONTAL) + # Call SetScroll() to create thumbnail widget. + # This is provided by each of the two demos. self.SetScroll() self.log = log @@ -78,6 +98,7 @@ class ThumbDemoConfig(wx.Frame): self.panel.SetSizer(sizer) sizer.Layout() + self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) @@ -99,6 +120,7 @@ class ThumbDemoConfig(wx.Frame): self.scroll.Bind(EVT_THUMBNAILS_POINTED, self.OnPointed) self.scroll.Bind(EVT_THUMBNAILS_DCLICK, self.OnDClick) + # Add thumbnail widget and control panel to SplitterWindow. self.splitter.SplitVertically(self.scroll, self.panel, 300) self.splitter.SetMinimumPaneSize(140) @@ -112,6 +134,7 @@ class ThumbDemoConfig(wx.Frame): def DoLayout(self): + """Layout controls.""" splitsizer = wx.BoxSizer(wx.VERTICAL) optionsizer = wx.StaticBoxSizer(self.optionsizer_staticbox, wx.VERTICAL) diff --git a/demo/agw/ThumbnailCtrl.py b/demo/agw/ThumbnailCtrl.py index 40a657ed..1b6d4a48 100644 --- a/demo/agw/ThumbnailCtrl.py +++ b/demo/agw/ThumbnailCtrl.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +# ThumbnailCtrl Demo Michael Eager @ 2020 Oct 23 +# Adapted from ThumbnailCtrl Demo Andrea Gavana @ 10 Dec 2005 + import wx import os @@ -21,6 +24,29 @@ from ThumbDemoConfig import ThumbDemoConfig class ThumbnailCtrlDemo(ThumbDemoConfig): + """Demo program for ThumbnailCtrl widget. + + :class:`ThumbnailCtrl` provides an image browser widget in a + scrollable window containing thumbnail images. The class reads + the specified directory, filters by image type, and create + thumbnail images. The images can be selected, resized, or rotated. + Files can be deleted. Optionally, tooltips can provide information + about the image file or popups can be displayed when a thumbnail is + selected. + + The included :class:`Thumb` supports image files like JPEG, GIF, or + PNG, using either native Python or PIL functions. + + :class:`ScrolledThumbnail` is used by :class:`ThumbnailCtrl`, to + provide the scrolling thumbnail window. Many of the methods of + :class:`ThumbnailCtrl` are actually delegated to + :class:`ScrolledThumbnail`. + + For full documentation, see the comments in agw/thumbnailctrl.py. + + This class extends the common code in ThumbDemoConfig to work with + the :class:`ThumbnailCtrl` widget. + """ def __init__(self, parent, log): @@ -37,11 +63,15 @@ class ThumbnailCtrlDemo(ThumbDemoConfig): super().__init__ (parent, log, name=name, about=msg) + # Create ThumbnailCtrl in the left side of the splitter window. + # Default: Use native image handling functions, edit to use PIL. + # Call ThumbnailCtrl:ShowDir() to read directory and display images. def SetScroll(self): self.scroll = TC.ThumbnailCtrl(self.splitter, -1, imagehandler=TC.NativeImageHandler) #scroll = TC.ThumbnailCtrl(self.splitter, -1, imagehandler=TC.PILImageHandler) + # Display file names with thumbnails. self.scroll.ShowFileNames() if os.path.isdir("../bitmaps"): self.scroll.ShowDir(os.path.normpath(os.getcwd() + "/../bitmaps")) @@ -49,20 +79,17 @@ class ThumbnailCtrlDemo(ThumbDemoConfig): self.scroll.ShowDir(os.getcwd()) + # Following three functions override dummy functions in ThumbDemoConfig + # to add checkbox for displaying folder path in ThumbnailCtrl widget. def DoComboCheckbox(self): self.showcombo = wx.CheckBox(self.panel, -1, "Show folder combobox") - def DoBindCombo(self): self.Bind(wx.EVT_CHECKBOX, self.OnShowComboBox, self.showcombo) def DoAddCombo(self, customsizer): customsizer.Add(self.showcombo, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) - def ShowDir(self, dir): - self.scroll.ShowDir(dir) - - def OnShowComboBox(self, event): if self.showcombo.GetValue() == 1: @@ -74,6 +101,9 @@ class ThumbnailCtrlDemo(ThumbDemoConfig): event.Skip() + def ShowDir(self, dir): + self.scroll.ShowDir(dir) + #--------------------------------------------------------------------------- From 846a4f4e4acd180eba23632a2b182f542ab26a11 Mon Sep 17 00:00:00 2001 From: eagerm Date: Mon, 16 Nov 2020 12:31:00 -0800 Subject: [PATCH 10/10] Remove unused sizer, add spacing to controls. --- demo/agw/ThumbDemoConfig.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/demo/agw/ThumbDemoConfig.py b/demo/agw/ThumbDemoConfig.py index 69ec4aae..392192d5 100644 --- a/demo/agw/ThumbDemoConfig.py +++ b/demo/agw/ThumbDemoConfig.py @@ -32,7 +32,7 @@ class ThumbDemoConfig(wx.Frame): def __init__(self, parent, log, name, about): - wx.Frame.__init__(self, parent, size=(850,800)) + wx.Frame.__init__(self, parent, size=(850,820)) self.name = name self.about = about @@ -55,8 +55,6 @@ class ThumbDemoConfig(wx.Frame): wx.SP_3D | wx.WANTS_CHARS | wx.SP_LIVE_UPDATE) self.panel = wx.Panel(self.splitter, -1) - sizer = wx.BoxSizer(wx.HORIZONTAL) - # Call SetScroll() to create thumbnail widget. # This is provided by each of the two demos. self.SetScroll() @@ -95,10 +93,6 @@ class ThumbDemoConfig(wx.Frame): self.SetProperties() self.DoLayout() - self.panel.SetSizer(sizer) - sizer.Layout() - - self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle1) self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle2) self.Bind(wx.EVT_RADIOBUTTON, self.OnChangeOutline, self.radiostyle3) @@ -146,12 +140,14 @@ class ThumbDemoConfig(wx.Frame): dirsizer = wx.StaticBoxSizer(self.dirsizer_staticbox, wx.HORIZONTAL) dirsizer.Add(self.dirbutton, 0, wx.LEFT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) splitsizer.Add(dirsizer, 0, wx.EXPAND|wx.TOP|wx.LEFT, 5) + splitsizer.AddSpacer(15) radiosizer.Add(self.radiostyle1, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) radiosizer.Add(self.radiostyle2, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) radiosizer.Add(self.radiostyle3, 0, wx.LEFT|wx.TOP|wx.ADJUST_MINSIZE, 3) radiosizer.Add(self.radiostyle4, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) thumbsizer.Add(radiosizer, 1, wx.EXPAND, 0) splitsizer.Add(thumbsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + splitsizer.AddSpacer(15) customsizer.Add(self.highlight, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.showfiles, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) customsizer.Add(self.enabledragging, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) @@ -160,6 +156,7 @@ class ThumbDemoConfig(wx.Frame): self.DoAddCombo(customsizer) customsizer.Add(self.enabletooltip, 0, wx.LEFT|wx.BOTTOM|wx.ADJUST_MINSIZE, 3) splitsizer.Add(customsizer, 0, wx.TOP|wx.EXPAND|wx.LEFT, 5) + splitsizer.AddSpacer(15) zoomsizer.Add(self.textzoom, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) zoomsizer.Add(self.zoombutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3) thumbsizesizer.Add(self.textthumbwidth, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 3)