@ -6,14 +6,18 @@ Created on Wed May 29 00:02:24 2019
@author : ivan
@author : ivan
"""
"""
import sys , fitz , io , subprocess , os
import sys
import fitz
import io
import subprocess
import os
from copy import copy
from copy import copy
from PyQt5.QtWidgets import QApplication , QAction , QLabel , QDialogButtonBox , QDialog , QFileDialog , QMessageBox , QPushButton , QLineEdit , QCheckBox , QSpinBox , QDoubleSpinBox , QTableWidgetItem , QTabWidget , QComboBox , QWidget , QScrollArea , QMainWindow , QShortcut
from PyQt5.QtWidgets import QApplication , QAction , QLabel , QDialogButtonBox , QDialog , QFileDialog , QMessageBox , QPushButton , QLineEdit , QCheckBox , QSpinBox , QDoubleSpinBox , QTableWidgetItem , QTabWidget , QComboBox , QWidget , QScrollArea , QMainWindow , QShortcut
from PyQt5.QtCore import QFile , QObject , Qt , pyqtSlot , QSettings
from PyQt5.QtCore import QFile , QObject , Qt , pyqtSlot , QSettings
from PyQt5.QtGui import QPixmap , QImage , QKeySequence
from PyQt5.QtGui import QPixmap , QImage , QKeySequence
from PyQt5 import uic
from PyQt5 import uic
from chordsheet.tableView import ChordTableView , BlockTableView , MItemModel , MProxyStyle
from chordsheet.tableView import ChordTableView , BlockTableView
from reportlab.lib.units import mm , cm , inch , pica
from reportlab.lib.units import mm , cm , inch , pica
from reportlab.lib.pagesizes import A4 , A5 , LETTER , LEGAL
from reportlab.lib.pagesizes import A4 , A5 , LETTER , LEGAL
@ -32,24 +36,30 @@ if getattr(sys, 'frozen', False):
else :
else :
scriptDir = os . path . abspath ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
scriptDir = os . path . abspath ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
QApplication . setAttribute ( Qt . AA_EnableHighDpiScaling , True ) # enable automatic high DPI scaling on Windows
# enable automatic high DPI scaling on Windows
QApplication . setAttribute ( Qt . AA_EnableHighDpiScaling , True )
QApplication . setOrganizationName ( " Ivan Holmes " )
QApplication . setOrganizationName ( " Ivan Holmes " )
QApplication . setOrganizationDomain ( " ivanholmes.co.uk " )
QApplication . setOrganizationDomain ( " ivanholmes.co.uk " )
QApplication . setApplicationName ( " Chordsheet " )
QApplication . setApplicationName ( " Chordsheet " )
settings = QSettings ( )
settings = QSettings ( )
pdfmetrics . registerFont ( TTFont ( ' FreeSans ' , os . path . join ( scriptDir , ' fonts ' , ' FreeSans.ttf ' ) ) )
pdfmetrics . registerFont (
TTFont ( ' FreeSans ' , os . path . join ( scriptDir , ' fonts ' , ' FreeSans.ttf ' ) ) )
if sys . platform == " darwin " :
if sys . platform == " darwin " :
pdfmetrics . registerFont ( TTFont ( ' HelveticaNeue ' , ' HelveticaNeue.ttc ' , subfontIndex = 0 ) )
pdfmetrics . registerFont (
TTFont ( ' HelveticaNeue ' , ' HelveticaNeue.ttc ' , subfontIndex = 0 ) )
# dictionaries for combo boxes
# dictionaries for combo boxes
pageSizeDict = { ' A4 ' : A4 , ' A5 ' : A5 , ' Letter ' : LETTER , ' Legal ' : LEGAL }
pageSizeDict = { ' A4 ' : A4 , ' A5 ' : A5 , ' Letter ' : LETTER , ' Legal ' : LEGAL }
unitDict = { ' mm ' : mm , ' cm ' : cm , ' inch ' : inch , ' point ' : 1 , ' pica ' : pica } # point is 1 because reportlab's native unit is points.
# point is 1 because reportlab's native unit is points.
unitDict = { ' mm ' : mm , ' cm ' : cm , ' inch ' : inch , ' point ' : 1 , ' pica ' : pica }
class DocumentWindow ( QMainWindow ) :
class DocumentWindow ( QMainWindow ) :
"""
"""
Class for the main window of the application .
Class for the main window of the application .
"""
"""
def __init__ ( self , doc , style , filename = None ) :
def __init__ ( self , doc , style , filename = None ) :
"""
"""
Initialisation function for the main window of the application .
Initialisation function for the main window of the application .
@ -77,7 +87,7 @@ class DocumentWindow(QMainWindow):
if filename :
if filename :
try :
try :
self . openFile ( filename )
self . openFile ( filename )
except :
except Exception :
UnreadableMessageBox ( ) . exec ( )
UnreadableMessageBox ( ) . exec ( )
def closeEvent ( self , event ) :
def closeEvent ( self , event ) :
@ -103,7 +113,8 @@ class DocumentWindow(QMainWindow):
self . window . actionOpen . triggered . connect ( self . menuFileOpenAction )
self . window . actionOpen . triggered . connect ( self . menuFileOpenAction )
self . window . actionSave . triggered . connect ( self . menuFileSaveAction )
self . window . actionSave . triggered . connect ( self . menuFileSaveAction )
self . window . actionSave_as . triggered . connect ( self . menuFileSaveAsAction )
self . window . actionSave_as . triggered . connect ( self . menuFileSaveAsAction )
self . window . actionSave_PDF . triggered . connect ( self . menuFileSavePDFAction )
self . window . actionSave_PDF . triggered . connect (
self . menuFileSavePDFAction )
self . window . actionPrint . triggered . connect ( self . menuFilePrintAction )
self . window . actionPrint . triggered . connect ( self . menuFilePrintAction )
self . window . actionClose . triggered . connect ( self . menuFileCloseAction )
self . window . actionClose . triggered . connect ( self . menuFileCloseAction )
self . window . actionUndo . triggered . connect ( self . menuEditUndoAction )
self . window . actionUndo . triggered . connect ( self . menuEditUndoAction )
@ -125,14 +136,18 @@ class DocumentWindow(QMainWindow):
self . window . actionCopy . setShortcut ( QKeySequence . Copy )
self . window . actionCopy . setShortcut ( QKeySequence . Copy )
self . window . actionPaste . setShortcut ( QKeySequence . Paste )
self . window . actionPaste . setShortcut ( QKeySequence . Paste )
self . window . pageSizeComboBox . currentIndexChanged . connect ( self . pageSizeAction )
self . window . documentUnitsComboBox . currentIndexChanged . connect ( self . unitAction )
self . window . pageSizeComboBox . currentIndexChanged . connect (
self . pageSizeAction )
self . window . documentUnitsComboBox . currentIndexChanged . connect (
self . unitAction )
self . window . includedFontCheckBox . stateChanged . connect ( self . includedFontAction )
self . window . includedFontCheckBox . stateChanged . connect (
self . includedFontAction )
self . window . generateButton . clicked . connect ( self . generateAction )
self . window . generateButton . clicked . connect ( self . generateAction )
self . window . guitarVoicingButton . clicked . connect ( self . guitarVoicingAction )
self . window . guitarVoicingButton . clicked . connect (
self . guitarVoicingAction )
self . window . addChordButton . clicked . connect ( self . addChordAction )
self . window . addChordButton . clicked . connect ( self . addChordAction )
self . window . removeChordButton . clicked . connect ( self . removeChordAction )
self . window . removeChordButton . clicked . connect ( self . removeChordAction )
self . window . updateChordButton . clicked . connect ( self . updateChordAction )
self . window . updateChordButton . clicked . connect ( self . updateChordAction )
@ -166,10 +181,12 @@ class DocumentWindow(QMainWindow):
Fills the window ' s fields with the values from its style.
Fills the window ' s fields with the values from its style.
"""
"""
self . window . pageSizeComboBox . addItems ( list ( pageSizeDict . keys ( ) ) )
self . window . pageSizeComboBox . addItems ( list ( pageSizeDict . keys ( ) ) )
self . window . pageSizeComboBox . setCurrentText ( list ( pageSizeDict . keys ( ) ) [ 0 ] )
self . window . pageSizeComboBox . setCurrentText (
list ( pageSizeDict . keys ( ) ) [ 0 ] )
self . window . documentUnitsComboBox . addItems ( list ( unitDict . keys ( ) ) )
self . window . documentUnitsComboBox . addItems ( list ( unitDict . keys ( ) ) )
self . window . documentUnitsComboBox . setCurrentText ( list ( unitDict . keys ( ) ) [ 0 ] )
self . window . documentUnitsComboBox . setCurrentText (
list ( unitDict . keys ( ) ) [ 0 ] )
self . window . lineSpacingDoubleSpinBox . setValue ( self . style . lineSpacing )
self . window . lineSpacingDoubleSpinBox . setValue ( self . style . lineSpacing )
@ -195,15 +212,20 @@ class DocumentWindow(QMainWindow):
def chordClickedAction ( self , index ) :
def chordClickedAction ( self , index ) :
# set the controls to the values from the selected chord
# set the controls to the values from the selected chord
self . window . chordNameLineEdit . setText ( self . window . chordTableView . model . item ( index . row ( ) , 0 ) . text ( ) )
self . window . guitarVoicingLineEdit . setText ( self . window . chordTableView . model . item ( index . row ( ) , 1 ) . text ( ) )
self . window . chordNameLineEdit . setText (
self . window . chordTableView . model . item ( index . row ( ) , 0 ) . text ( ) )
self . window . guitarVoicingLineEdit . setText (
self . window . chordTableView . model . item ( index . row ( ) , 1 ) . text ( ) )
def blockClickedAction ( self , index ) :
def blockClickedAction ( self , index ) :
# set the controls to the values from the selected block
# set the controls to the values from the selected block
bChord = self . window . blockTableView . model . item ( index . row ( ) , 0 ) . text ( )
bChord = self . window . blockTableView . model . item ( index . row ( ) , 0 ) . text ( )
self . window . blockChordComboBox . setCurrentText ( bChord if bChord else " None " )
self . window . blockLengthLineEdit . setText ( self . window . blockTableView . model . item ( index . row ( ) , 1 ) . text ( ) )
self . window . blockNotesLineEdit . setText ( self . window . blockTableView . model . item ( index . row ( ) , 2 ) . text ( ) )
self . window . blockChordComboBox . setCurrentText (
bChord if bChord else " None " )
self . window . blockLengthLineEdit . setText (
self . window . blockTableView . model . item ( index . row ( ) , 1 ) . text ( ) )
self . window . blockNotesLineEdit . setText (
self . window . blockTableView . model . item ( index . row ( ) , 2 ) . text ( ) )
def getPath ( self , value ) :
def getPath ( self , value ) :
"""
"""
@ -219,13 +241,16 @@ class DocumentWindow(QMainWindow):
def menuFileNewAction ( self ) :
def menuFileNewAction ( self ) :
self . doc = Document ( ) # new document object
self . doc = Document ( ) # new document object
self . lastDoc = copy ( self . doc ) # copy this object as reference to check against on quitting
self . currentFilePath = None # reset file path (this document hasn't been saved yet)
# copy this object as reference to check against on quitting
self . lastDoc = copy ( self . doc )
# reset file path (this document hasn't been saved yet)
self . currentFilePath = None
self . UIInitDocument ( )
self . UIInitDocument ( )
self . updatePreview ( )
self . updatePreview ( )
def menuFileOpenAction ( self ) :
def menuFileOpenAction ( self ) :
filePath = QFileDialog . getOpenFileName ( self . window . tabWidget , ' Open file ' , self . getPath ( " workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
filePath = QFileDialog . getOpenFileName ( self . window . tabWidget , ' Open file ' , self . getPath (
" workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
if filePath :
if filePath :
self . openFile ( filePath )
self . openFile ( filePath )
@ -243,14 +268,16 @@ class DocumentWindow(QMainWindow):
def menuFileSaveAction ( self ) :
def menuFileSaveAction ( self ) :
self . updateDocument ( )
self . updateDocument ( )
if not self . currentFilePath :
if not self . currentFilePath :
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath ( " workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath (
" workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
else :
else :
filePath = self . currentFilePath
filePath = self . currentFilePath
self . saveFile ( filePath )
self . saveFile ( filePath )
def menuFileSaveAsAction ( self ) :
def menuFileSaveAsAction ( self ) :
self . updateDocument ( )
self . updateDocument ( )
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath ( " workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath (
" workingPath " ) , " Chordsheet ML files (*.xml *.cml) " ) [ 0 ]
if filePath :
if filePath :
self . saveFile ( filePath )
self . saveFile ( filePath )
@ -267,7 +294,8 @@ class DocumentWindow(QMainWindow):
def menuFileSavePDFAction ( self ) :
def menuFileSavePDFAction ( self ) :
self . updateDocument ( )
self . updateDocument ( )
self . updatePreview ( )
self . updatePreview ( )
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath ( " lastExportPath " ) , " PDF files (*.pdf) " ) [ 0 ]
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , self . getPath (
" lastExportPath " ) , " PDF files (*.pdf) " ) [ 0 ]
if filePath :
if filePath :
savePDF ( d , s , filePath )
savePDF ( d , s , filePath )
self . setPath ( " lastExportPath " , filePath )
self . setPath ( " lastExportPath " , filePath )
@ -284,36 +312,36 @@ class DocumentWindow(QMainWindow):
self . saveWarning ( )
self . saveWarning ( )
def menuFileAboutAction ( self ) :
def menuFileAboutAction ( self ) :
aDialog = AboutDialog ( )
AboutDialog ( )
def menuEditUndoAction ( self ) :
def menuEditUndoAction ( self ) :
try :
try :
QApplication . focusWidget ( ) . undo ( ) # see if the built in widget supports it
QApplication . focusWidget ( ) . undo ( ) # see if the built in widget supports it
except :
except Exception :
pass # if not just fail silently
pass # if not just fail silently
def menuEditRedoAction ( self ) :
def menuEditRedoAction ( self ) :
try :
try :
QApplication . focusWidget ( ) . redo ( )
QApplication . focusWidget ( ) . redo ( )
except :
except Exception :
pass
pass
def menuEditCutAction ( self ) :
def menuEditCutAction ( self ) :
try :
try :
QApplication . focusWidget ( ) . cut ( )
QApplication . focusWidget ( ) . cut ( )
except :
except Exception :
pass
pass
def menuEditCopyAction ( self ) :
def menuEditCopyAction ( self ) :
try :
try :
QApplication . focusWidget ( ) . copy ( )
QApplication . focusWidget ( ) . copy ( )
except :
except Exception :
pass
pass
def menuEditPasteAction ( self ) :
def menuEditPasteAction ( self ) :
try :
try :
QApplication . focusWidget ( ) . paste ( )
QApplication . focusWidget ( ) . paste ( )
except :
except Exception :
pass
pass
def saveWarning ( self ) :
def saveWarning ( self ) :
@ -322,14 +350,15 @@ class DocumentWindow(QMainWindow):
"""
"""
self . updateDocument ( ) # update the document to catch all changes
self . updateDocument ( ) # update the document to catch all changes
if ( self . lastDoc == self . doc ) :
if self . lastDoc == self . doc :
self . close ( )
self . close ( )
else :
else :
wantToSave = UnsavedMessageBox ( ) . exec ( )
wantToSave = UnsavedMessageBox ( ) . exec ( )
if wantToSave == QMessageBox . Save :
if wantToSave == QMessageBox . Save :
if not ( self . currentFilePath ) :
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , str ( os . path . expanduser ( " ~ " ) ) , " Chordsheet ML files (*.xml *.cml) " )
if not self . currentFilePath :
filePath = QFileDialog . getSaveFileName ( self . window . tabWidget , ' Save file ' , str (
os . path . expanduser ( " ~ " ) ) , " Chordsheet ML files (*.xml *.cml) " )
self . currentFilePath = filePath [ 0 ]
self . currentFilePath = filePath [ 0 ]
self . doc . saveXML ( self . currentFilePath )
self . doc . saveXML ( self . currentFilePath )
self . close ( )
self . close ( )
@ -348,7 +377,8 @@ class DocumentWindow(QMainWindow):
def clearChordLineEdits ( self ) :
def clearChordLineEdits ( self ) :
self . window . chordNameLineEdit . clear ( )
self . window . chordNameLineEdit . clear ( )
self . window . guitarVoicingLineEdit . clear ( )
self . window . guitarVoicingLineEdit . clear ( )
self . window . chordNameLineEdit . repaint ( ) # necessary on Mojave with PyInstaller (or previous contents will be shown)
# necessary on Mojave with PyInstaller (or previous contents will be shown)
self . window . chordNameLineEdit . repaint ( )
self . window . guitarVoicingLineEdit . repaint ( )
self . window . guitarVoicingLineEdit . repaint ( )
def updateChordDict ( self ) :
def updateChordDict ( self ) :
@ -380,9 +410,10 @@ class DocumentWindow(QMainWindow):
self . doc . chordList . append ( Chord ( cName ) )
self . doc . chordList . append ( Chord ( cName ) )
if self . window . guitarVoicingLineEdit . text ( ) :
if self . window . guitarVoicingLineEdit . text ( ) :
try :
try :
self . doc . chordList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering ( self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
self . doc . chordList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering (
self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
success = True # chord successfully parsed
success = True # chord successfully parsed
except :
except Exception :
VoicingWarningMessageBox ( ) . exec ( ) # Voicing is malformed, warn user
VoicingWarningMessageBox ( ) . exec ( ) # Voicing is malformed, warn user
else :
else :
success = True # chord successfully parsed
success = True # chord successfully parsed
@ -404,9 +435,10 @@ class DocumentWindow(QMainWindow):
self . doc . chordList [ row ] = Chord ( cName )
self . doc . chordList [ row ] = Chord ( cName )
if self . window . guitarVoicingLineEdit . text ( ) :
if self . window . guitarVoicingLineEdit . text ( ) :
try :
try :
self . doc . chordList [ row ] . voicings [ ' guitar ' ] = parseFingering ( self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
self . doc . chordList [ row ] . voicings [ ' guitar ' ] = parseFingering (
self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
success = True
success = True
except :
except Exception :
VoicingWarningMessageBox ( ) . exec ( )
VoicingWarningMessageBox ( ) . exec ( )
else :
else :
success = True
success = True
@ -421,7 +453,8 @@ class DocumentWindow(QMainWindow):
def clearBlockLineEdits ( self ) :
def clearBlockLineEdits ( self ) :
self . window . blockLengthLineEdit . clear ( )
self . window . blockLengthLineEdit . clear ( )
self . window . blockNotesLineEdit . clear ( )
self . window . blockNotesLineEdit . clear ( )
self . window . blockLengthLineEdit . repaint ( ) # necessary on Mojave with PyInstaller (or previous contents will be shown)
# necessary on Mojave with PyInstaller (or previous contents will be shown)
self . window . blockLengthLineEdit . repaint ( )
self . window . blockNotesLineEdit . repaint ( )
self . window . blockNotesLineEdit . repaint ( )
def removeBlockAction ( self ) :
def removeBlockAction ( self ) :
@ -437,18 +470,21 @@ class DocumentWindow(QMainWindow):
self . updateBlocks ( )
self . updateBlocks ( )
try :
try :
bLength = int ( self . window . blockLengthLineEdit . text ( ) ) # can the value entered for block length be cast as an integer
except :
# can the value entered for block length be cast as an integer
bLength = int ( self . window . blockLengthLineEdit . text ( ) )
except Exception :
bLength = False
bLength = False
if bLength : # create the block
if bLength : # create the block
self . doc . blockList . append ( Block ( bLength ,
self . doc . blockList . append ( Block ( bLength ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText ( ) ] ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText (
) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
self . window . blockTableView . populate ( self . doc . blockList )
self . window . blockTableView . populate ( self . doc . blockList )
self . clearBlockLineEdits ( )
self . clearBlockLineEdits ( )
else :
else :
LengthWarningMessageBox ( ) . exec ( ) # show warning that length was not entered or in wrong format
# show warning that length was not entered or in wrong format
LengthWarningMessageBox ( ) . exec ( )
def updateBlockAction ( self ) :
def updateBlockAction ( self ) :
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
@ -456,13 +492,14 @@ class DocumentWindow(QMainWindow):
try :
try :
bLength = int ( self . window . blockLengthLineEdit . text ( ) )
bLength = int ( self . window . blockLengthLineEdit . text ( ) )
except :
except Exception :
bLength = False
bLength = False
row = self . window . blockTableView . selectionModel ( ) . currentIndex ( ) . row ( )
row = self . window . blockTableView . selectionModel ( ) . currentIndex ( ) . row ( )
if bLength :
if bLength :
self . doc . blockList [ row ] = ( Block ( bLength ,
self . doc . blockList [ row ] = ( Block ( bLength ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText ( ) ] ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText (
) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
self . window . blockTableView . populate ( self . doc . blockList )
self . window . blockTableView . populate ( self . doc . blockList )
self . clearBlockLineEdits ( )
self . clearBlockLineEdits ( )
@ -482,23 +519,28 @@ class DocumentWindow(QMainWindow):
savePDF ( self . doc , self . style , self . currentPreview )
savePDF ( self . doc , self . style , self . currentPreview )
pdfView = fitz . Document ( stream = self . currentPreview , filetype = ' pdf ' )
pdfView = fitz . Document ( stream = self . currentPreview , filetype = ' pdf ' )
pix = pdfView [ 0 ] . getPixmap ( matrix = fitz . Matrix ( 4 , 4 ) , alpha = False ) # render at 4x resolution and scale
# render at 4x resolution and scale
pix = pdfView [ 0 ] . getPixmap ( matrix = fitz . Matrix ( 4 , 4 ) , alpha = False )
fmt = QImage . Format_RGB888
fmt = QImage . Format_RGB888
qtimg = QImage ( pix . samples , pix . width , pix . height , pix . stride , fmt )
qtimg = QImage ( pix . samples , pix . width , pix . height , pix . stride , fmt )
self . window . imageLabel . setPixmap ( QPixmap . fromImage ( qtimg ) . scaled ( self . window . scrollArea . width ( ) - 30 , self . window . scrollArea . height ( ) - 30 , Qt . KeepAspectRatio , transformMode = Qt . SmoothTransformation ) )
self . window . imageLabel . setPixmap ( QPixmap . fromImage ( qtimg ) . scaled ( self . window . scrollArea . width (
) - 30 , self . window . scrollArea . height ( ) - 30 , Qt . KeepAspectRatio , transformMode = Qt . SmoothTransformation ) )
# -30 because the scrollarea has a margin of 12 each side (extra for safety)
# -30 because the scrollarea has a margin of 12 each side (extra for safety)
self . window . imageLabel . repaint ( ) # necessary on Mojave with PyInstaller (or previous contents will be shown)
except :
warning = QMessageBox . warning ( self , " Preview failed " , " Could not update the preview. " , buttons = QMessageBox . Ok , defaultButton = QMessageBox . Ok )
# necessary on Mojave with PyInstaller (or previous contents will be shown)
self . window . imageLabel . repaint ( )
except Exception :
QMessageBox . warning ( self , " Preview failed " , " Could not update the preview. " ,
buttons = QMessageBox . Ok , defaultButton = QMessageBox . Ok )
def updateTitleBar ( self ) :
def updateTitleBar ( self ) :
"""
"""
Update the application ' s title bar to reflect the current document.
Update the application ' s title bar to reflect the current document.
"""
"""
if self . currentFilePath :
if self . currentFilePath :
self . setWindowTitle ( _version . appName + " – " + os . path . basename ( self . currentFilePath ) )
self . setWindowTitle ( _version . appName + " – " +
os . path . basename ( self . currentFilePath ) )
else :
else :
self . setWindowTitle ( _version . appName )
self . setWindowTitle ( _version . appName )
@ -508,9 +550,11 @@ class DocumentWindow(QMainWindow):
"""
"""
chordTableList = [ ]
chordTableList = [ ]
for i in range ( self . window . chordTableView . model . rowCount ( ) ) :
for i in range ( self . window . chordTableView . model . rowCount ( ) ) :
chordTableList . append ( Chord ( parseName ( self . window . chordTableView . model . item ( i , 0 ) . text ( ) ) ) ) ,
chordTableList . append (
Chord ( parseName ( self . window . chordTableView . model . item ( i , 0 ) . text ( ) ) ) ) ,
if self . window . chordTableView . model . item ( i , 1 ) . text ( ) :
if self . window . chordTableView . model . item ( i , 1 ) . text ( ) :
chordTableList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering ( self . window . chordTableView . model . item ( i , 1 ) . text ( ) , ' guitar ' )
chordTableList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering (
self . window . chordTableView . model . item ( i , 1 ) . text ( ) , ' guitar ' )
self . doc . chordList = chordTableList
self . doc . chordList = chordTableList
@ -520,10 +564,14 @@ class DocumentWindow(QMainWindow):
"""
"""
blockTableList = [ ]
blockTableList = [ ]
for i in range ( self . window . blockTableView . model . rowCount ( ) ) :
for i in range ( self . window . blockTableView . model . rowCount ( ) ) :
blockLength = int ( self . window . blockTableView . model . item ( i , 1 ) . text ( ) )
blockChord = self . chordDict [ ( self . window . blockTableView . model . item ( i , 0 ) . text ( ) if self . window . blockTableView . model . item ( i , 0 ) . text ( ) else " None " ) ]
blockNotes = self . window . blockTableView . model . item ( i , 2 ) . text ( ) if self . window . blockTableView . model . item ( i , 2 ) . text ( ) else None
blockTableList . append ( Block ( blockLength , chord = blockChord , notes = blockNotes ) )
blockLength = int (
self . window . blockTableView . model . item ( i , 1 ) . text ( ) )
blockChord = self . chordDict [ ( self . window . blockTableView . model . item (
i , 0 ) . text ( ) if self . window . blockTableView . model . item ( i , 0 ) . text ( ) else " None " ) ]
blockNotes = self . window . blockTableView . model . item ( i , 2 ) . text (
) if self . window . blockTableView . model . item ( i , 2 ) . text ( ) else None
blockTableList . append (
Block ( blockLength , chord = blockChord , notes = blockNotes ) )
self . doc . blockList = blockTableList
self . doc . blockList = blockTableList
@ -531,41 +579,57 @@ class DocumentWindow(QMainWindow):
"""
"""
Update the Document object by reading values from the UI.
Update the Document object by reading values from the UI.
"""
"""
self . doc . title = self . window . titleLineEdit . text ( ) # Title can be empty string but not None
self . doc . subtitle = ( self . window . subtitleLineEdit . text ( ) if self . window . subtitleLineEdit . text ( ) else None )
self . doc . composer = ( self . window . composerLineEdit . text ( ) if self . window . composerLineEdit . text ( ) else None )
self . doc . arranger = ( self . window . arrangerLineEdit . text ( ) if self . window . arrangerLineEdit . text ( ) else None )
self . doc . tempo = ( self . window . tempoLineEdit . text ( ) if self . window . tempoLineEdit . text ( ) else None )
self . doc . timeSignature = int ( self . window . timeSignatureSpinBox . value ( ) ) if self . window . timeSignatureSpinBox . value ( ) else self . doc . timeSignature
self . doc . title = self . window . titleLineEdit . text (
) # Title can be empty string but not None
self . doc . subtitle = ( self . window . subtitleLineEdit . text (
) if self . window . subtitleLineEdit . text ( ) else None )
self . doc . composer = ( self . window . composerLineEdit . text (
) if self . window . composerLineEdit . text ( ) else None )
self . doc . arranger = ( self . window . arrangerLineEdit . text (
) if self . window . arrangerLineEdit . text ( ) else None )
self . doc . tempo = ( self . window . tempoLineEdit . text ( )
if self . window . tempoLineEdit . text ( ) else None )
self . doc . timeSignature = int ( self . window . timeSignatureSpinBox . value (
) ) if self . window . timeSignatureSpinBox . value ( ) else self . doc . timeSignature
self . style . pageSize = pageSizeDict [ self . pageSizeSelected ]
self . style . pageSize = pageSizeDict [ self . pageSizeSelected ]
self . style . unit = unitDict [ self . unitSelected ]
self . style . unit = unitDict [ self . unitSelected ]
self . style . leftMargin = float ( self . window . leftMarginLineEdit . text ( ) ) if self . window . leftMarginLineEdit . text ( ) else self . style . leftMargin
self . style . topMargin = float ( self . window . topMarginLineEdit . text ( ) ) if self . window . topMarginLineEdit . text ( ) else self . style . topMargin
self . style . lineSpacing = float ( self . window . lineSpacingDoubleSpinBox . value ( ) ) if self . window . lineSpacingDoubleSpinBox . value ( ) else self . style . lineSpacing
self . style . leftMargin = float ( self . window . leftMarginLineEdit . text (
) ) if self . window . leftMarginLineEdit . text ( ) else self . style . leftMargin
self . style . topMargin = float ( self . window . topMarginLineEdit . text (
) ) if self . window . topMarginLineEdit . text ( ) else self . style . topMargin
self . style . lineSpacing = float ( self . window . lineSpacingDoubleSpinBox . value (
) ) if self . window . lineSpacingDoubleSpinBox . value ( ) else self . style . lineSpacing
# make sure the unit width isn't too wide to draw!
# make sure the unit width isn't too wide to draw!
if self . window . beatWidthLineEdit . text ( ) :
if self . window . beatWidthLineEdit . text ( ) :
if ( self . style . pageSize [ 0 ] - 2 * self . style . leftMargin * mm ) > = ( float ( self . window . beatWidthLineEdit . text ( ) ) * 2 * self . doc . timeSignature * mm ) :
if ( self . style . pageSize [ 0 ] - 2 * self . style . leftMargin * mm ) > = ( float ( self . window . beatWidthLineEdit . text ( ) ) * 2 * self . doc . timeSignature * mm ) :
self . style . unitWidth = float ( self . window . beatWidthLineEdit . text ( ) )
self . style . unitWidth = float (
self . window . beatWidthLineEdit . text ( ) )
else :
else :
maxBeatWidth = ( self . style . pageSize [ 0 ] - 2 * self . style . leftMargin * mm ) / ( 2 * self . doc . timeSignature * mm )
warning = QMessageBox . warning ( self , " Out of range " , " Beat width is out of range. It can be a maximum of {}. " . format ( maxBeatWidth ) , buttons = QMessageBox . Ok , defaultButton = QMessageBox . Ok )
maxBeatWidth = (
self . style . pageSize [ 0 ] - 2 * self . style . leftMargin * mm ) / ( 2 * self . doc . timeSignature * mm )
QMessageBox . warning ( self , " Out of range " , " Beat width is out of range. It can be a maximum of {}. " . format (
maxBeatWidth ) , buttons = QMessageBox . Ok , defaultButton = QMessageBox . Ok )
self . updateChords ( )
self . updateChords ( )
self . updateBlocks ( )
self . updateBlocks ( )
self . style . font = ( ' FreeSans ' if self . style . useIncludedFont else ' HelveticaNeue ' )
self . style . font = (
' FreeSans ' if self . style . useIncludedFont else ' HelveticaNeue ' )
# something for the font box here
# something for the font box here
class GuitarDialog ( QDialog ) :
class GuitarDialog ( QDialog ) :
"""
"""
Dialogue to allow the user to enter a guitar chord voicing . Not particularly advanced at present !
Dialogue to allow the user to enter a guitar chord voicing . Not particularly advanced at present !
May be extended in future .
May be extended in future .
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . UIFileLoader ( str ( os . path . join ( scriptDir , ' ui ' , ' guitardialog.ui ' ) ) )
self . UIFileLoader (
str ( os . path . join ( scriptDir , ' ui ' , ' guitardialog.ui ' ) ) )
def UIFileLoader ( self , ui_file ) :
def UIFileLoader ( self , ui_file ) :
ui_file = QFile ( ui_file )
ui_file = QFile ( ui_file )
@ -590,16 +654,19 @@ class GuitarDialog(QDialog):
else :
else :
return None
return None
class AboutDialog ( QDialog ) :
class AboutDialog ( QDialog ) :
"""
"""
Dialogue showing information about the program .
Dialogue showing information about the program .
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . UIFileLoader ( str ( os . path . join ( scriptDir , ' ui ' , ' aboutdialog.ui ' ) ) )
self . UIFileLoader ( str ( os . path . join ( scriptDir , ' ui ' , ' aboutdialog.ui ' ) ) )
icon = QImage ( str ( os . path . join ( scriptDir , ' ui ' , ' icon.png ' ) ) )
icon = QImage ( str ( os . path . join ( scriptDir , ' ui ' , ' icon.png ' ) ) )
self . dialog . iconLabel . setPixmap ( QPixmap . fromImage ( icon ) . scaled ( self . dialog . iconLabel . width ( ) , self . dialog . iconLabel . height ( ) , Qt . KeepAspectRatio , transformMode = Qt . SmoothTransformation ) )
self . dialog . iconLabel . setPixmap ( QPixmap . fromImage ( icon ) . scaled ( self . dialog . iconLabel . width (
) , self . dialog . iconLabel . height ( ) , Qt . KeepAspectRatio , transformMode = Qt . SmoothTransformation ) )
self . dialog . versionLabel . setText ( " Version " + _version . version )
self . dialog . versionLabel . setText ( " Version " + _version . version )
@ -612,10 +679,12 @@ class AboutDialog(QDialog):
self . dialog = uic . loadUi ( ui_file )
self . dialog = uic . loadUi ( ui_file )
ui_file . close ( )
ui_file . close ( )
class UnsavedMessageBox ( QMessageBox ) :
class UnsavedMessageBox ( QMessageBox ) :
"""
"""
Message box to alert the user of unsaved changes and allow them to choose how to act .
Message box to alert the user of unsaved changes and allow them to choose how to act .
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
@ -623,13 +692,16 @@ class UnsavedMessageBox(QMessageBox):
self . setWindowTitle ( " Unsaved changes " )
self . setWindowTitle ( " Unsaved changes " )
self . setText ( " The document has been modified. " )
self . setText ( " The document has been modified. " )
self . setInformativeText ( " Do you want to save your changes? " )
self . setInformativeText ( " Do you want to save your changes? " )
self . setStandardButtons ( QMessageBox . Save | QMessageBox . Discard | QMessageBox . Cancel )
self . setStandardButtons (
QMessageBox . Save | QMessageBox . Discard | QMessageBox . Cancel )
self . setDefaultButton ( QMessageBox . Save )
self . setDefaultButton ( QMessageBox . Save )
class UnreadableMessageBox ( QMessageBox ) :
class UnreadableMessageBox ( QMessageBox ) :
"""
"""
Message box to warn the user that the chosen file cannot be opened .
Message box to warn the user that the chosen file cannot be opened .
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
@ -640,10 +712,12 @@ class UnreadableMessageBox(QMessageBox):
self . setStandardButtons ( QMessageBox . Ok )
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
class NameWarningMessageBox ( QMessageBox ) :
class NameWarningMessageBox ( QMessageBox ) :
"""
"""
Message box to warn the user that a chord must have a name
Message box to warn the user that a chord must have a name
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
@ -654,31 +728,38 @@ class NameWarningMessageBox(QMessageBox):
self . setStandardButtons ( QMessageBox . Ok )
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
class VoicingWarningMessageBox ( QMessageBox ) :
class VoicingWarningMessageBox ( QMessageBox ) :
"""
"""
Message box to warn the user that the voicing entered could not be parsed
Message box to warn the user that the voicing entered could not be parsed
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . setIcon ( QMessageBox . Warning )
self . setIcon ( QMessageBox . Warning )
self . setWindowTitle ( " Malformed voicing " )
self . setWindowTitle ( " Malformed voicing " )
self . setText ( " The voicing you entered was not understood and has not been applied. " )
self . setInformativeText ( " Please try re-entering it in the correct format. " )
self . setText (
" The voicing you entered was not understood and has not been applied. " )
self . setInformativeText (
" Please try re-entering it in the correct format. " )
self . setStandardButtons ( QMessageBox . Ok )
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
class LengthWarningMessageBox ( QMessageBox ) :
class LengthWarningMessageBox ( QMessageBox ) :
"""
"""
Message box to warn the user that a block must have a length
Message box to warn the user that a block must have a length
"""
"""
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . setIcon ( QMessageBox . Warning )
self . setIcon ( QMessageBox . Warning )
self . setWindowTitle ( " Block without valid length " )
self . setWindowTitle ( " Block without valid length " )
self . setText ( " Blocks must have a whole number length. " )
self . setText ( " Blocks must have a whole number length. " )
self . setInformativeText ( " Please enter a valid length for your block and try again. " )
self . setInformativeText (
" Please enter a valid length for your block and try again. " )
self . setStandardButtons ( QMessageBox . Ok )
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
@ -689,7 +770,9 @@ if __name__ == '__main__':
d = Document ( )
d = Document ( )
s = Style ( )
s = Style ( )
w = DocumentWindow ( d , s , filename = ( sys . argv [ 1 ] if len ( sys . argv ) > 1 else None ) ) # pass first argument as filename
# pass first argument as filename
w = DocumentWindow ( d , s , filename = (
sys . argv [ 1 ] if len ( sys . argv ) > 1 else None ) )
w . show ( )
w . show ( )
sys . exit ( app . exec_ ( ) )
sys . exit ( app . exec_ ( ) )