f642b46742
git-svn-id: http://comictagger.googlecode.com/svn/trunk@2 6c5673fe-1810-88d6-992b-cd32ca31540c
481 lines
15 KiB
Python
481 lines
15 KiB
Python
|
|
from PyQt4 import QtCore, QtGui, uic
|
|
|
|
from volumeselectionwindow import VolumeSelectionWindow
|
|
from options import Options, MetaDataStyle
|
|
from genericmetadata import GenericMetadata
|
|
from comicvinetalker import ComicVineTalker
|
|
from comicarchive import ComicArchive
|
|
import utils
|
|
import locale
|
|
# this reads the environment and inits the right locale
|
|
locale.setlocale(locale.LC_ALL, "")
|
|
|
|
|
|
import os
|
|
class TaggerWindow( QtGui.QMainWindow):
|
|
|
|
appName = "ComicTagger"
|
|
|
|
def __init__(self, opts , parent = None):
|
|
super(TaggerWindow, self).__init__(parent)
|
|
|
|
uic.loadUi('taggerwindow.ui', self)
|
|
self.setWindowIcon(QtGui.QIcon('app.png'))
|
|
self.center()
|
|
self.raise_()
|
|
|
|
self.opts = opts
|
|
self.data_style = opts.data_style
|
|
|
|
#set up a default metadata object
|
|
self.metadata = GenericMetadata()
|
|
self.comic_archive = None
|
|
|
|
self.configMenus()
|
|
self.statusBar()
|
|
self.updateAppTitle()
|
|
self.setAcceptDrops(True)
|
|
self.droppedFile=None
|
|
|
|
|
|
self.populateComboBoxes()
|
|
|
|
# hook up the callbacks
|
|
self.cbDataStyle.currentIndexChanged.connect(self.setDataStyle)
|
|
|
|
self.updateStyleTweaks()
|
|
|
|
|
|
self.openArchive( opts.filename )
|
|
|
|
# fill in some explicit metadata stuff from our options
|
|
# this overrides what we just read in
|
|
if self.metadata.series is None:
|
|
self.metadata.series = opts.series_name
|
|
if self.metadata.issueNumber is None:
|
|
self.metadata.issueNumber = opts.issue_number
|
|
|
|
|
|
def updateAppTitle( self ):
|
|
if self.comic_archive is None:
|
|
self.setWindowTitle( self.appName )
|
|
else:
|
|
self.setWindowTitle( self.appName + " - " + self.comic_archive.path)
|
|
|
|
def configMenus( self):
|
|
|
|
# File Menu
|
|
self.actionExit.setShortcut( 'Ctrl+Q' )
|
|
self.actionExit.setStatusTip( 'Exit application' )
|
|
self.actionExit.triggered.connect( QtGui.qApp.quit )
|
|
|
|
self.actionLoad.setShortcut( 'Ctrl+O' )
|
|
self.actionLoad.setStatusTip( 'Load comic archive' )
|
|
self.actionLoad.triggered.connect( self.selectFile )
|
|
|
|
self.actionWrite_Tags.setShortcut( 'Ctrl+S' )
|
|
self.actionWrite_Tags.setStatusTip( 'Save tags to comic archive' )
|
|
self.actionWrite_Tags.triggered.connect( self.commitMetadata )
|
|
|
|
#self.actionRepackage.setShortcut( )
|
|
self.actionRepackage.setStatusTip( 'Re-create archive as CBZ' )
|
|
self.actionRepackage.triggered.connect( self.repackageArchive )
|
|
|
|
# Tag Menu
|
|
self.actionParse_Filename.setShortcut( 'Ctrl+F' )
|
|
self.actionParse_Filename.setStatusTip( 'Try to extract tags from filename' )
|
|
self.actionParse_Filename.triggered.connect( self.useFilename )
|
|
|
|
self.actionQuery_Online.setShortcut( 'Ctrl+W' )
|
|
self.actionQuery_Online.setStatusTip( 'Search online for tags' )
|
|
self.actionQuery_Online.triggered.connect( self.queryOnline )
|
|
|
|
# Help Menu
|
|
self.actionAbout.setShortcut( 'Ctrl+A' )
|
|
self.actionAbout.setStatusTip( 'Show the ' + self.appName + ' info' )
|
|
self.actionAbout.triggered.connect( self.aboutApp )
|
|
|
|
# ToolBar
|
|
|
|
self.toolBar.addAction( self.actionLoad )
|
|
self.toolBar.addAction( self.actionWrite_Tags )
|
|
self.toolBar.addAction( self.actionParse_Filename )
|
|
self.toolBar.addAction( self.actionQuery_Online )
|
|
|
|
|
|
def repackageArchive( self ):
|
|
QtGui.QMessageBox.information(self, self.tr("Repackage Comic Archive"), self.tr("TBD"))
|
|
|
|
def aboutApp( self ):
|
|
QtGui.QMessageBox.information(self, self.tr("About " + self.appName ), self.tr(self.appName + " 0.1"))
|
|
|
|
|
|
def dragEnterEvent(self, event):
|
|
self.droppedFile=None
|
|
if event.mimeData().hasUrls():
|
|
url=event.mimeData().urls()[0]
|
|
if url.isValid():
|
|
if url.scheme()=="file":
|
|
self.droppedFile=url.toLocalFile()
|
|
event.accept()
|
|
|
|
def dropEvent(self, event):
|
|
#print self.droppedFile # displays the file name
|
|
self.openArchive( str(self.droppedFile) )
|
|
|
|
def openArchive( self, path ):
|
|
|
|
if path is None or path == "":
|
|
return
|
|
|
|
ca = ComicArchive( path )
|
|
|
|
if ca is not None and ca.seemsToBeAComicArchive():
|
|
|
|
self.comic_archive = ca
|
|
|
|
self.metadata = self.comic_archive.readMetadata( self.data_style )
|
|
|
|
if self.metadata.isEmpty:
|
|
self.metadata = self.comic_archive.metadataFromFilename( )
|
|
|
|
image_data = self.comic_archive.getCoverPage()
|
|
if not image_data is None:
|
|
img = QtGui.QImage()
|
|
img.loadFromData( image_data )
|
|
self.lblCover.setPixmap(QtGui.QPixmap(img))
|
|
self.lblCover.setScaledContents(True)
|
|
|
|
#!!!ATB should I clear the form???
|
|
self.metadataToForm()
|
|
self.updateAppTitle()
|
|
self.updateInfoBox()
|
|
|
|
|
|
else:
|
|
QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("That file doesn't appear to be a comic archive!"))
|
|
|
|
def updateInfoBox( self ):
|
|
|
|
ca = self.comic_archive
|
|
info_text = os.path.basename( ca.path ) + "\n"
|
|
info_text += str(ca.getNumberOfPages()) + " pages \n"
|
|
if ca.hasCIX():
|
|
info_text += "* ComicRack tags\n"
|
|
if ca.hasCBI():
|
|
info_text += "* ComicBookLover tags\n"
|
|
|
|
self.lblArchiveInfo.setText( info_text )
|
|
|
|
def metadataToForm( self ):
|
|
# copy the the metadata object into to the form
|
|
|
|
#helper func
|
|
def assignText( field, value):
|
|
if value is not None:
|
|
field.setText( u"{0}".format(value) )
|
|
|
|
md = self.metadata
|
|
|
|
assignText( self.leSeries, md.series )
|
|
assignText( self.leIssueNum, md.issueNumber )
|
|
assignText( self.leIssueCount, md.issueCount )
|
|
assignText( self.leVolumeNum, md.volumeNumber )
|
|
assignText( self.leVolumeCount, md.volumeCount )
|
|
assignText( self.leTitle, md.title )
|
|
assignText( self.lePublisher, md.publisher )
|
|
assignText( self.lePubMonth, md.publicationMonth )
|
|
assignText( self.lePubYear, md.publicationYear )
|
|
assignText( self.leGenre, md.genre )
|
|
assignText( self.leImprint, md.imprint )
|
|
assignText( self.teComments, md.comments )
|
|
assignText( self.teNotes, md.notes )
|
|
assignText( self.leCriticalRating, md.criticalRating )
|
|
assignText( self.leMaturityRating, md.maturityRating )
|
|
assignText( self.leStoryArc, md.storyArc )
|
|
assignText( self.leScanInfo, md.scanInfo )
|
|
assignText( self.leSeriesGroup, md.seriesGroup )
|
|
assignText( self.leAltSeries, md.alternateSeries )
|
|
assignText( self.leAltIssueNum, md.alternateNumber )
|
|
assignText( self.leAltIssueCount, md.alternateCount )
|
|
assignText( self.leWebLink, md.webLink )
|
|
assignText( self.teCharacters, md.characters )
|
|
assignText( self.teTeams, md.teams )
|
|
assignText( self.teLocations, md.locations )
|
|
assignText( self.leFormat, md.format )
|
|
|
|
if md.language is not None:
|
|
i = self.cbLanguage.findData( md.language )
|
|
self.cbLanguage.setCurrentIndex( i )
|
|
|
|
if md.country is not None:
|
|
i = self.cbCountry.findText( md.country )
|
|
self.cbCountry.setCurrentIndex( i )
|
|
|
|
if md.manga is not None:
|
|
i = self.cbManga.findData( md.manga )
|
|
self.cbManga.setCurrentIndex( i )
|
|
|
|
if md.blackAndWhite is not None and md.blackAndWhite:
|
|
self.cbBW.setChecked( True )
|
|
|
|
assignText( self.teTags, utils.listToString( md.tags ) )
|
|
|
|
# !!! Should we clear the credits table or just avoid duplicates?
|
|
while self.twCredits.rowCount() > 0:
|
|
self.twCredits.removeRow(0)
|
|
|
|
if md.credits is not None and len(md.credits) != 0:
|
|
|
|
self.twCredits.setSortingEnabled( False )
|
|
|
|
row = 0
|
|
for credit in md.credits:
|
|
|
|
# before we add the credit, see if the role-person pair already exists:
|
|
r = 0
|
|
while r < self.twCredits.rowCount():
|
|
if ( self.twCredits.item(r, 0).text() == credit['role'].title() and
|
|
self.twCredits.item(r, 1).text() == credit['person'] ):
|
|
break
|
|
r = r + 1
|
|
|
|
# if we didn't make it through the table, it's there alread, so continue without adding
|
|
if ( r != self.twCredits.rowCount() ):
|
|
continue
|
|
|
|
self.twCredits.insertRow(row)
|
|
|
|
item_text = credit['role'].title()
|
|
item = QtGui.QTableWidgetItem(item_text)
|
|
#item.setData( QtCore.Qt.UserRole ,record['id'])
|
|
item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
|
|
self.twCredits.setItem(row, 0, item)
|
|
|
|
item_text = credit['person']
|
|
item = QtGui.QTableWidgetItem(item_text)
|
|
item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled)
|
|
self.twCredits.setItem(row, 1, item)
|
|
|
|
row += 1
|
|
|
|
self.twCredits.setSortingEnabled( True )
|
|
|
|
|
|
def formToMetadata( self ):
|
|
|
|
#helper func
|
|
def xlate( data, type_str):
|
|
s = u"{0}".format(data).strip()
|
|
if s == "":
|
|
return None
|
|
elif type_str == "str":
|
|
return s
|
|
else:
|
|
return int(s)
|
|
|
|
# copy the data from the form into the metadata
|
|
md = self.metadata
|
|
md.series = xlate( self.leSeries.text(), "str" )
|
|
md.issueNumber = xlate( self.leIssueNum.text(), "str" )
|
|
md.issueCount = xlate( self.leIssueCount.text(), "int" )
|
|
md.volumeNumber = xlate( self.leVolumeNum.text(), "int" )
|
|
md.volumeCount = xlate( self.leVolumeCount.text(), "int" )
|
|
md.title = xlate( self.leTitle.text(), "str" )
|
|
md.publisher = xlate( self.lePublisher.text(), "str" )
|
|
md.publicationMonth = xlate( self.lePubMonth.text(), "int" )
|
|
md.publicationYear = xlate( self.lePubYear.text(), "int" )
|
|
md.genre = xlate( self.leGenre.text(), "str" )
|
|
md.imprint = xlate( self.leImprint.text(), "str" )
|
|
md.comments = xlate( self.teComments.toPlainText(), "str" )
|
|
md.notes = xlate( self.teNotes.toPlainText(), "str" )
|
|
md.criticalRating = xlate( self.leCriticalRating.text(), "int" )
|
|
md.maturityRating = xlate( self.leMaturityRating.text(), "str" )
|
|
|
|
md.storyArc = xlate( self.leStoryArc.text(), "str" )
|
|
md.scanInfo = xlate( self.leScanInfo.text(), "str" )
|
|
md.seriesGroup = xlate( self.leSeriesGroup.text(), "str" )
|
|
md.alternateSeries = xlate( self.leAltSeries.text(), "str" )
|
|
md.alternateNumber = xlate( self.leAltIssueNum.text(), "int" )
|
|
md.alternateCount = xlate( self.leAltIssueCount.text(), "int" )
|
|
md.webLink = xlate( self.leWebLink.text(), "str" )
|
|
md.characters = xlate( self.teCharacters.toPlainText(), "str" )
|
|
md.teams = xlate( self.teTeams.toPlainText(), "str" )
|
|
md.locations = xlate( self.teLocations.toPlainText(), "str" )
|
|
|
|
md.format = xlate( self.leFormat.text(), "str" )
|
|
md.country = xlate( self.cbCountry.currentText(), "str" )
|
|
|
|
langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex()).toString()
|
|
md.language = xlate( langiso, "str" )
|
|
|
|
manga_code = self.cbManga.itemData(self.cbManga.currentIndex()).toString()
|
|
md.manga = xlate( manga_code, "str" )
|
|
|
|
# Make a list from the coma delimited tags string
|
|
tmp = xlate( self.teTags.toPlainText(), "str" )
|
|
if tmp != None:
|
|
def striplist(l):
|
|
return([x.strip() for x in l])
|
|
|
|
md.tags = striplist(tmp.split( "," ))
|
|
|
|
if ( self.cbBW.isChecked() ):
|
|
md.blackAndWhite = True
|
|
else:
|
|
md.blackAndWhite = False
|
|
|
|
|
|
def useFilename( self ):
|
|
self.metadata = self.comic_archive.metadataFromFilename( )
|
|
self.metadataToForm()
|
|
|
|
|
|
def selectFile( self ):
|
|
path = str(QtGui.QFileDialog.getOpenFileName())
|
|
self.openArchive( path )
|
|
|
|
def queryOnline(self):
|
|
|
|
if str(self.leSeries.text()).strip() != "":
|
|
series_name = str(self.leSeries.text()).strip()
|
|
else:
|
|
QtGui.QMessageBox.information(self, self.tr("Whoops"), self.tr("Need to enter a series name to query."))
|
|
return
|
|
|
|
issue_number = str(self.leIssueNum.text()).strip()
|
|
|
|
selector = VolumeSelectionWindow( self, series_name, issue_number )
|
|
selector.setModal(True)
|
|
selector.exec_()
|
|
|
|
if selector.result():
|
|
#we should now have a volume ID
|
|
|
|
comicVine = ComicVineTalker()
|
|
self.metadata = comicVine.fetchIssueData( selector.volume_id, selector.issue_number )
|
|
|
|
# Now push the right data into the edit controls
|
|
self.metadataToForm()
|
|
#!!!ATB should I clear the form???
|
|
|
|
def commitMetadata(self):
|
|
|
|
if (not self.metadata is None and not self.comic_archive is None):
|
|
self.formToMetadata()
|
|
self.comic_archive.writeMetadata( self.metadata, self.data_style )
|
|
self.updateInfoBox()
|
|
|
|
QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
|
|
|
|
|
|
else:
|
|
QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("No data to commit!"))
|
|
|
|
|
|
def setDataStyle(self, s):
|
|
self.data_style, b = self.cbDataStyle.itemData(s).toInt()
|
|
self.updateStyleTweaks()
|
|
|
|
|
|
def updateStyleTweaks( self ):
|
|
|
|
# depending on the current data style, certain fields are disabled
|
|
|
|
inactive_color = QtGui.QColor(255, 170, 150)
|
|
active_palette = self.leSeries.palette()
|
|
|
|
inactive_palette1 = self.leSeries.palette()
|
|
inactive_palette1.setColor(QtGui.QPalette.Base, inactive_color)
|
|
|
|
inactive_palette2 = self.leSeries.palette()
|
|
|
|
inactive_palette3 = self.leSeries.palette()
|
|
inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
|
|
|
|
inactive_palette3.setColor(QtGui.QPalette.Base, inactive_color)
|
|
|
|
#helper func
|
|
def enableWidget( item, enable ):
|
|
inactive_palette3.setColor(item.backgroundRole(), inactive_color)
|
|
inactive_palette2.setColor(item.backgroundRole(), inactive_color)
|
|
inactive_palette3.setColor(item.foregroundRole(), inactive_color)
|
|
|
|
if enable:
|
|
item.setPalette(active_palette)
|
|
if type(item) == QtGui.QCheckBox:
|
|
item.setEnabled( True )
|
|
elif type(item) == QtGui.QComboBox:
|
|
item.setEnabled( True )
|
|
else:
|
|
item.setReadOnly( False )
|
|
else:
|
|
if type(item) == QtGui.QCheckBox:
|
|
item.setPalette(inactive_palette2)
|
|
item.setEnabled( False )
|
|
elif type(item) == QtGui.QComboBox:
|
|
item.setPalette(inactive_palette3)
|
|
item.setEnabled( False )
|
|
else:
|
|
item.setReadOnly( True )
|
|
item.setPalette(inactive_palette1)
|
|
|
|
|
|
cbi_only = [ self.leVolumeCount, self.cbCountry, self.leCriticalRating, self.teTags ]
|
|
cix_only = [
|
|
self.leImprint, self.teNotes, self.cbBW, self.cbManga,
|
|
self.leStoryArc, self.leScanInfo, self.leSeriesGroup,
|
|
self.leAltSeries, self.leAltIssueNum, self.leAltIssueCount,
|
|
self.leWebLink, self.teCharacters, self.teTeams,
|
|
self.teLocations, self.leMaturityRating, self.leFormat
|
|
]
|
|
|
|
if self.data_style == MetaDataStyle.CIX:
|
|
for item in cix_only:
|
|
enableWidget( item, True )
|
|
for item in cbi_only:
|
|
enableWidget(item, False )
|
|
|
|
if self.data_style == MetaDataStyle.CBI:
|
|
for item in cbi_only:
|
|
enableWidget( item, True )
|
|
for item in cix_only:
|
|
enableWidget(item, False )
|
|
|
|
|
|
|
|
def center(self):
|
|
screen = QtGui.QDesktopWidget().screenGeometry()
|
|
size = self.geometry()
|
|
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
|
|
|
|
def populateComboBoxes( self ):
|
|
|
|
# Add the entries to the tag style combobox
|
|
self.cbDataStyle.addItem( "ComicBookLover", MetaDataStyle.CBI )
|
|
self.cbDataStyle.addItem( "ComicRack", MetaDataStyle.CIX )
|
|
|
|
# select the current style
|
|
if ( self.data_style == MetaDataStyle.CBI ):
|
|
self.cbDataStyle.setCurrentIndex ( 0 )
|
|
elif ( self.data_style == MetaDataStyle.CIX ):
|
|
self.cbDataStyle.setCurrentIndex ( 1 )
|
|
|
|
# Add the entries to the country combobox
|
|
self.cbCountry.addItem( "", "" )
|
|
for c in utils.countries:
|
|
self.cbCountry.addItem( c[1], c[0] )
|
|
|
|
# Add the entries to the language combobox
|
|
self.cbLanguage.addItem( "", "" )
|
|
lang_dict = utils.getLanguageDict()
|
|
for key in sorted(lang_dict, cmp=locale.strcoll, key=lang_dict.get):
|
|
self.cbLanguage.addItem( lang_dict[key], key )
|
|
|
|
# Add the entries to the manga combobox
|
|
self.cbManga.addItem( "", "" )
|
|
self.cbManga.addItem( "Yes", "Yes" )
|
|
self.cbManga.addItem( "Yes (Right to Left)", "YesAndRightToLeft" )
|
|
self.cbManga.addItem( "No", "No" )
|