Color tweaking tweaked

Attempt at using listview/model for pages


git-svn-id: http://comictagger.googlecode.com/svn/trunk@50 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville@gmail.com 2012-11-16 19:32:46 +00:00
parent 904acf35f3
commit 693ad006a4
9 changed files with 397 additions and 68 deletions

View File

@ -453,36 +453,45 @@ class ComicArchive:
def getPage( self, index ):
num_pages = self.getNumberOfPages()
image_data = None
filename = self.getPageName( index )
if filename is not None:
image_data = self.archiver.readArchiveFile( filename )
return image_data
def getPageName( self, index ):
page_list = self.getPageNameList()
num_pages = len( page_list )
if num_pages == 0 or index >= num_pages:
return None
return page_list[index]
def getPageNameList( self , sort_list=True):
# get the list file names in the archive, and sort
files = self.archiver.getArchiveFilenameList()
# seems like some archive creators are on Windows, and don't know about case-sensitivity!
files.sort(key=lambda x: x.lower())
if sort_list:
files.sort(key=lambda x: x.lower())
# make a sub-list of image files
page_list = []
for name in files:
if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png" ] ):
page_list.append(name)
return page_list
image_data = self.archiver.readArchiveFile( page_list[index] )
return image_data
def getNumberOfPages(self):
count = 0
def getNumberOfPages( self ):
for item in self.archiver.getArchiveFilenameList():
if ( item[-4:].lower() in [ ".jpg", "jpeg", ".png" ] ):
count += 1
return count
return len( self.getPageNameList( sort_list=False ) )
def readCBI( self ):

View File

@ -26,7 +26,27 @@ from genericmetadata import GenericMetadata
import utils
class ComicInfoXml:
writer_synonyms = ['writer', 'plotter', 'scripter']
penciller_synonyms = [ 'artist', 'penciller', 'penciler', 'breakdowns' ]
inker_synonyms = [ 'inker', 'artist', 'finishes' ]
colorist_synonyms = [ 'colorist', 'colourist', 'colorer', 'colourer' ]
letterer_synonyms = [ 'letterer']
cover_synonyms = [ 'cover', 'covers', 'coverartist', 'cover artist' ]
editor_synonyms = [ 'editor']
def getParseableCredits( self ):
parsable_credits = []
parsable_credits.extend( self.writer_synonyms )
parsable_credits.extend( self.penciller_synonyms )
parsable_credits.extend( self.inker_synonyms )
parsable_credits.extend( self.colorist_synonyms )
parsable_credits.extend( self.letterer_synonyms )
parsable_credits.extend( self.cover_synonyms )
parsable_credits.extend( self.editor_synonyms )
return parsable_credits
def metadataFromString( self, string ):
tree = ET.ElementTree(ET.fromstring( string ))
@ -110,25 +130,25 @@ class ComicInfoXml:
# first, loop thru credits, and build a list for each role that CIX supports
for credit in metadata.credits:
if credit['role'].lower() in set( ['writer', 'plotter', 'scripter'] ):
if credit['role'].lower() in set( self.writer_synonyms ):
credit_writer_list.append(credit['person'])
if credit['role'].lower() in set( [ 'artist', 'penciller', 'penciler', 'breakdowns' ] ):
if credit['role'].lower() in set( self.penciller_synonyms ):
credit_penciller_list.append(credit['person'])
if credit['role'].lower() in set( [ 'inker', 'artist', 'finishes' ] ):
if credit['role'].lower() in set( self.inker_synonyms ):
credit_inker_list.append(credit['person'])
if credit['role'].lower() in set( [ 'colorist', 'colourist', 'colorer', 'colourer' ]):
if credit['role'].lower() in set( self.colorist_synonyms ):
credit_colorist_list.append(credit['person'])
if credit['role'].lower() in set( [ 'letterer'] ):
if credit['role'].lower() in set( self.letterer_synonyms ):
credit_letterer_list.append(credit['person'])
if credit['role'].lower() in set( [ 'cover', 'covers', 'coverartist', 'cover artist' ] ):
if credit['role'].lower() in set( self.cover_synonyms ):
credit_cover_list.append(credit['person'])
if credit['role'].lower() in set( [ 'editor'] ):
if credit['role'].lower() in set( self.editor_synonyms ):
credit_editor_list.append(credit['person'])
# second, convert each list to string, and add to XML struct

View File

@ -39,7 +39,8 @@ class ComicVineTalker(QObject):
def __init__(self, api_key):
QObject.__init__(self)
self.api_key = api_key
# key that is registered to comictagger
self.api_key = '27431e6787042105bd3e47e169a624521f89f3a4'
def testKey( self ):

View File

@ -32,6 +32,13 @@ from imagefetcher import ImageFetcher
import utils
class IssueIdentifier:
ResultNoMatches = 0
ResultFoundMatchButBadCoverScore = 1
ResultFoundMatchButNotFirstPage = 2
ResultMultipleMatchesWithBadImageScores = 3
ResultOneGoodMatch = 4
ResultMultipleGoodMatches = 5
def __init__(self, comic_archive, cv_api_key ):
self.comic_archive = comic_archive
@ -39,6 +46,7 @@ class IssueIdentifier:
self.additional_metadata = None
self.min_score_thresh = 22
self.min_score_distance = 2
self.strong_score_thresh = 8
self.additional_metadata = GenericMetadata()
self.cv_api_key = cv_api_key
self.output_function = IssueIdentifier.defaultWriteOutput
@ -263,7 +271,7 @@ class IssueIdentifier:
if len(self.match_list) == 0:
self.log_msg( ":-( no matches!" )
return self.match_list
# sort list by image match scores
self.match_list.sort(key=lambda k: k['distance'])
@ -286,13 +294,33 @@ class IssueIdentifier:
if len(self.match_list) == 1:
if best_score > self.min_score_thresh:
self.log_msg( "!!!! Very weak score for the cover. Maybe it's not the cover?" )
self.log_msg( "Comparing other pages now..." )
found = False
for i in range(ca.getNumberOfPages()):
image_data = ca.getPage(i)
page_hash = self.calculateHash( image_data )
distance = ImageHasher.hamming_distance(page_hash, self.match_list[0]['url_image_hash'])
if distance <= self.strong_score_thresh:
print "Found a great match d={0} on page {1}!".format(distance, i+1)
found = True
break
elif distance < self.min_score_thresh:
print "Found a good match d={0} on page {1}".format(distance, i)
found = True
self.log_msg( ".", newline=False )
self.log_msg( "" )
if not found:
self.log_msg( "No matching pages in the issue. Bummer" )
print_match(self.match_list[0])
return self.match_list
elif best_score > self.min_score_thresh and len(self.match_list) > 1:
self.log_msg( "No good image matches! Need to use other info..." )
return self.match_list
#now pare down list, remove any item more than specified distant from the top scores
for item in reversed(self.match_list):
if item['distance'] > best_score + self.min_score_distance:
@ -307,7 +335,7 @@ class IssueIdentifier:
self.log_msg( "More than one likley candiate. Maybe a lexical comparison??" )
for item in self.match_list:
print_match(item)
return self.match_list

View File

@ -109,6 +109,6 @@ class PageBrowserWindow(QtGui.QDialog):
new_w = 0;
scaled_pixmap = self.current_pixmap.scaled(new_w, new_h, QtCore.Qt.KeepAspectRatio)
self.lblPage.setPixmap( scaled_pixmap )

View File

@ -89,7 +89,20 @@ def main():
splash.show()
splash.raise_()
app.processEvents()
app.processEvents()
"""
lw = QtGui.QListWidget()
icon = QtGui.QIcon('app.png')
for i in range(10):
lw.addItem( QtGui.QListWidgetItem( icon, "Item {0}".format(i) ) )
lw.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
#lw.setViewMode(QtGui.QListView.IconMode)
lw.setMovement(QtGui.QListView.Snap)
lw.setGridSize(QtCore.QSize(100,100))
lw.show()
sys.exit(app.exec_())
"""
try:
tagger_window = TaggerWindow( opts, settings )

View File

@ -28,6 +28,7 @@ import os
from volumeselectionwindow import VolumeSelectionWindow
from options import Options, MetaDataStyle
from comicinfoxml import ComicInfoXml
from genericmetadata import GenericMetadata
from comicvinetalker import ComicVineTalker
from comicarchive import ComicArchive
@ -63,6 +64,163 @@ def clickable(widget):
widget.installEventFilter(filter)
return filter.dblclicked
"""
class PageTableModel(QtCore.QAbstractTableModel):
def __init__(self, comic_archive, parent=None, *args):
QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.comic_archive = comic_archive
page_list = comic_archive.getPageNameList()
self.page_model = []
i = 0
for page in page_list:
item = dict()
item['number'] = i
item['filename'] = page
item['thumb'] = None
self.page_model.append( item )
i +=1
def rowCount(self, parent):
return len(self.page_model)
def columnCount(self, parent):
return 3
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role == QtCore.Qt.DisplayRole:
# page num
if index.column() == 0:
return QtCore.QVariant(self.page_model[index.row()]['number'])
# page filename
if index.column() == 1:
return QtCore.QVariant(self.page_model[index.row()]['filename'])
elif role == QtCore.Qt.DecorationRole:
if index.column() == 2:
if self.page_model[index.row()]['thumb'] is None:
image_data = self.comic_archive.getPage( self.page_model[index.row()]['number'] )
img = QtGui.QImage()
img.loadFromData( image_data )
pixmap = QtGui.QPixmap(QtGui.QPixmap(img))
#scaled_pixmap = pixmap.scaled(100, 150, QtCore.Qt.KeepAspectRatio)
self.page_model[index.row()]['thumb'] = pixmap #scaled_pixmap
return QtCore.QVariant(self.page_model[index.row()]['thumb'])
else:
return QtCore.QVariant()
"""
class PageListModel(QtCore.QAbstractListModel):
def __init__(self, comic_archive, parent=None, *args):
QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.comic_archive = comic_archive
page_list = comic_archive.getPageNameList()
self.page_model = []
i = 0
for page in page_list:
item = dict()
item['number'] = i
item['filename'] = page
item['thumb'] = None
self.page_model.append( item )
i +=1
def rowCount(self, parent):
return len(self.page_model)
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role == QtCore.Qt.DisplayRole:
# page num
return QtCore.QVariant(self.page_model[index.row()]['number'])
elif role == QtCore.Qt.DecorationRole:
if self.page_model[index.row()]['thumb'] is None:
#timestamp = datetime.datetime.now()
image_data = self.comic_archive.getPage( self.page_model[index.row()]['number'] )
img = QtGui.QImage()
img.loadFromData( image_data )
pixmap = QtGui.QPixmap(QtGui.QPixmap(img))
scaled_pixmap = pixmap.scaled(100, 150, QtCore.Qt.KeepAspectRatio)
self.page_model[index.row()]['thumb'] = scaled_pixmap
return QtCore.QVariant(self.page_model[index.row()]['thumb'])
else:
return QtCore.QVariant()
def flags( self, index):
defaultFlags = QtCore.QAbstractTableModel.flags(self, index)
if index.isValid():
return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
else:
return QtCore.Qt.ItemIsDropEnabled | defaultFlags
def removeRows(self, row, count, parent=QtCore.QModelIndex()):
print "removeRows", row, count
return True
def insertRows(self, row, count, parent=QtCore.QModelIndex()):
print "insertRows", row, count
return False
def beginRemoveRows(self, sourceParent, start, end, destinationParent, dest):
print "beginRemoveRows"
def dropMimeData(self,data, action, row, col, parent):
print "dropMimeData", action, row, col
if (row != -1):
beginRow = row
elif (parent.isValid()):
beginRow = parent.row()
print beginRow
return True
if (action == QtCore.Qt.IgnoreAction):
return True
#if ( not data.hasFormat("application/vnd.text.list"))
# return False
if (column > 0):
return False
#def beginMoveRows(self, sourceParent, start, end, destinationParent, dest):
# print "rowsMoved"
def supportedDropActions(self):
#print "supportedDropActions"
return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
class TaggerWindow( QtGui.QMainWindow):
@ -312,7 +470,8 @@ class TaggerWindow( QtGui.QMainWindow):
self.metadataToForm()
self.clearDirtyFlag() # also updates the app title
self.updateInfoBox()
self.updateInfoBox()
#self.updatePagesInfo()
else:
QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("That file doesn't appear to be a comic archive!"))
@ -349,7 +508,39 @@ class TaggerWindow( QtGui.QMainWindow):
self.lblTagList.setText( tag_info )
def updatePagesInfo( self ):
#tablemodel = PageTableModel( self.comic_archive, self )
#self.tableView.setModel(tablemodel)
listmodel = PageListModel( self.comic_archive, self )
self.listView.setModel(listmodel)
#self.listView.setDragDropMode(self.InternalMove)
#listmodel.installEventFilter(self)
self.listView.setDragEnabled(True)
self.listView.setAcceptDrops(True)
self.listView.setDropIndicatorShown(True)
#listmodel.rowsMoved.connect( self.rowsMoved )
#listmodel.rowsRemoved.connect( self.rowsRemoved )
#listmodel.beginMoveRows.connect( self.beginMoveRows )
#def rowsMoved( self, b, c, d):
# print "rowsMoved"
#def rowsRemoved( self,b, c, d):
# print "rowsRemoved"
"""
def eventFilter(self, sender, event):
if (event.type() == QtCore.QEvent.ChildRemoved):
print "QEvent::ChildRemoved"
return False # don't actually interrupt anything
"""
def setDirtyFlag( self, param1=None, param2=None, param3=None ):
if not self.dirtyFlag:
self.dirtyFlag = True
@ -495,6 +686,7 @@ class TaggerWindow( QtGui.QMainWindow):
row += 1
self.twCredits.setSortingEnabled( True )
self.updateCreditColors()
def addNewCreditEntry( self, row, role, name ):
self.twCredits.insertRow(row)
@ -520,7 +712,6 @@ class TaggerWindow( QtGui.QMainWindow):
return False
def formToMetadata( self ):
#helper func
@ -681,6 +872,32 @@ class TaggerWindow( QtGui.QMainWindow):
def setDataStyle(self, s):
self.data_style, b = self.cbDataStyle.itemData(s).toInt()
self.updateStyleTweaks()
def updateCreditColors( self ):
inactive_color = QtGui.QColor(255, 170, 150)
active_palette = self.leSeries.palette()
active_color = active_palette.color( QtGui.QPalette.Base )
cix_credits = ComicInfoXml().getParseableCredits()
if self.data_style == MetaDataStyle.CIX:
#loop over credit table, mark selected rows
r = 0
while r < self.twCredits.rowCount():
if str(self.twCredits.item(r, 0).text()).lower() not in cix_credits:
print "Bad credit for CIX:", self.twCredits.item(r, 0).text()
self.twCredits.item(r, 0).setBackgroundColor( inactive_color )
else:
self.twCredits.item(r, 0).setBackgroundColor( active_color )
r = r + 1
if self.data_style == MetaDataStyle.CBI:
#loop over credit table, make all active color
r = 0
while r < self.twCredits.rowCount():
self.twCredits.item(r, 0).setBackgroundColor( active_color )
r = r + 1
def updateStyleTweaks( self ):
@ -707,6 +924,7 @@ class TaggerWindow( QtGui.QMainWindow):
if enable:
item.setPalette(active_palette)
item.setAutoFillBackground( False )
if type(item) == QtGui.QCheckBox:
item.setEnabled( True )
elif type(item) == QtGui.QComboBox:
@ -714,6 +932,7 @@ class TaggerWindow( QtGui.QMainWindow):
else:
item.setReadOnly( False )
else:
item.setAutoFillBackground( True )
if type(item) == QtGui.QCheckBox:
item.setPalette(inactive_palette2)
item.setEnabled( False )
@ -733,7 +952,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.leWebLink, self.teCharacters, self.teTeams,
self.teLocations, self.cbMaturityRating, self.cbFormat
]
if self.data_style == MetaDataStyle.CIX:
for item in cix_only:
enableWidget( item, True )
@ -746,6 +965,8 @@ class TaggerWindow( QtGui.QMainWindow):
for item in cix_only:
enableWidget(item, False )
self.updateCreditColors()
def cellDoubleClicked( self, r, c ):
self.editCredit()
@ -804,7 +1025,8 @@ class TaggerWindow( QtGui.QMainWindow):
# add new entry
row = self.twCredits.rowCount()
self.addNewCreditEntry( row, new_role, new_name)
self.updateCreditColors()
self.setDirtyFlag()
def removeCredit( self ):

View File

@ -96,7 +96,7 @@
</font>
</property>
<property name="text">
<string>filename</string>
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -131,7 +131,7 @@
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>archive type</string>
<string/>
</property>
<property name="wordWrap">
<bool>false</bool>
@ -152,7 +152,7 @@
</font>
</property>
<property name="text">
<string>page count</string>
<string/>
</property>
</widget>
</item>
@ -167,7 +167,7 @@
</sizepolicy>
</property>
<property name="text">
<string>tag list</string>
<string/>
</property>
</widget>
</item>
@ -386,12 +386,12 @@
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string># Issues</string>
<string>Volume</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="leIssueCount"/>
<widget class="QLineEdit" name="leVolumeNum"/>
</item>
</layout>
</item>
@ -403,12 +403,12 @@
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Volume</string>
<string># Issues</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leVolumeNum"/>
<widget class="QLineEdit" name="leIssueCount"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
@ -458,6 +458,9 @@
</item>
<item>
<layout class="QFormLayout" name="formLayout_16">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_35">
<property name="text">
@ -497,7 +500,7 @@
<item row="0" column="1">
<widget class="QComboBox" name="cbMaturityRating">
<property name="autoFillBackground">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
@ -557,7 +560,7 @@
<item row="1" column="1">
<widget class="QCheckBox" name="cbBW">
<property name="autoFillBackground">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="text">
<string>Black &amp;&amp; White</string>
@ -574,7 +577,7 @@
<item row="2" column="1">
<widget class="QComboBox" name="cbManga">
<property name="autoFillBackground">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -962,6 +965,39 @@
<attribute name="title">
<string>Pages</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QListView" name="listView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="movement">
<enum>QListView::Snap</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="gridSize">
<size>
<width>100</width>
<height>170</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="selectionRectVisible">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
@ -977,7 +1013,7 @@
<x>0</x>
<y>0</y>
<width>975</width>
<height>28</height>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuComicTagger">

View File

@ -3,17 +3,12 @@
Features
----------------
Toolbar icons
Infobox needs fixing up
Page Browser, mode-less dialog
Auto-select:
msgbox on autoselect failure, or warning
Multi-match dialog
More auto-select logic using metadata
Page search on only match, but bad image match, to find cover??
Maybe, if only one match, but bad score, compare each page in the archive to online cover
Check aspect ratio, and maybe break cover into two parts for hashing?
Stand-alone CLI
@ -24,16 +19,16 @@ Stand-alone CLI
TaggerWindow entry fields
General layout
Special tabbed Dialogs needed for:
Pages Info
Color changing stuff need more work
- Indicate credits for CR style
CR has editable dropdowns/comboboxes for Format, Publisher, Imprint
Pages Info - maybe a custom painted widget
Verify/warn on save (maybe just on over-write?)
Style sheets for windows/mac/linux
Add class for warning/info messages with "Don't show again" checkbox.
Add list of these flag to settings
Version 2 - GUI to handle mutliple files or folders
-----------
@ -42,9 +37,19 @@ Version 2 - GUI to handle mutliple files or folders
-----------------
Bugs
----------------
Disable CBL for RAR
SERIOUS BUG: rebuilding zips!
http://stackoverflow.com/questions/11578443/trigger-io-errno-18-cross-device-link
MAC:
toolbar
weird unrar complaints
Page browser sizing
Disable save when read-only
Be more tolerant of mis-labled extensions i.e. cbr when it's a cbz
Form type validation Ints vs strings for month, year. etc
@ -59,10 +64,8 @@ Other settings possibilities:
Clear caches
Content Hashes!!
Image Hashes:
Failures of average hash:
Failures of image hash:
Thor 600 Wrap-around w/ different aspect ratio
Bone 3 - Variant Cover,
Avengers #1, #13, #81
@ -90,9 +93,13 @@ Auto search:
Lexical analysis
Searching w/o issue #?
Determine alternate covers from CV somehow
-------------
Other
------------
Content Hashes!!
Archive function to detect tag blocks out of sync
@ -104,7 +111,6 @@ Longer term:
Think about mass tagging and (semi) automatic volume selection
----------------------------------------------
COMIC RACK Questions
@ -119,9 +125,3 @@ Some that seem library only:
Proposed Values
Community Rating
Mac Notes:
python ~/pyinstaller-2.0/pyinstaller.py tagger.py -w -i windows/nsis/app.ico