def __init__(self, parent, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.HSCROLL | wx.VSCROLL, name='PhotoFilmStripList'): wx.ScrolledWindow.__init__(self, parent, id, pos, size, style, name) self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetBackgroundColour(wx.BLACK) clientSize = wx.Size(-1, self.STRIP_HEIGHT + wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y)) self.SetSizeHints(clientSize, clientSize) self.__frozen = False self.__pictures = [] self.__selIdxs = [] self.__hvrIdx = -1 self.__dragIdx = -1 self.__dropIdx = -1 self.__dragBmp = None self.__dragBmpIdx = -1 self.__dragX = 0 self.__dragOffX = 0 self.__UpdateVirtualSize() self.SetScrollRate(1, 0) self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.OnCaptureLost) ImageCache().RegisterWin(self) ImageCache().thumb = wx.ArtProvider.GetBitmap( wx.ART_NORMAL_FILE, size=wx.Size(120, 120)) self.Bind(EVT_THUMB_READY, self.__OnThumbReady)
def __UpdateVirtualSize(self): width = 0 for pic in self.__pictures: bmp = ImageCache().GetThumbBmp(pic) # if the picture cannot be loaded GetWidth may return -1 width += bmp.GetWidth() + self.GAP self.SetVirtualSize((width, self.STRIP_HEIGHT)) self.Refresh()
def OnPaint(self, event): pdc = wx.BufferedPaintDC(self) try: dc = wx.GCDC(pdc) except StandardError: dc = pdc dc.SetBackground(wx.BLACK_BRUSH) dc.Clear() vx = self.GetViewStart()[0] clientWidth = self.GetClientSize()[0] diaRect = wx.Rect(-vx, 0, 0, self.STRIP_HEIGHT) for idx, pic in enumerate(self.__pictures): bmp = ImageCache().GetThumbBmp(pic) # if the picture cannot be loaded GetWidth may return -1 bmpWidth = bmp.GetWidth() diaRect.SetWidth(bmpWidth + self.GAP) if idx == self.__dropIdx and self.__dragIdx > idx: diaRect.OffsetXY(self.__dragBmp.GetWidth(), 0) if diaRect.right + 1 >= 0 and idx != self.__dragIdx: if diaRect.left <= clientWidth: if self.IsExposedRect(diaRect): label = os.path.splitext( os.path.basename(pic.GetFilename()))[0] diaNo = idx + 1 if idx >= self.__dropIdx and idx < self.__dragIdx: diaNo += 1 if idx <= self.__dropIdx and idx > self.__dragIdx: diaNo -= 1 self.__DrawDia(dc, diaRect, diaRect.x + vx, bmp, str(diaNo), label, idx in self.__selIdxs, idx == self.__hvrIdx) else: break if idx != self.__dragIdx or self.__dragIdx == self.__dropIdx: diaRect.OffsetXY(diaRect.width, 0) if idx == self.__dropIdx and self.__dragIdx < idx: diaRect.OffsetXY(self.__dragBmp.GetWidth(), 0) if self.__dragIdx is not None: dc.DrawBitmap(self.__dragBmp, self.__dragX - self.__dragOffX - vx, 0, True)
def SetPicture(self, picture): self._picture = picture if self._picture is not None: self._wxImg = ImageCache().GetImage(picture) if self._curThread is not None: self._curThread.Abort() self._curThread = ScaleThread(picture, self.OnThreadDone) self._curThread.start() self.Notify()
def __CreateDiaBmp(self, picIdx, selected=False, highlighted=False, dropIdx=None): pic = self.__pictures[picIdx] thumbBmp = ImageCache().GetThumbBmp(pic) diaRect = self.GetDiaRect(picIdx) holeOffset = diaRect.x bmp = wx.Bitmap(diaRect.width, diaRect.height) diaNo = str(picIdx + 1) label = os.path.splitext(os.path.basename(pic.GetFilename()))[0] dc = wx.MemoryDC(bmp) try: dc = wx.GCDC(dc) except Exception: pass if dropIdx is not None: diaNo = str(dropIdx + 1) dropRect = self.GetDiaRect(dropIdx) if dropIdx > picIdx: holeOffset = dropRect.right + 1 - diaRect.width diaRect.SetX(0) self.__DrawDia(dc, diaRect, holeOffset, thumbBmp, diaNo, label, selected, highlighted) return bmp
def __LoadThumbnail(self, pic, picId): ImageCache().RegisterPicture(pic) return thumbNail = None if self.__fileRev >= 3: cur = self.__GetCursor() cur.execute("SELECT * FROM `thumbnail` WHERE picture_id=?", (picId,)) row = cur.fetchone() if row: thumbWidth = row["width"] thumbHeight = row["height"] thumbData = row["data"] thumbNail = PILBackend.ImageFromBuffer((thumbWidth, thumbHeight), thumbData) if thumbNail is None: thumbNail = PILBackend.GetThumbnail(pic, height=120) ImageCache().RegisterPicture(pic, thumbNail)
def OnImportPics(self, event): dlg = wx.FileDialog(self, _("Import images"), Settings().GetImagePath(), "", _("Image files") + " (*.*)|*.*", wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_PREVIEW) if dlg.ShowModal() == wx.ID_OK: pics = [] for path in dlg.GetPaths(): if not self._CheckImportedPic(path): continue pic = Picture(path) pics.append(pic) ImageCache().RegisterPicture(pic) selItms = self.lvPics.GetSelected() self.InsertPictures(pics, selItms[0] + 1 if selItms else None, autopath=True) Settings().SetImagePath(os.path.dirname(path)) selPics = self.lvPics.GetSelectedPictures() self.pnlEditPicture.SetPictures(selPics) dlg.Destroy()
class ImageProxy(Observable): def __init__(self): Observable.__init__(self) self._curThread = None self._picture = None self._wxImg = None self._wxBmp = None self._curSize = -1, -1 def Destroy(self): if self._curThread: self._curThread.Abort() self._curThread.join() def IsOk(self): return self._picture is not None def OnThreadDone(self, img): self._wxImg = img wx.CallAfter(self.Notify) def SetPicture(self, picture): self._picture = picture if self._picture is not None: self._wxImg = ImageCache().GetImage(picture) if self._curThread is not None: self._curThread.Abort() self._curThread = ScaleThread(picture, self.OnThreadDone) self._curThread.start() self.Notify() def GetWidth(self): return self._picture.GetWidth() def GetHeight(self): return self._picture.GetHeight() def GetSize(self): return self.GetWidth(), self.GetHeight() def Scale(self, width, height): if not (width > 0 and height > 0): return img = self._wxImg.Scale(width, height) self._wxBmp = img.ConvertToBitmap() self._curSize = width, height def GetCurrentSize(self): return self._curSize def GetBitmap(self): return self._wxBmp
def GetDiaRect(self, idx): sx = 0 for picIdx, pic in enumerate(self.__pictures): thumbWidth = ImageCache().GetThumbBmp(pic).GetWidth() if idx == picIdx: rect = wx.Rect(sx, 0, thumbWidth + self.GAP, self.STRIP_HEIGHT) return rect sx += thumbWidth + self.GAP
def HitTest(self, pos): pos = self.CalcUnscrolledPosition(pos) sx = 0 for idx, pic in enumerate(self.__pictures): thumbWidth = ImageCache().GetThumbBmp(pic).GetWidth() rect = wx.Rect(sx, 0, thumbWidth + self.GAP, self.STRIP_HEIGHT) if rect.Contains(pos): return idx sx += thumbWidth + self.GAP return -1
def OnImportPics(self, event): dlg = wx.FileDialog(self, _(u"Import images"), Settings().GetImagePath(), "", _(u"Imagefiles") + " (*.*)|*.*", wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_PREVIEW) if dlg.ShowModal() == wx.ID_OK: pics = [] for path in dlg.GetPaths(): if self.__project.GetTimelapse(): picPattern = PicturePattern.Create(path) if not picPattern.IsOk(): dlgErr = wx.MessageDialog( self, _(u"Filename '%s' does not match a number pattern " u"which is necessary for a time lapse slide " u"show!") % path, _(u"Error"), wx.OK | wx.ICON_ERROR) dlgErr.ShowModal() dlgErr.Destroy() continue pic = Picture(path) pics.append(pic) ImageCache().RegisterPicture(pic) selItms = self.lvPics.GetSelected() self.InsertPictures(pics, selItms[0] + 1 if selItms else None, autopath=True) Settings().SetImagePath(os.path.dirname(path)) selPics = self.lvPics.GetSelectedPictures() self.pnlEditPicture.SetPictures(selPics) dlg.Destroy()