Added page browser, other misc stuff

git-svn-id: http://comictagger.googlecode.com/svn/trunk@35 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville@gmail.com 2012-11-15 01:25:01 +00:00
parent 448e2a9d89
commit 54f41a5ac0
14 changed files with 1054 additions and 671 deletions

View File

@ -448,24 +448,32 @@ class ComicArchive:
def getCoverPage(self):
if self.getNumberOfPages() == 0:
# assume first page is the cover (for now)
return self.getPage( 0 )
def getPage( self, index ):
num_pages = self.getNumberOfPages()
if num_pages == 0 or index >= num_pages:
return None
# get the list file names in the archive, and sort
files = self.archiver.getArchiveFilenameList()
# seems like the scanners are on Windows, and don't know about case-sensitivity!
# seems like some archive creators are on Windows, and don't know about case-sensitivity!
files.sort(key=lambda x: x.lower())
# find the first image file, assume it's the cover
# make a sub-list of image files
page_list = []
for name in files:
if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png" ] ):
break
page_list.append(name)
image_data = self.archiver.readArchiveFile( name )
image_data = self.archiver.readArchiveFile( page_list[index] )
return image_data
def getNumberOfPages(self):
count = 0

3
ctversion.py Normal file
View File

@ -0,0 +1,3 @@
# This file should contan only these comments, and the line below.
# Used by packaging makefiles and app
version="0.0.1"

View File

@ -20,6 +20,7 @@ limitations under the License.
import sys
import getopt
import platform
class Enum(set):
def __getattr__(self, name):
@ -47,7 +48,7 @@ class Options:
def parseCmdLineArgs(self):
# mac no likey this from .app bundle
if getattr(sys, 'frozen', None):
if platform.system() == "Darwin" and getattr(sys, 'frozen', None):
return
# parse command line options
@ -83,9 +84,8 @@ class Options:
print( __doc__ )
sys.exit(0)
# process arguments
for arg in args:
process(arg) # process() is defined elsewhere
if self.filename == "" and len(args) > 0:
self.filename = args[0]
return opts

100
pagebrowser.py Normal file
View File

@ -0,0 +1,100 @@
"""
A PyQT4 dialog to show pages of a comic archive
"""
"""
Copyright 2012 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import sys
from PyQt4 import QtCore, QtGui, uic
import os
from settings import ComicTaggerSettings
class PageBrowserWindow(QtGui.QDialog):
def __init__(self, parent):
super(PageBrowserWindow, self).__init__(None)
uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'pagebrowser.ui' ), self)
self.lblPage.setPixmap(QtGui.QPixmap(os.path.join(ComicTaggerSettings.baseDir(), 'nocover.png' )))
self.lblPage.setSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Ignored)
self.comic_archive = None
self.current_pixmap = None
self.page_count = 0
self.current_page_num = 0
self.btnNext.clicked.connect( self.nextPage )
self.btnPrev.clicked.connect( self.prevPage )
self.show()
def setComicArchive(self, ca):
self.comic_archive = ca
self.page_count = ca.getNumberOfPages()
self.current_page_num = 0
self.setPage()
def nextPage(self):
if self.current_page_num + 1 < self.page_count:
self.current_page_num += 1
self.setPage()
def prevPage(self):
if self.current_page_num - 1 >= 0:
self.current_page_num -= 1
self.setPage()
def setPage( self ):
image_data = self.comic_archive.getPage( self.current_page_num )
if image_data is not None:
self.setCurrentPixmap( image_data )
self.setDisplayPixmap( 0, 0)
self.setWindowTitle("Page Browser - Page {0} (of {1}) ".format(self.current_page_num+1, self.page_count ) )
def setCurrentPixmap( self, image_data ):
if image_data is not None:
img = QtGui.QImage()
img.loadFromData( image_data )
self.current_pixmap = QtGui.QPixmap(QtGui.QPixmap(img))
def resizeEvent( self, resize_event ):
if self.current_pixmap is not None:
delta_w = resize_event.size().width() - resize_event.oldSize().width()
delta_h = resize_event.size().height() - resize_event.oldSize().height()
self.setDisplayPixmap( delta_w , delta_h )
def setDisplayPixmap( self, delta_w , delta_h ):
# the deltas let us know what the new width and height of the label will be
new_h = self.lblPage.height() + delta_h
new_w = self.lblPage.width() + delta_w
if new_h < 0:
new_h = 0;
if new_w < 0:
new_w = 0;
scaled_pixmap = self.current_pixmap.scaled(new_w, new_h, QtCore.Qt.KeepAspectRatio)
self.lblPage.setPixmap( scaled_pixmap )

129
pagebrowser.ui Normal file
View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>dialogPageBrowser</class>
<widget class="QDialog" name="dialogPageBrowser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>429</width>
<height>637</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Page Browser</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblPage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>150</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QPushButton" name="btnPrev">
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnNext">
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>dialogPageBrowser</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>dialogPageBrowser</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -44,10 +44,12 @@ class ComicTaggerSettings:
@staticmethod
def baseDir():
if getattr(sys, 'frozen', None):
return sys._MEIPASS
if platform.system() == "Darwin" and getattr(sys, 'frozen', None):
return sys._MEIPASS
elif platform.system() == "Windows":
return "."
else:
return os.path.dirname(__file__)
return os.path.dirname(__file__)
def __init__(self):

View File

@ -23,6 +23,8 @@ limitations under the License.
import sys
import signal
import os
import traceback
import time
from PyQt4 import QtCore, QtGui
@ -82,10 +84,22 @@ def main():
app = QtGui.QApplication(sys.argv)
tagger_window = TaggerWindow( opts, settings )
tagger_window.show()
sys.exit(app.exec_())
img = QtGui.QPixmap(os.path.join(ComicTaggerSettings.baseDir(), 'graphics/tags.png' ))
splash = QtGui.QSplashScreen(img)
splash.show()
splash.raise_()
app.processEvents()
app.processEvents()
try:
tagger_window = TaggerWindow( opts, settings )
tagger_window.show()
splash.finish( tagger_window )
sys.exit(app.exec_())
except Exception, e:
QtGui.QMessageBox.critical(QtGui.QMainWindow(), "Error", "Unhandled exception in app:\n" + traceback.format_exc() )
if __name__ == "__main__":
main()

View File

@ -19,8 +19,11 @@ limitations under the License.
"""
from PyQt4 import QtCore, QtGui, uic
from PyQt4.QtCore import QUrl,pyqtSignal
import locale
import platform
import os
from volumeselectionwindow import VolumeSelectionWindow
from options import Options, MetaDataStyle
@ -30,17 +33,39 @@ from comicarchive import ComicArchive
from crediteditorwindow import CreditEditorWindow
from settingswindow import SettingsWindow
from settings import ComicTaggerSettings
from pagebrowser import PageBrowserWindow
import utils
import ctversion
# this reads the environment and inits the right locale
locale.setlocale(locale.LC_ALL, "")
import os
# helper func to allow a label to be clickable
def clickable(widget):
class Filter(QtCore.QObject):
dblclicked = pyqtSignal()
def eventFilter(self, obj, event):
if obj == widget:
if event.type() == QtCore.QEvent.MouseButtonDblClick:
self.dblclicked.emit()
return True
return False
filter = Filter(widget)
widget.installEventFilter(filter)
return filter.dblclicked
class TaggerWindow( QtGui.QMainWindow):
appName = "ComicTagger"
version = "1.0" # TODO read in from a file??
version = ctversion.version
def __init__(self, opts, settings, parent = None):
super(TaggerWindow, self).__init__(parent)
@ -69,6 +94,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.setAcceptDrops(True)
self.droppedFile=None
self.page_browser = None
self.populateComboBoxes()
@ -78,6 +104,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.btnAddCredit.clicked.connect(self.addCredit)
self.btnRemoveCredit.clicked.connect(self.removeCredit)
self.twCredits.cellDoubleClicked.connect(self.editCredit)
clickable(self.lblCover).connect(self.showPageBrowser)
self.connectDirtyFlagSignals()
self.updateStyleTweaks()
@ -260,6 +287,9 @@ class TaggerWindow( QtGui.QMainWindow):
img.loadFromData( image_data )
self.lblCover.setPixmap(QtGui.QPixmap(img))
self.lblCover.setScaledContents(True)
if self.page_browser is not None:
self.page_browser.setComicArchive( self.comic_archive )
self.metadataToForm()
self.clearDirtyFlag() # also updates the app title
@ -565,6 +595,7 @@ class TaggerWindow( QtGui.QMainWindow):
selector = VolumeSelectionWindow( self, self.settings.cv_api_key, series_name, issue_number, self.comic_archive, self.settings )
selector.setModal(True)
selector.exec_()
if selector.result():
#we should now have a volume ID
@ -848,3 +879,13 @@ class TaggerWindow( QtGui.QMainWindow):
else:
event.ignore()
def showPageBrowser( self ):
if self.page_browser is None:
self.page_browser = PageBrowserWindow( self )
if self.comic_archive is not None:
self.page_browser.setComicArchive( self.comic_archive )
self.page_browser.finished.connect(self.pageBrowserClosed)
def pageBrowserClosed( self ):
self.page_browser = None

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,13 @@ Infobox needs fixing up
Page Browser, mode-less dialog
Multi-match dialog
More auto-select logic using metadata
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??
Check aspect ratio, and maybe break cover into two parts for hashing?
Stand-alone CLI
Info dump
optionless args
@ -53,7 +56,8 @@ Lots of error checking
Other settings possibilities:
Last tag style
Last "Open" folder (include dragged)
Keep a history of queries somewhere??
Clear caches
Content Hashes!!

View File

@ -101,13 +101,9 @@ class VolumeSelectionWindow(QtGui.QDialog):
self.btnIssues.clicked.connect(self.showIssues)
self.btnAutoSelect.clicked.connect(self.autoSelect)
self.show()
QtCore.QCoreApplication.processEvents()
self.performQuery()
self.twList.selectRow(0)
def requery( self, ):
self.performQuery( refresh=True )
self.twList.selectRow(0)
@ -188,9 +184,10 @@ class VolumeSelectionWindow(QtGui.QDialog):
self.search_thread.progressUpdate.connect( self.searchProgressUpdate )
self.search_thread.start()
QtCore.QCoreApplication.processEvents()
#QtCore.QCoreApplication.processEvents()
self.progdialog.exec_()
def searchCanceled( self ):
print "query cancelled"
self.search_thread.searchComplete.disconnect( self.searchComplete )

View File

@ -1,25 +1,37 @@
# This Makefile expects that certain GNU utils are available:
# rm, cp, grep, cut, cat
TAGGER_BASE:= c:\Users\tony\Dropbox\tagger\comictagger
DIST_DIR := $(TAGGER_BASE)\windows\dist
NSIS_CMD := "C:\Program Files (x86)\NSIS\makensis.exe"
VERSION := $(shell grep version "$(TAGGER_BASE)\ctversion.py" | cut -d= -f2)
all: clean
all: clean dist package
win_dist:
dist:
cd "$(TAGGER_BASE)" &
"C:\Python27\Scripts\cxfreeze.bat" $(TAGGER_BASE)\tagger.py --icon nsis\app.ico --base-name=Win32GUI
"C:\Python27\Scripts\cxfreeze.bat" $(TAGGER_BASE)\tagger.py --icon nsis\app.ico
# --base-name=Win32GUI
cp -R C:\Python27\Lib\site-packages\PyQt4\plugins\imageformats $(DIST_DIR)
cp "$(TAGGER_BASE)\UnRAR2\UnRARDLL\unrar.dll" $(DIST_DIR)
cp "$(TAGGER_BASE)\*.ui" $(DIST_DIR)
cp "$(TAGGER_BASE)\nocover.png" $(DIST_DIR)
cp "$(TAGGER_BASE)\app.png" $(DIST_DIR)
cp -r "$(TAGGER_BASE)\graphics" $(DIST_DIR)
rm "$(DIST_DIR)\QtWebKit4.dll"
rm "$(DIST_DIR)\PyQt4.QtWebKit.pyd"
package:
echo !define RELEASE_STR $(VERSION) > $(TAGGER_BASE)\windows\nsis\release.nsh
$(NSIS_CMD) "$(TAGGER_BASE)\windows\nsis\comictagger.nsi"
mv "$(TAGGER_BASE)\windows\nsis\ComicTagger*.exe" "$(TAGGER_BASE)\windows"
clean:
rm -rf dist
-rm -rf dist
-rm -f "*~" *.pyc *.pyo
-rm "$(TAGGER_BASE)\windows\*.exe"
-rm -f "$(TAGGER_BASE)\windows\*.exe"
test:
echo !define RELEASE_STR $(VERSION) > test.nsh

View File

@ -1 +1 @@
make clean win_dist
make clean dist package

View File

@ -1 +0,0 @@
!define RELEASE_STR "1.0"