@ -6,14 +6,18 @@ Created on Wed May 29 00:02:24 2019
@author : ivan
"""
import sys , fitz , io , subprocess , os
import sys
import fitz
import io
import subprocess
import os
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.QtCore import QFile , QObject , Qt , pyqtSlot , QSettings
from PyQt5.QtGui import QPixmap , QImage , QKeySequence
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.pagesizes import A4 , A5 , LETTER , LEGAL
@ -32,24 +36,30 @@ if getattr(sys, 'frozen', False):
else :
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 . setOrganizationDomain ( " ivanholmes.co.uk " )
QApplication . setApplicationName ( " Chordsheet " )
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 " :
pdfmetrics . registerFont ( TTFont ( ' HelveticaNeue ' , ' HelveticaNeue.ttc ' , subfontIndex = 0 ) )
pdfmetrics . registerFont (
TTFont ( ' HelveticaNeue ' , ' HelveticaNeue.ttc ' , subfontIndex = 0 ) )
# dictionaries for combo boxes
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.
pageSizeDict = { ' A4 ' : A4 , ' A5 ' : A5 , ' Letter ' : LETTER , ' Legal ' : LEGAL }
# 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 for the main window of the application .
"""
def __init__ ( self , doc , style , filename = None ) :
"""
Initialisation function for the main window of the application .
@ -66,7 +76,7 @@ class DocumentWindow(QMainWindow):
self . lastDoc = copy ( self . doc )
self . currentFilePath = filename
self . UIFileLoader ( str ( os . path . join ( scriptDir , ' ui ' , ' mainwindow.ui ' ) ) )
self . UIFileLoader ( str ( os . path . join ( scriptDir , ' ui ' , ' mainwindow.ui ' ) ) )
self . UIInitStyle ( )
self . updateChordDict ( )
@ -77,7 +87,7 @@ class DocumentWindow(QMainWindow):
if filename :
try :
self . openFile ( filename )
except :
except Exception :
UnreadableMessageBox ( ) . exec ( )
def closeEvent ( self , event ) :
@ -103,7 +113,8 @@ class DocumentWindow(QMainWindow):
self . window . actionOpen . triggered . connect ( self . menuFileOpenAction )
self . window . actionSave . triggered . connect ( self . menuFileSaveAction )
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 . actionClose . triggered . connect ( self . menuFileCloseAction )
self . window . actionUndo . triggered . connect ( self . menuEditUndoAction )
@ -125,14 +136,18 @@ class DocumentWindow(QMainWindow):
self . window . actionCopy . setShortcut ( QKeySequence . Copy )
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 . guitarVoicingButton . clicked . connect ( self . guitarVoicingAction )
self . window . guitarVoicingButton . clicked . connect (
self . guitarVoicingAction )
self . window . addChordButton . clicked . connect ( self . addChordAction )
self . window . removeChordButton . clicked . connect ( self . removeChordAction )
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.
"""
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 . setCurrentText ( list ( unitDict . keys ( ) ) [ 0 ] )
self . window . documentUnitsComboBox . setCurrentText (
list ( unitDict . keys ( ) ) [ 0 ] )
self . window . lineSpacingDoubleSpinBox . setValue ( self . style . lineSpacing )
@ -195,15 +212,20 @@ class DocumentWindow(QMainWindow):
def chordClickedAction ( self , index ) :
# 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 ) :
# set the controls to the values from the selected block
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 ) :
"""
@ -218,14 +240,17 @@ class DocumentWindow(QMainWindow):
return settings . setValue ( value , os . path . dirname ( fullpath ) )
def menuFileNewAction ( self ) :
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)
self . doc = Document ( ) # new document object
# 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 . updatePreview ( )
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 :
self . openFile ( filePath )
@ -243,14 +268,16 @@ class DocumentWindow(QMainWindow):
def menuFileSaveAction ( self ) :
self . updateDocument ( )
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 :
filePath = self . currentFilePath
self . saveFile ( filePath )
def menuFileSaveAsAction ( self ) :
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 :
self . saveFile ( filePath )
@ -267,7 +294,8 @@ class DocumentWindow(QMainWindow):
def menuFileSavePDFAction ( self ) :
self . updateDocument ( )
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 :
savePDF ( d , s , filePath )
self . setPath ( " lastExportPath " , filePath )
@ -284,36 +312,36 @@ class DocumentWindow(QMainWindow):
self . saveWarning ( )
def menuFileAboutAction ( self ) :
aDialog = AboutDialog ( )
AboutDialog ( )
def menuEditUndoAction ( self ) :
try :
QApplication . focusWidget ( ) . undo ( ) # see if the built in widget supports it
except :
pass # if not just fail silently
except Exception :
pass # if not just fail silently
def menuEditRedoAction ( self ) :
try :
QApplication . focusWidget ( ) . redo ( )
except :
except Exception :
pass
def menuEditCutAction ( self ) :
try :
QApplication . focusWidget ( ) . cut ( )
except :
except Exception :
pass
def menuEditCopyAction ( self ) :
try :
QApplication . focusWidget ( ) . copy ( )
except :
except Exception :
pass
def menuEditPasteAction ( self ) :
try :
QApplication . focusWidget ( ) . paste ( )
except :
except Exception :
pass
def saveWarning ( self ) :
@ -322,14 +350,15 @@ class DocumentWindow(QMainWindow):
"""
self . updateDocument ( ) # update the document to catch all changes
if ( self . lastDoc == self . doc ) :
if self . lastDoc == self . doc :
self . close ( )
else :
wantToSave = UnsavedMessageBox ( ) . exec ( )
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 . doc . saveXML ( self . currentFilePath )
self . close ( )
@ -348,20 +377,21 @@ class DocumentWindow(QMainWindow):
def clearChordLineEdits ( self ) :
self . window . chordNameLineEdit . 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 ( )
def updateChordDict ( self ) :
"""
Updates the dictionary used to generate the Chord menu ( on the block tab )
"""
self . chordDict = { ' None ' : None }
self . chordDict . update ( { c . name : c for c in self . doc . chordList } )
self . chordDict = { ' None ' : None }
self . chordDict . update ( { c . name : c for c in self . doc . chordList } )
self . window . blockChordComboBox . clear ( )
self . window . blockChordComboBox . addItems ( list ( self . chordDict . keys ( ) ) )
def removeChordAction ( self ) :
if self . window . chordTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
if self . window . chordTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
self . updateChords ( )
row = self . window . chordTableView . selectionModel ( ) . currentIndex ( ) . row ( )
@ -380,12 +410,13 @@ class DocumentWindow(QMainWindow):
self . doc . chordList . append ( Chord ( cName ) )
if self . window . guitarVoicingLineEdit . text ( ) :
try :
self . doc . chordList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering ( self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
success = True # chord successfully parsed
except :
self . doc . chordList [ - 1 ] . voicings [ ' guitar ' ] = parseFingering (
self . window . guitarVoicingLineEdit . text ( ) , ' guitar ' )
success = True # chord successfully parsed
except Exception :
VoicingWarningMessageBox ( ) . exec ( ) # Voicing is malformed, warn user
else :
success = True # chord successfully parsed
success = True # chord successfully parsed
else :
NameWarningMessageBox ( ) . exec ( ) # Chord has no name, warn user
@ -396,7 +427,7 @@ class DocumentWindow(QMainWindow):
def updateChordAction ( self ) :
success = False # see comments above
if self . window . chordTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
if self . window . chordTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
self . updateChords ( )
row = self . window . chordTableView . selectionModel ( ) . currentIndex ( ) . row ( )
cName = parseName ( self . window . chordNameLineEdit . text ( ) )
@ -404,9 +435,10 @@ class DocumentWindow(QMainWindow):
self . doc . chordList [ row ] = Chord ( cName )
if self . window . guitarVoicingLineEdit . text ( ) :
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
except :
except Exception :
VoicingWarningMessageBox ( ) . exec ( )
else :
success = True
@ -421,11 +453,12 @@ class DocumentWindow(QMainWindow):
def clearBlockLineEdits ( self ) :
self . window . blockLengthLineEdit . 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 ( )
def removeBlockAction ( self ) :
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
self . updateBlocks ( )
row = self . window . blockTableView . selectionModel ( ) . currentIndex ( ) . row ( )
@ -437,33 +470,37 @@ class DocumentWindow(QMainWindow):
self . updateBlocks ( )
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
if bLength : # create the block
self . doc . blockList . append ( Block ( bLength ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText ( ) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
chord = self . chordDict [ self . window . blockChordComboBox . currentText (
) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
self . window . blockTableView . populate ( self . doc . blockList )
self . clearBlockLineEdits ( )
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 ) :
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
if self . window . blockTableView . selectionModel ( ) . hasSelection ( ) : # check for selection
self . updateBlocks ( )
try :
bLength = int ( self . window . blockLengthLineEdit . text ( ) )
except :
except Exception :
bLength = False
row = self . window . blockTableView . selectionModel ( ) . currentIndex ( ) . row ( )
if bLength :
self . doc . blockList [ row ] = ( Block ( bLength ,
chord = self . chordDict [ self . window . blockChordComboBox . currentText ( ) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
chord = self . chordDict [ self . window . blockChordComboBox . currentText (
) ] ,
notes = ( self . window . blockNotesLineEdit . text ( ) if not " " else None ) ) )
self . window . blockTableView . populate ( self . doc . blockList )
self . clearBlockLineEdits ( )
else :
@ -482,23 +519,28 @@ class DocumentWindow(QMainWindow):
savePDF ( self . doc , self . style , self . currentPreview )
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
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)
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 ) :
"""
Update the application ' s title bar to reflect the current document.
"""
if self . currentFilePath :
self . setWindowTitle ( _version . appName + " – " + os . path . basename ( self . currentFilePath ) )
self . setWindowTitle ( _version . appName + " – " +
os . path . basename ( self . currentFilePath ) )
else :
self . setWindowTitle ( _version . appName )
@ -508,9 +550,11 @@ class DocumentWindow(QMainWindow):
"""
chordTableList = [ ]
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 ( ) :
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
@ -520,10 +564,14 @@ class DocumentWindow(QMainWindow):
"""
blockTableList = [ ]
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
@ -531,41 +579,57 @@ class DocumentWindow(QMainWindow):
"""
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 . 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!
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 ) :
self . style . unitWidth = float ( self . window . beatWidthLineEdit . text ( ) )
self . style . unitWidth = float (
self . window . beatWidthLineEdit . text ( ) )
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 . 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
class GuitarDialog ( QDialog ) :
"""
Dialogue to allow the user to enter a guitar chord voicing . Not particularly advanced at present !
May be extended in future .
"""
def __init__ ( self ) :
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 ) :
ui_file = QFile ( ui_file )
@ -590,16 +654,19 @@ class GuitarDialog(QDialog):
else :
return None
class AboutDialog ( QDialog ) :
"""
Dialogue showing information about the program .
"""
def __init__ ( self ) :
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 ' ) ) )
self . dialog . iconLabel . setPixmap ( QPixmap . fromImage ( icon ) . scaled ( self . dialog . iconLabel . width ( ) , self . dialog . iconLabel . height ( ) , Qt . KeepAspectRatio , transformMode = Qt . SmoothTransformation ) )
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 . versionLabel . setText ( " Version " + _version . version )
@ -612,10 +679,12 @@ class AboutDialog(QDialog):
self . dialog = uic . loadUi ( ui_file )
ui_file . close ( )
class UnsavedMessageBox ( QMessageBox ) :
"""
Message box to alert the user of unsaved changes and allow them to choose how to act .
"""
def __init__ ( self ) :
super ( ) . __init__ ( )
@ -623,13 +692,16 @@ class UnsavedMessageBox(QMessageBox):
self . setWindowTitle ( " Unsaved changes " )
self . setText ( " The document has been modified. " )
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 )
class UnreadableMessageBox ( QMessageBox ) :
"""
Message box to warn the user that the chosen file cannot be opened .
"""
def __init__ ( self ) :
super ( ) . __init__ ( )
@ -640,10 +712,12 @@ class UnreadableMessageBox(QMessageBox):
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
class NameWarningMessageBox ( QMessageBox ) :
"""
Message box to warn the user that a chord must have a name
"""
def __init__ ( self ) :
super ( ) . __init__ ( )
@ -654,31 +728,38 @@ class NameWarningMessageBox(QMessageBox):
self . setStandardButtons ( QMessageBox . Ok )
self . setDefaultButton ( QMessageBox . Ok )
class VoicingWarningMessageBox ( QMessageBox ) :
"""
Message box to warn the user that the voicing entered could not be parsed
"""
def __init__ ( self ) :
super ( ) . __init__ ( )
self . setIcon ( QMessageBox . Warning )
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 . setDefaultButton ( QMessageBox . Ok )
class LengthWarningMessageBox ( QMessageBox ) :
"""
Message box to warn the user that a block must have a length
"""
def __init__ ( self ) :
super ( ) . __init__ ( )
self . setIcon ( QMessageBox . Warning )
self . setWindowTitle ( " Block without valid 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 . setDefaultButton ( QMessageBox . Ok )
@ -689,7 +770,9 @@ if __name__ == '__main__':
d = Document ( )
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 ( )
sys . exit ( app . exec_ ( ) )