예제 #1
0
 def GetWildcard(self):
     if self._extensions:
         if len(self._extensions) < 5:
             label = '%s (%s)' % (self._label, ','.join(self._extensions))
         else:
             label = self._label
         menu = [wildcard_list(self._to_local(label), self._extensions)]
     else:
         menu = []
     menu.append(self._all_files + '|*')
     return '|'.join(menu)
예제 #2
0
 def GetWildcard(self):
     if self._extensions:
         if len(self._extensions) < 5:
             label = '%s (%s)' % (self._label,
                 ','.join(self._extensions))
         else:
             label = self._label
         menu = [wildcard_list(self._to_local(label), self._extensions)]
     else:
         menu = []
     menu.append(self._all_files + '|*')
     return '|'.join(menu)
예제 #3
0
class Grid(droplet.Mixin, gridlib.Grid):
    border = 4
    Table = Table
    wildcard = '|'.join([wildcard.wildcard_list(_('Images'),
        formField.IMAGE_READ_EXTENSIONS), _('All files'), '*'])
    corner_logo = _corner_logo = None

    def __init__(self, parent, thumb_size=THUMB_SIZE):
        super(Grid, self).__init__(parent)
        #table
        self.table = self.Table(thumb_size)
        self.image_table = self.table.table
        self.SetTable(self.table, True)
        self.SetRowLabelSize(260)
        self._rows_number = self.GetNumberRows()
        self._cols_number = self.GetNumberCols()
        self._cols_sized = []
        #bitmap
        self.PENCIL_BITMAP = getPencilBitmap()
        self.PENCIL_BITMAP_SIZE = self.PENCIL_BITMAP.GetSize()
        self.GRAY_BRUSH = wx.Brush("WHEAT", wx.TRANSPARENT)
        self.GRAY_PEN = wx.Pen(GRAY)
        #editor
        self.SetDefaultEditor(gridlib.GridCellTextEditor())
        #drop
        self.SetAsFileDropTarget(self, self.OnDrop)
        self.SetAsFileDropTarget(self.GetEmpty(), self.OnDrop)
        self.SetAsFileDropTarget(self.GetTopLevelParent(), self.OnDrop)
        #events
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(gridlib.EVT_GRID_CELL_CHANGE, self.OnGridCellChange)
        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK,
            self.OnGridCellLeftClick)
        self.Bind(gridlib.EVT_GRID_CELL_RIGHT_CLICK,
            self.OnGridCellRightClicked)
        self.Bind(gridlib.EVT_GRID_CMD_LABEL_RIGHT_CLICK,
            self.OnGridLabelRightClicked)
        self.Bind(gridlib.EVT_GRID_LABEL_LEFT_DCLICK,
            self.OnGridLabelLeftDclicked)
        self.GetGridRowLabelWindow().Bind(wx.EVT_PAINT,
            self.OnRowLabelPaint)
        self.GetGridColLabelWindow().Bind(wx.EVT_PAINT,
            self.OnColLabelPaint)
        #FIXME: logo might get corrupted
        #self.GetGridCornerLabelWindow().Bind(wx.EVT_PAINT,
        #    self.OnCornerLabelPaint)
        self.Bind(gridlib.EVT_GRID_EDITOR_HIDDEN,
            self.OnGridEditorHidden)

    def OnGridEditorHidden(self, evt):
        wx.CallAfter(self.ShowLog)

    def ShowLog(self):
        log = self.table.log
        self.table.log = ''
        if log:
            self.ShowError(log)

    #---refresh
    def UpdateIfNeeded(self):
        needs_update = False
        for image in self.image_table.images:
            needs_update = needs_update or image.update_if_modified()
        if needs_update:
            self.RefreshAll(update_column=True, force_thumbs=True)

    def UpdateRowsColsNumbers(self):
        """Only consider adding or removing rows."""
        for current, new, delmsg, addmsg in [
            (self._rows_number, self.table.GetNumberRows(),
                gridlib.GRIDTABLE_NOTIFY_ROWS_DELETED,
                gridlib.GRIDTABLE_NOTIFY_ROWS_APPENDED),
            (self._cols_number, self.table.GetNumberCols(),
                gridlib.GRIDTABLE_NOTIFY_COLS_DELETED,
                gridlib.GRIDTABLE_NOTIFY_COLS_APPENDED),
        ]:
            if new < current:
                msg = gridlib.GridTableMessage(self.table, delmsg, new,
                    current - new)
                self.ProcessTableMessage(msg)
            elif new > current:
                msg = gridlib.GridTableMessage(self.table, addmsg,
                    new - current)
                self.ProcessTableMessage(msg)
        self._rows_number = self.GetNumberRows()
        self._cols_number = self.GetNumberCols()

    def UpdateValues(self):
        """Update all displayed values"""
        # This sends an event to the grid table to update all of the values
        msg = gridlib.GridTableMessage(self.table,
            gridlib.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
        self.ProcessTableMessage(msg)

    def UpdateThumbs(self, force_thumbs=False):
        if self.image_table.images:
            heights = []
            for image in self.image_table.images:
                if force_thumbs or not hasattr(image, 'thumb_wx'):
                    image.thumb_wx = wxPil.pil_wxBitmap(image.thumb)
                heights.append(image.thumb.size[1])
            self.SetColLabelSize(max(heights) + 2 * self.border)

    def RefreshAll(self, update_column=False, force_thumbs=False):
        self.BeginBatch()
        self.UpdateThumbs(force_thumbs)
        self.GetGridColLabelWindow().Refresh()
        self.UpdateRowsColsNumbers()
        self.UpdateValues()
        self.EndBatch()
        self.AdjustScrollbars()
        if update_column:
            for col in range(self.GetNumberCols()):
                if col not in self._cols_sized:
                    self.SetColSize(col, COL_WIDTH)
                    self._cols_sized.append(col)
        self.ForceRefresh()

    #---events
    def OnGridCellLeftClick(self, evt):
        col = evt.GetCol()
        self.SetTitleFilename(self.image_table.images[col].filename)
        evt.Skip()

    def SetTitleFilename(self, filename):
        wx.GetTopLevelParent(self).SetTitleFilename(filename)

    def OnColLabelPaint(self, evt):
        window = self.GetGridColLabelWindow()
        rect = window.GetClientRect()
        dc = wx.PaintDC(window)
        dc.Clear()
        pen = dc.GetPen()
        dc.SetPen(self.GRAY_PEN)
        dc.DrawLine(rect[0], rect[1] + rect[3] - 1,
            rect[0] + rect[2], rect[1] + rect[3] - 1)

        def label_rect(position, col):
            col_width = self.GetColSize(col)
            col_height = self.GetColLabelSize()
            return (position, 0, col_width, col_height)

        def get_bitmap(index):
            return self.image_table.images[index].thumb_wx

        self._LabelPaint(dc, co=0, amount=self.GetNumberCols(),
            label_rect=label_rect, get_size=self.GetColSize,
            get_label=None,  # self.GetColLabelValue,
            get_bitmap=get_bitmap, border=False, center_bitmap=True,
            pen=pen)

    def OnCornerLabelPaint(self, evt):
        if not self.corner_logo:
            return evt.Skip()
        if not self._corner_logo:
            self._corner_logo = graphics.bitmap(self.corner_logo)
        window = self.GetGridCornerLabelWindow()
        rect = window.GetClientRect()
        dc = wx.PaintDC(window)
        pen = dc.GetPen()
        dc.SetPen(self.GRAY_PEN)
        dc.DrawLine(rect[0], rect[1] + rect[3] - 1,
            rect[0] + rect[2], rect[1] + rect[3] - 1)

        def label_rect(position, col):
            return rect

        def get_bitmap(index):
            return self._corner_logo

        self._LabelPaint(dc, co=0, amount=1,
            label_rect=label_rect, get_size=self.GetColSize,
            get_label=None,  # self.GetColLabelValue,
            get_bitmap=get_bitmap, border=False, center_bitmap=True,
            pen=pen)

    def CopyCellValue(self, row, col):
        if self.table.GetNumberCols():
            clipboard.copy_text(unicode(self.table.GetValue(row, col)))

    def OnDrop(self, filenames, x, y):
        self.OpenImages(filenames)

    def OnGridCellChange(self, event):
        wx.CallAfter(self.RefreshAll)

    def OnGridCellRightClicked(self, event):
        """(row, evt) -> display a popup menu when a row label is
        right clicked"""
        # Did we click on a row or a column?
        event.Skip()
        row, col = event.GetRow(), event.GetCol()
        #bind menu events

        def on_copy(event):
            self.CopyCellValue(row, col)

        def on_add(event):
            self.AddColumnRow(col)

        def on_delete_cell(event):
            self.DeleteCell(row, col)

        #build menu control
        menu = wx.Menu()
        self._AppendMenuItem(menu, _('&Copy Value'), on_copy,
            id=wx.ID_COPY)
        if pyexiv2:
            self._AppendMenuItem(menu, _('&Add Tag'), on_add, 'Ctrl+N',
                id=wx.ID_ADD)
        if self.image_table.is_cell_deletable(row, col):
            self._AppendMenuItem(menu, _("&Delete Tag"), on_delete_cell,
                'Del', id=wx.ID_DELETE)
        #show menu
        self.PopupMenu(menu)
        menu.Destroy()

    def OnGridLabelRightClicked(self, event):
        event.Skip()
        row, col = event.GetRow(), event.GetCol()
        if row == -1:
            self.OnGridColLabelRightClicked(col)
        else:
            self.OnGridRowLabelRightClicked(row)

    def OnGridLabelLeftDclicked(self, event):
        event.Skip()
        row, col = event.GetRow(), event.GetCol()
        if row == -1:
            system.start(self.image_table.get_image_filename(col))

    def OnGridColLabelRightClicked(self, col):
        menu = wx.Menu()
        #open
        self._AppendMenuItem(menu, _('&Open...'), self.OnOpen,
            id=wx.ID_OPEN)
        #open url
        self._AppendMenuItem(menu, _('Open &Url...'), self.OnOpenUrl,
            'Shift+Ctrl+O')
        #remove image

        def on_remove(event):
            self.DeleteCols(col)

        self._AppendMenuItem(menu, _('&Remove Image'), on_remove)
        #show menu
        self.PopupMenu(menu)
        menu.Destroy()

    def _AppendMenuItem(self, menu, label, method, shortcut='', id=None):
        if id is None:
            id = wx.NewId()
        menu.Append(id, '%s\t%s' % (label, shortcut))
        self.Bind(wx.EVT_MENU, method, id=id)

    def OnGridRowLabelRightClicked(self, row):
        #build menu control
        menu = wx.Menu()
        self.CreateRowLabelMenu(menu, row)
        #show menu
        self.PopupMenu(menu)
        menu.Destroy()

    def CreateRowLabelMenu(self, menu, row):
        #bind menu events
        def on_add(event):
            self.AddRow()

        def on_copy(event):
            self.CopyRowLabel(row)

        def on_delete_row(event):
            self.DeleteRows(row)

        def on_set_row_label(event):
            self.RenameRowLabelValue(row)

        def on_set_row_values(event):
            self.ChangeRowValues(row)

        self._AppendMenuItem(menu, _('&Copy Tag'), on_copy,
            'Shift+Ctrl+C')
        if pyexiv2:
            self._AppendMenuItem(menu,
                _('&Add Tag to All Images...'),
                on_add, 'Shift+Ctrl+N')
        if self.image_table.is_row_editable(row) and pyexiv2:
            self._AppendMenuItem(menu,
                _("&Delete Tag from All Images..."),
                on_delete_row, 'Shift+Del')
            self._AppendMenuItem(menu,
                _("&Rename Tag for All Images..."),
                on_set_row_label, 'Shift+Ctrl+R')
            self._AppendMenuItem(menu,
                _("&Modify Value for All Images..."),
                on_set_row_values, 'Shift+Ctrl+M')

    def CopyRowLabel(self, row):
        clipboard.copy_text('<%s>' % self.table.GetRowLabelValue(row))

    def AddRow(self):
        key, value = AddTagDialog(self, self.image_table.keys).GetModal()
        if key:
            row = self.table.GetNumberRows()
            col = self.GetGridCursorCol()
            log = self.image_table.add_key(key, value)
            if log:
                self.ShowError(log, _('Unable to add tag <%s>') % key)
            self.RefreshAll()
            wx.CallAfter(self.MakeCellVisible, row, col)

    def AddColumnRow(self, col):
        key, value = AddTagDialog(self, self.image_table.keys).GetModal()
        if key:
            row = self.table.GetNumberRows()
            image = self.image_table.images[col]
            log = self.image_table.add_image_key(image, key, value)
            if log:
                self.ShowError(log, _('Unable to add tag <%s>') % key)
            self.RefreshAll()
            wx.CallAfter(self.MakeCellVisible, row, col)

    def DeleteCell(self, row, col):
        key = self.GetRowLabelValue(row)
        image_name = self.GetColLabelValue(col)
        if self.Ask('%s\n<%s>' % (CONFIRM_DELETE_TAG % image_name, key)) \
                == wx.ID_YES:
            log = self.image_table.delete_cell(row, col)
            if log:
                self.ShowError(log, _('Unable to delete tag <%s>') % key)
            self.RefreshAll()

    def DeleteRows(self, pos=0, num=1):
        key = self.GetRowLabelValue(pos)
        if self.Ask('%s\n<%s>' % (CONFIRM_DELETE_TAG_ALL, key)) \
                == wx.ID_YES:
            log = self.table.DeleteRows(pos, num)
            if log:
                self.ShowError(log, _('Unable to delete tag <%s>') % key)
            self.RefreshAll()

    def DeleteCols(self, pos=0, num=1):
        log = self.table.DeleteCols(pos, num)
        if log:
            self.ShowError(log, _('Unable to remove image'))
        if not self.CheckEmpty():
            self.RefreshAll()

    def RenameRowLabelValue(self, row):
        key_old = self.GetRowLabelValue(row)
        key_new = self.AskText(_('Rename tag for all images to:'),
            title=TITLE, value=key_old)
        if key_new:
            log = self.table.SetRowLabelValue(row, key_new)
            if log:
                self.ShowError(log, _('Unable to rename tag <%s>')\
                    % key_old)
            self.RefreshAll()

    def ChangeRowValues(self, row):
        value = self.AskText(_('Change value for all images to:'),
            title=TITLE)
        if value:
            key = self.GetRowLabelValue(row)
            log = self.image_table.set_key_value(key, value)
            if log:
                self.ShowError(log, _('Unable to change tag <%s>')\
                    % key)
            self.RefreshAll()

    def OnKeyDown(self, event):
        key_code = event.GetKeyCode()
        #print key_code
        row, col = self.GetCellRowCol()
        shift = event.ShiftDown()
        ctrl = event.ControlDown()
        alt = event.AltDown()
        if self.ProcessKey(key_code, row, col, shift, ctrl, alt):
            event.Skip()

    def ProcessKey(self, key_code, row, col, shift, ctrl, alt):
        if key_code == 127 \
                and self.image_table.is_cell_deletable(row, col):
            if shift:
                self.DeleteRows(row)
            else:
                self.DeleteCell(row, col)
        elif ctrl:
            if key_code == 67:
                #Ctrl+C
                if shift:
                    self.CopyRowLabel(row)
                else:
                    self.CopyCellValue(row, col)
            elif key_code == 78:
                #Ctrl+N
                if shift:
                    self.AddRow()
                else:
                    self.AddColumnRow(col)
            elif key_code == 82 and shift:
                #Ctrl+R
                self.RenameRowLabelValue(row)
            elif key_code == 77 and shift:
                #Ctrl+M
                self.ChangeRowValues(row)
        else:
            return True

    def OnRowLabelPaint(self, evt):
        window = self.GetGridRowLabelWindow()
        rect = window.GetClientRect()
        dc = wx.PaintDC(window)
        pen = dc.GetPen()
        dc.SetPen(self.GRAY_PEN)
        dc.DrawLine(rect[0] + rect[2] - 1, rect[1],
            rect[0] + rect[2] - 1, rect[1] + rect[3])

        def label_rect(position, row):
            row_width = self.GetRowLabelSize()
            row_height = self.GetRowSize(row)
            return (0, position, row_width, row_height)

        def get_bitmap(index):
            if self.image_table.is_row_editable(index):
                return self.PENCIL_BITMAP

        self._LabelPaint(dc, co=1, amount=self.GetNumberRows(),
            label_rect=label_rect, get_size=self.GetRowSize,
            get_label=self.GetRowLabelValue,
            get_bitmap=get_bitmap, border=True, center_bitmap=False,
            pen=pen)

    #---paint
    def _LabelPaint(self, dc, co, amount, label_rect, get_size,
            get_label, get_bitmap, border, center_bitmap, pen):
        position = -self.GetViewStart()[co]\
            * self.GetScrollPixelsPerUnit()[co]
        dc.SetBrush(self.GRAY_BRUSH)
        for index in range(amount):
            size = get_size(index)
            rect = label_rect(position, index)
            position += size
            if position < 0:
                continue
            if border:
                dc.DrawLine(rect[0], rect[1] + rect[3] - 1,
                    rect[0] + rect[2], rect[1] + rect[3] - 1)
            bitmap = get_bitmap(index)
            if bitmap:
                bitmap_size = bitmap.GetSize()
                if center_bitmap:
                    #centered
                    offset_x = (rect[2] - bitmap_size[0]) / 2
                else:
                    #right aligned
                    offset_x = rect[2] - bitmap_size[0] - self.border
                offset_y = (rect[3] - bitmap_size[1]) / 2
                dc.DrawBitmap(bitmap,
                    rect[0] + offset_x,
                    rect[1] + offset_y,
                    True)
            else:
                offset_y = (rect[3] - self.PENCIL_BITMAP_SIZE[1]) / 2
            dc.SetPen(pen)
            if get_label:
                dc.DrawText(get_label(index), rect[0] + self.border,
                    rect[1] + offset_y)

    #---dialogs
    def Ask(self, message, title=''):
        return self.ShowMessage(message, title,
            style=wx.YES_NO | wx.ICON_QUESTION)

    def AskText(self, question, value='', title=''):
        dlg = wx.TextEntryDialog(self, question, title, value)
        if dlg.ShowModal() == wx.ID_OK:
            answer = dlg.GetValue()
        else:
            answer = None
        dlg.Destroy()
        return answer

    def ShowMessage(self, message, title='',
            style=wx.OK | wx.ICON_EXCLAMATION):
        dlg = wx.MessageDialog(self,
                message,
                title,
                style,
        )
        answer = dlg.ShowModal()
        dlg.Destroy()
        return answer

    def ShowError(self, message, title=TITLE):
        return self.ShowMessage(message, title, style=wx.OK | wx.ICON_ERROR)

    def OpenImage(self, filename):
        try:
            self.image_table.open_image(filename, encoding=WX_ENCODING)
        except IOError, message:
            self.show_error(_('Sorry, %s.') % str(message),
                title=_('Image Inspector'))
            return
        self.image_table.update()
        self.UpdateThumbs()
        self.SetTitleFilename(filename)
        self.RefreshAll(update_column=True)