def __init__(self, options, parent=None): """options is a list of strings. Each a comma-delimited field list. Eg. ['artist, title', 'album, genre'] """ QDialog.__init__(self, parent) connect = lambda c, signal, s: self.connect(c, SIGNAL(signal), s) self.listbox = ListBox() self.listbox.setSelectionMode(self.listbox.ExtendedSelection) buttons = ListButtons() self.listbox.addItems(options) hbox = QHBoxLayout() hbox.addWidget(self.listbox, 1) hbox.addLayout(buttons) okcancel = OKCancel() vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addLayout(okcancel) self.setLayout(vbox) connect(buttons, "add", self.addPattern) connect(buttons, "edit", self.editItem) buttons.duplicate.setVisible(False) self.connect(okcancel, SIGNAL('ok'), self.applySettings) self.connect(okcancel, SIGNAL('cancel'), self.applySettings) self.listbox.connectToListButtons(buttons) self.listbox.editButton = buttons.edit connect(self.listbox, 'itemDoubleClicked(QListWidgetItem *)', self._doubleClicked)
def __init__(self, parent=None, status=None): QWidget.__init__(self, parent) if status is None: self._status = {} genres = load_genres() else: self._status = status genres = status['genres'] self.listbox = ListBox() self._itemflags = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled [self.listbox.addItem(self._createItem(z)) for z in genres] buttons = ListButtons() self.listbox.connectToListButtons(buttons) self.listbox.setAutoScroll(False) self.connect(buttons, SIGNAL('add'), self.add) self.connect(buttons, SIGNAL('edit'), self.edit) hbox = QHBoxLayout() hbox.addWidget(self.listbox, 1) hbox.addLayout(buttons, 0) self.setLayout(hbox)
def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value')]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown (True) header.setStretchLastSection (True) header.setSortIndicator (0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons = True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip(translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files)
class ExTags(QDialog): """A dialog that shows you the tags in a file In addition, any attached cover art is shown.""" def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value')]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown (True) header.setStretchLastSection (True) header.setSortIndicator (0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons = True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip(translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files) def addField(self): win = EditField(parent=self, field_list=self.get_fieldlist) win.setModal(True) win.show() self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy) def _checkListBox(self): if self.table.rowCount() <= 0: self.table.setEnabled(False) self.listbuttons.edit.setEnabled(False) self.listbuttons.remove.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) self._reset.setEnabled(False) else: self.table.setEnabled(True) self._reset.setEnabled(True) if len(self.table.selectedIndexes()) / 2 > 1: self.listbuttons.edit.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) else: self.listbuttons.edit.setEnabled(True) self.listbuttons.remove.setEnabled(True) self.listbuttons.duplicate.setEnabled(True) self.table.resizeColumnToContents(0) def closeEvent(self,event): self.piclabel.close() QDialog.closeEvent(self,event) def closeMe(self): self.canceled = True self.close() def _deletePressed(self, item): if self.table.deletePressed: self.table.deletePressed = False self.removeField() def duplicate(self): self.editField(True) def editField(self, duplicate=False): """Opens a dialog to edit the currently selected Field. If duplicate is True the Edit Field dialog will be populated with the currently selected field's values. The new field'll then be added to the field list.""" row = self.table.currentRow() if row != -1: prevtag = self.get_field(row) if duplicate is True: win = EditField(prevtag, self, self.get_fieldlist, edit=False) else: win = EditField(prevtag, self, self.get_fieldlist) win.setModal(True) win.show() #Have to check for truth, because this method is #called by the doubleclicked signal. if duplicate is True: buddy = partial(self.editFieldBuddy, duplicate=True) else: buddy = self.editFieldBuddy self.connect(win, SIGNAL("donewithmyshit"), buddy) def editFieldBuddy(self, tag, value, prevtag = None, duplicate=False): rowcount = self.table.rowCount() if prevtag is not None: if duplicate: row = rowcount self._settag(rowcount, tag, value, ADD, self.previewMode, True) else: if tag == prevtag[0]: row = self.table.currentRow() self._settag(row, tag, value, EDIT, self.previewMode, True) if row + 1 < rowcount: self.table.selectRow(row + 1) else: cur_item = self.table.item(self.table.currentRow(),0) self.resetFields([cur_item]) self.table.setCurrentItem(cur_item, QItemSelectionModel.ClearAndSelect) self.table.selectRow(self.table.row(cur_item)) self.removeField() valitem = self._settag(rowcount, tag, value, ADD, self.previewMode, True) cur_item.linked = [valitem] else: self._settag(rowcount, tag, value, ADD, self.previewMode, True) self._checkListBox() self.filechanged = True self.table.clearSelection() def get_field(self, row, status = None): getitem = self.table.item item = getitem(row, 0) tag = unicode(item.text()) try: value = unicode(getitem(row, 1).text()) except AttributeError: value = unicode(self.table.cellWidget(row, 1).currentText()) if status: return (tag, value, item.status) else: return (tag, value) def _imageChanged(self): self.filechanged = True def loadSettings(self): cparser = PuddleConfig() self.get_fieldlist = gettaglist() get = lambda k,v : cparser.get('extendedtags', k, v, True) add = QColor.fromRgb(*get('add', [255, 255, 0])) edit = QColor.fromRgb(*get('edit', [0, 255,0])) remove = QColor.fromRgb(*get('remove', [255, 0, 0])) self._colors = {ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove)} item = self.table.item for row in xrange(self.table.rowCount()): field_item = self.get_item(row, 0) field_item.statusColors = self._colors field_item.status = field_item.status val_item = self.get_item(row, 1) val_item.statusColors = self._colors val_item.status = val_item.status def listtotag(self): get_field = self.get_field tags = {} lowered = {} listitems = [get_field(row, True) for row in xrange(self.table.rowCount())] for field, val, status in listitems: if status != REMOVE: if val == KEEP: continue l_field = field.lower() if l_field in lowered: tags[lowered[l_field]].append(val) else: lowered[l_field] = field tags[field] = [z.strip() for z in val.split('\\') if z.strip()] else: if field.lower() not in lowered: tags[field] = [] lowered[field.lower()] = field return tags def loadFiles(self, audios): if self.filechanged: self.save() self.filechanged = False self.table.clearContents() self.table.setRowCount(0) self.piclabel.lastfilename = audios[0].filepath self.piclabel.setEnabled(False) self.piclabel.setImages(None) if len(audios) == 1: audio = audios[0] self.setWindowTitle(audios[0].filepath) self._loadsingle(audio) else: self.setWindowTitle( translate('Extended Tags', 'Different files.')) from puddlestuff.tagmodel import status k = status['table'].model().taginfo[0] common, numvalues, imagetags = commontags(audios) images = common['__image'] del(common['__image']) previews = set(audios[0].preview) italics = set(audios[0].equal_fields()) self.piclabel.currentFile = audios[0] self.piclabel.filePattern = self.status['cover_pattern'] for audio in audios[1:]: previews = previews.intersection(audio.preview) italics = italics.intersection(audio.equal_fields()) row = 0 for field, values in common.iteritems(): if field in italics: preview = UNCHANGED #field in italics => field in previews. elif field in previews: preview = BOLD else: preview = UNCHANGED if numvalues[field] != len(audios): self._settag(row, field, values, multi=True) row += 1 else: if isinstance(values, basestring): self._settag(row, field, values, None, preview) row += 1 else: for v in values: self._settag(row, field, v, None, preview) row += 1 self.piclabel.setImageTags(imagetags) if images: self.piclabel.setEnabled(True) self.piclabel.setImages(images) else: self.piclabel.setImages(None) self.piclabel.setEnabled(True) if images == 0: self.piclabel.context = 'Cover Varies' self.piclabel.removepic.setEnabled(True) self._checkListBox() def _loadsingle(self, tags): items = [] d = tags.usertags.copy() italics = tags.equal_fields() self.piclabel.currentFile = tags self.piclabel.filePattern = self.status['cover_pattern'] for key, val in sorted(d.items()): if key in italics: preview = UNCHANGED elif key in tags.preview: preview = BOLD else: preview = UNCHANGED if isinstance(val, basestring): items.append([key, val, None, preview]) else: [items.append([key, z, None, preview]) for z in val] [self._settag(i, *item) for i, item in enumerate(items)] self.piclabel.lastfilename = tags.filepath if not tags.library: self.piclabel.setImageTags(tags.IMAGETAGS) if tags.IMAGETAGS: if '__image' in tags.preview: images = tags.preview['__image'] else: images = tags.images self.piclabel.setEnabled(True) if images: self.piclabel.setImages(deepcopy(images)) else: self.piclabel.setImages(None) self._checkListBox() self.setWindowTitle(tags[PATH]) def okClicked(self): self.save() self.close() def _prevnext(self, row): if self.filechanged: self.save() self.loadFiles([self._files[row]]) self.emit(SIGNAL('rowChanged'), row) def get_item(self, row, column=None): if column is None: #Assume QModelIndex passed column = row.column() row = row.row() item = self.table.item(row, column) if item is None: item = self.table.cellWidget(row, column) return item def removeField(self): tb = self.table tb.setSortingEnabled(False) to_remove = {} rows = [] for index in self.table.selectedIndexes(): row = index.row() item = self.get_item(index) if item.status == ADD: to_remove[row] = item rows.append(row) item.status = REMOVE item.status = REMOVE [tb.removeRow(tb.row(z)) for z in to_remove.values()] tb.setSortingEnabled(True) self.filechanged = True self._checkListBox() if rows: row = max(rows) self.table.clearSelection() if row + 1 < self.table.rowCount(): self.table.selectRow(row + 1) def resetFields(self, items=None): box = self.table to_remove = {} #Stores row: item values so that only one item #gets removed per row. max_row = -1 for index in box.selectedIndexes(): row = index.row() item = self.table.item(row, index.column()) if item is None: item = self.table.cellWidget(row, index.column()) for i in item.linked: try: to_remove[box.row(i)] = i except RuntimeError: pass item.reset() if row > max_row: max_row = row if item.status == REMOVE: to_remove[row] = item self.table.clearSelection() if max_row != -1 and max_row + 1 < self.table.rowCount(): self.table.selectRow(max_row + 1) for item in to_remove.values(): self.table.removeRow(self.table.row(item)) self._checkListBox() def removePic(self): if self.piclabel.context == 'Cover Varies': self.piclabel.context = 'No Images' self.piclabel.removepic.setEnabled(False) if not isinstance(self.piclabel.removepic, QAction): self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.discconnect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) self.piclabel.setImages(None) def save(self): if not self.filechanged: table = self.table for row in range(table.rowCount()): combo = table.cellWidget(row, 1) if combo is not None and combo.currentIndex() != 0: self.filechanged = True break if not self.filechanged: return tags = self.listtotag() if self.piclabel.context != 'Cover Varies': if not self.piclabel.images: tags['__image'] = [] else: tags["__image"] = self.piclabel.images newtags = [z for z in tags if z not in self.get_fieldlist] if newtags and newtags != ['__image']: settaglist(newtags + self.get_fieldlist) self.emit(SIGNAL('extendedtags'), tags) def _settag(self, row, field, value, status=None, preview=False, check=False, multi=False): tb = self.table tb.setSortingEnabled(False) if row >= tb.rowCount(): tb.insertRow(row) field_item = StatusWidgetItem(field, status, self._colors, preview) tb.setItem(row, 0, field_item) if not multi and (len(value) == 1 or isinstance(value, basestring)): valitem = StatusWidgetItem(to_string(value), status, self._colors, preview) tb.setItem(row, 1, valitem) else: valitem = StatusWidgetCombo(value, status, self._colors, preview) tb.setCellWidget(row, 1, valitem) else: field_item = tb.item(row, 0) field_item.setText(field) field_item.status = status val_item = self.get_item(row, 1) val_item.setText(value) val_item.status = status if check: lowered_tag = field.lower() for row in xrange(tb.rowCount()): item = tb.item(row, 0) text = unicode(item.text()) if text != field and text.lower() == lowered_tag: item.setText(field) if item.status not in [ADD, REMOVE]: item.status = EDIT try: tb.item(row, 1).status = EDIT except AttributeError: tb.cellWidget(row, 1).status = EDIT tb.setSortingEnabled(True) return field_item
def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value') ]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown(True) header.setStretchLastSection(True) header.setSortIndicator(0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons=True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip( translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files)
class ExTags(QDialog): """A dialog that shows you the tags in a file In addition, any attached cover art is shown.""" def __init__(self, parent=None, row=None, files=None, preview_mode=False, artwork=True, status=None): if status is None: status = {'cover_pattern': 'folder'} self.status = status QDialog.__init__(self, parent) winsettings('extendedtags', self) self.get_fieldlist = [] self.previewMode = preview_mode add = QColor.fromRgb(255, 255, 0) edit = QColor.fromRgb(0, 255, 0) remove = QColor.fromRgb(255, 0, 0) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } self.table = QTableWidget(0, 2, self) self.table.setVerticalHeader(VerticalHeader()) self.table.verticalHeader().setVisible(False) self.table.setSortingEnabled(True) self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.setHorizontalHeaderLabels([ translate('Extended Tags', 'Field'), translate('Extended Tags', 'Value') ]) header = self.table.horizontalHeader() header.setVisible(True) header.setSortIndicatorShown(True) header.setStretchLastSection(True) header.setSortIndicator(0, Qt.AscendingOrder) self.piclabel = PicWidget(buttons=True) self.connect(self.piclabel, SIGNAL('imageChanged'), self._imageChanged) if not isinstance(self.piclabel.removepic, QAction): self.connect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.connect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) if row >= 0 and files: buttons = MoveButtons(files, row) self.connect(buttons, SIGNAL('indexChanged'), self._prevnext) buttons.setVisible(True) else: buttons = MoveButtons([], row) buttons.setVisible(False) self._files = files self.okcancel = OKCancel() self.okcancel.insertWidget(0, buttons) self._reset = QToolButton() self._reset.setToolTip( translate('Extended Tags', 'Resets the selected fields to their original value.')) self._reset.setIcon(get_icon('edit-undo', ':/undo.png')) self.connect(self._reset, SIGNAL('clicked()'), self.resetFields) self.listbuttons = ListButtons() self.listbuttons.layout().addWidget(self._reset) self.listbuttons.moveup.hide() self.listbuttons.movedown.hide() listframe = QFrame() listframe.setFrameStyle(QFrame.Box) hbox = QHBoxLayout() hbox.addWidget(self.table, 1) hbox.addLayout(self.listbuttons, 0) listframe.setLayout(hbox) layout = QVBoxLayout() if artwork: imageframe = QFrame() imageframe.setFrameStyle(QFrame.Box) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(self.piclabel) vbox.addStretch() vbox.addStrut(0) imageframe.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(listframe, 1) hbox.addSpacing(4) hbox.addWidget(imageframe) hbox.addStrut(1) layout.addLayout(hbox) else: layout.addWidget(listframe) layout.addLayout(self.okcancel) self.setLayout(layout) self.connect(self.okcancel, SIGNAL("cancel"), self.closeMe) self.connect(self.table, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"), self.editField) self.connect(self.table, SIGNAL("itemSelectionChanged()"), self._checkListBox) self.connect(self.okcancel, SIGNAL("ok"), self.okClicked) clicked = SIGNAL('clicked()') self.connect(self.listbuttons, SIGNAL('edit'), self.editField) self.connect(self.listbuttons.add, clicked, self.addField) self.connect(self.listbuttons.remove, clicked, self.removeField) self.connect(self.listbuttons, SIGNAL('duplicate'), self.duplicate) self.setMinimumSize(450, 350) self.canceled = False self.filechanged = False if row >= 0 and files: self._prevnext(row) else: self.loadFiles(files) def addField(self): win = EditField(parent=self, field_list=self.get_fieldlist) win.setModal(True) win.show() self.connect(win, SIGNAL("donewithmyshit"), self.editFieldBuddy) def _checkListBox(self): if self.table.rowCount() <= 0: self.table.setEnabled(False) self.listbuttons.edit.setEnabled(False) self.listbuttons.remove.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) self._reset.setEnabled(False) else: self.table.setEnabled(True) self._reset.setEnabled(True) if len(self.table.selectedIndexes()) / 2 > 1: self.listbuttons.edit.setEnabled(False) self.listbuttons.duplicate.setEnabled(False) else: self.listbuttons.edit.setEnabled(True) self.listbuttons.remove.setEnabled(True) self.listbuttons.duplicate.setEnabled(True) self.table.resizeColumnToContents(0) def closeEvent(self, event): self.piclabel.close() QDialog.closeEvent(self, event) def closeMe(self): self.canceled = True self.close() def _deletePressed(self, item): if self.table.deletePressed: self.table.deletePressed = False self.removeField() def duplicate(self): self.editField(True) def editField(self, duplicate=False): """Opens a dialog to edit the currently selected Field. If duplicate is True the Edit Field dialog will be populated with the currently selected field's values. The new field'll then be added to the field list.""" row = self.table.currentRow() if row != -1: prevtag = self.get_field(row) if duplicate is True: win = EditField(prevtag, self, self.get_fieldlist, edit=False) else: win = EditField(prevtag, self, self.get_fieldlist) win.setModal(True) win.show() #Have to check for truth, because this method is #called by the doubleclicked signal. if duplicate is True: buddy = partial(self.editFieldBuddy, duplicate=True) else: buddy = self.editFieldBuddy self.connect(win, SIGNAL("donewithmyshit"), buddy) def editFieldBuddy(self, tag, value, prevtag=None, duplicate=False): rowcount = self.table.rowCount() if prevtag is not None: if duplicate: row = rowcount self._settag(rowcount, tag, value, ADD, self.previewMode, True) else: if tag == prevtag[0]: row = self.table.currentRow() self._settag(row, tag, value, EDIT, self.previewMode, True) if row + 1 < rowcount: self.table.selectRow(row + 1) else: cur_item = self.table.item(self.table.currentRow(), 0) self.resetFields([cur_item]) self.table.setCurrentItem( cur_item, QItemSelectionModel.ClearAndSelect) self.table.selectRow(self.table.row(cur_item)) self.removeField() valitem = self._settag(rowcount, tag, value, ADD, self.previewMode, True) cur_item.linked = [valitem] else: self._settag(rowcount, tag, value, ADD, self.previewMode, True) self._checkListBox() self.filechanged = True self.table.clearSelection() def get_field(self, row, status=None): getitem = self.table.item item = getitem(row, 0) tag = unicode(item.text()) try: value = unicode(getitem(row, 1).text()) except AttributeError: value = unicode(self.table.cellWidget(row, 1).currentText()) if status: return (tag, value, item.status) else: return (tag, value) def _imageChanged(self): self.filechanged = True def loadSettings(self): cparser = PuddleConfig() self.get_fieldlist = gettaglist() get = lambda k, v: cparser.get('extendedtags', k, v, True) add = QColor.fromRgb(*get('add', [255, 255, 0])) edit = QColor.fromRgb(*get('edit', [0, 255, 0])) remove = QColor.fromRgb(*get('remove', [255, 0, 0])) self._colors = { ADD: QBrush(add), EDIT: QBrush(edit), REMOVE: QBrush(remove) } item = self.table.item for row in xrange(self.table.rowCount()): field_item = self.get_item(row, 0) field_item.statusColors = self._colors field_item.status = field_item.status val_item = self.get_item(row, 1) val_item.statusColors = self._colors val_item.status = val_item.status def listtotag(self): get_field = self.get_field tags = {} lowered = {} listitems = [ get_field(row, True) for row in xrange(self.table.rowCount()) ] for field, val, status in listitems: if status != REMOVE: if val == KEEP: continue l_field = field.lower() if l_field in lowered: tags[lowered[l_field]].append(val) else: lowered[l_field] = field tags[field] = [ z.strip() for z in val.split('\\') if z.strip() ] else: if field.lower() not in lowered: tags[field] = [] lowered[field.lower()] = field return tags def loadFiles(self, audios): if self.filechanged: self.save() self.filechanged = False self.table.clearContents() self.table.setRowCount(0) self.piclabel.lastfilename = audios[0].filepath self.piclabel.setEnabled(False) self.piclabel.setImages(None) if len(audios) == 1: audio = audios[0] self.setWindowTitle(audios[0].filepath) self._loadsingle(audio) else: self.setWindowTitle(translate('Extended Tags', 'Different files.')) from puddlestuff.tagmodel import status k = status['table'].model().taginfo[0] common, numvalues, imagetags = commontags(audios) images = common['__image'] del (common['__image']) previews = set(audios[0].preview) italics = set(audios[0].equal_fields()) self.piclabel.currentFile = audios[0] self.piclabel.filePattern = self.status['cover_pattern'] for audio in audios[1:]: previews = previews.intersection(audio.preview) italics = italics.intersection(audio.equal_fields()) row = 0 for field, values in common.iteritems(): if field in italics: preview = UNCHANGED #field in italics => field in previews. elif field in previews: preview = BOLD else: preview = UNCHANGED if numvalues[field] != len(audios): self._settag(row, field, values, multi=True) row += 1 else: if isinstance(values, basestring): self._settag(row, field, values, None, preview) row += 1 else: for v in values: self._settag(row, field, v, None, preview) row += 1 self.piclabel.setImageTags(imagetags) if images: self.piclabel.setEnabled(True) self.piclabel.setImages(images) else: self.piclabel.setImages(None) self.piclabel.setEnabled(True) if images == 0: self.piclabel.context = 'Cover Varies' self.piclabel.removepic.setEnabled(True) self._checkListBox() def _loadsingle(self, tags): items = [] d = tags.usertags.copy() italics = tags.equal_fields() self.piclabel.currentFile = tags self.piclabel.filePattern = self.status['cover_pattern'] for key, val in sorted(d.items()): if key in italics: preview = UNCHANGED elif key in tags.preview: preview = BOLD else: preview = UNCHANGED if isinstance(val, basestring): items.append([key, val, None, preview]) else: [items.append([key, z, None, preview]) for z in val] [self._settag(i, *item) for i, item in enumerate(items)] self.piclabel.lastfilename = tags.filepath if not tags.library: self.piclabel.setImageTags(tags.IMAGETAGS) if tags.IMAGETAGS: if '__image' in tags.preview: images = tags.preview['__image'] else: images = tags.images self.piclabel.setEnabled(True) if images: self.piclabel.setImages(deepcopy(images)) else: self.piclabel.setImages(None) self._checkListBox() self.setWindowTitle(tags[PATH]) def okClicked(self): self.save() self.close() def _prevnext(self, row): if self.filechanged: self.save() self.loadFiles([self._files[row]]) self.emit(SIGNAL('rowChanged'), row) def get_item(self, row, column=None): if column is None: #Assume QModelIndex passed column = row.column() row = row.row() item = self.table.item(row, column) if item is None: item = self.table.cellWidget(row, column) return item def removeField(self): tb = self.table tb.setSortingEnabled(False) to_remove = {} rows = [] for index in self.table.selectedIndexes(): row = index.row() item = self.get_item(index) if item.status == ADD: to_remove[row] = item rows.append(row) item.status = REMOVE item.status = REMOVE [tb.removeRow(tb.row(z)) for z in to_remove.values()] tb.setSortingEnabled(True) self.filechanged = True self._checkListBox() if rows: row = max(rows) self.table.clearSelection() if row + 1 < self.table.rowCount(): self.table.selectRow(row + 1) def resetFields(self, items=None): box = self.table to_remove = {} #Stores row: item values so that only one item #gets removed per row. max_row = -1 for index in box.selectedIndexes(): row = index.row() item = self.table.item(row, index.column()) if item is None: item = self.table.cellWidget(row, index.column()) for i in item.linked: try: to_remove[box.row(i)] = i except RuntimeError: pass item.reset() if row > max_row: max_row = row if item.status == REMOVE: to_remove[row] = item self.table.clearSelection() if max_row != -1 and max_row + 1 < self.table.rowCount(): self.table.selectRow(max_row + 1) for item in to_remove.values(): self.table.removeRow(self.table.row(item)) self._checkListBox() def removePic(self): if self.piclabel.context == 'Cover Varies': self.piclabel.context = 'No Images' self.piclabel.removepic.setEnabled(False) if not isinstance(self.piclabel.removepic, QAction): self.disconnect(self.piclabel.removepic, SIGNAL('clicked()'), self.removePic) else: self.discconnect(self.piclabel.removepic, SIGNAL('triggered()'), self.removePic) self.piclabel.setImages(None) def save(self): if not self.filechanged: table = self.table for row in range(table.rowCount()): combo = table.cellWidget(row, 1) if combo is not None and combo.currentIndex() != 0: self.filechanged = True break if not self.filechanged: return tags = self.listtotag() if self.piclabel.context != 'Cover Varies': if not self.piclabel.images: tags['__image'] = [] else: tags["__image"] = self.piclabel.images newtags = [z for z in tags if z not in self.get_fieldlist] if newtags and newtags != ['__image']: settaglist(newtags + self.get_fieldlist) self.emit(SIGNAL('extendedtags'), tags) def _settag(self, row, field, value, status=None, preview=False, check=False, multi=False): tb = self.table tb.setSortingEnabled(False) if row >= tb.rowCount(): tb.insertRow(row) field_item = StatusWidgetItem(field, status, self._colors, preview) tb.setItem(row, 0, field_item) if not multi and (len(value) == 1 or isinstance(value, basestring)): valitem = StatusWidgetItem(to_string(value), status, self._colors, preview) tb.setItem(row, 1, valitem) else: valitem = StatusWidgetCombo(value, status, self._colors, preview) tb.setCellWidget(row, 1, valitem) else: field_item = tb.item(row, 0) field_item.setText(field) field_item.status = status val_item = self.get_item(row, 1) val_item.setText(value) val_item.status = status if check: lowered_tag = field.lower() for row in xrange(tb.rowCount()): item = tb.item(row, 0) text = unicode(item.text()) if text != field and text.lower() == lowered_tag: item.setText(field) if item.status not in [ADD, REMOVE]: item.status = EDIT try: tb.item(row, 1).status = EDIT except AttributeError: tb.cellWidget(row, 1).status = EDIT tb.setSortingEnabled(True) return field_item