Browse Source

Vastly improve commenting

master 0.3.1
Ivan Holmes 5 years ago
parent
commit
17e14e6eeb
  1. 1
      _version.py
  2. 17
      chordsheet/document.py
  3. 9
      chordsheet/primitives.py
  4. 21
      chordsheet/tableView.py
  5. 114
      gui.py

1
_version.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
appName = "Chordsheet"
version = '0.4'

17
chordsheet/document.py

@ -9,6 +9,7 @@ defaultTimeSignature = 4
class Style:
def __init__(self, **kwargs):
# set up the style using sane defaults
self.unit = kwargs.get('unit', mm)
self.pageSize = kwargs.get('pageSize', A4)
@ -18,7 +19,7 @@ class Style:
self.lineSpacing = kwargs.get('lineSpacing', 1.15)
self.separatorSize = kwargs.get('separatorSize', 5)
self.useIncludedFont = False
self.useIncludedFont = True
self.stringHzSp = 20*self.unit
self.stringHzGap = 2*self.unit
@ -72,6 +73,9 @@ class Document:
return NotImplemented
def loadXML(self, filepath):
"""
Read an XML file and import its contents.
"""
xmlDoc = ET.parse(filepath)
root = xmlDoc.getroot()
@ -105,11 +109,17 @@ class Document:
self.timeSignature = (int(root.find('timesignature').text) if root.find('timesignature') is not None else defaultTimeSignature)
def newFromXML(filepath):
"""
Create a new Document object directly from an XML file.
"""
doc = Document()
doc.loadXML(filepath)
return doc
def saveXML(self, filepath):
"""
Write the contents of the Document object to an XML file.
"""
root = ET.Element("chordsheet")
ET.SubElement(root, "title").text = self.title
@ -144,7 +154,4 @@ class Document:
ET.SubElement(blockElement, "notes").text = b.notes
tree = ET.ElementTree(root)
tree.write(filepath)
tree.write(filepath)

9
chordsheet/primitives.py

@ -5,6 +5,9 @@ from reportlab.lib.units import mm
from reportlab.graphics.shapes import *
def writeText(currentCanvas, style, string, size, vpos, **kwargs):
"""
Wrapper function to conveniently write text according to my requirements...
"""
margin = style.leftMargin*style.unit
align = kwargs.get('align', 'centre')
@ -28,11 +31,17 @@ def writeText(currentCanvas, style, string, size, vpos, **kwargs):
return size*style.lineSpacing
def drawHorizLine(currentCanvas, startpoint, endpoint, v_pos, h_origin, v_origin):
"""
Draw a horizontal line on the canvas taking origin point into account.
"""
x1 = h_origin+startpoint
x2 = h_origin+endpoint
currentCanvas.line(x1, v_pos, x2, v_pos)
def drawVertLine(currentCanvas, startpoint, endpoint, h_pos, h_origin, v_origin):
"""
Draw a vertical line on the canvas taking origin point into account.
"""
y1 = v_origin+startpoint
y2 = v_origin+endpoint
currentCanvas.line(h_pos, y1, h_pos, y2)

21
chordsheet/tableView.py

@ -14,8 +14,7 @@ class MProxyStyle(QtWidgets.QProxyStyle):
def drawPrimitive(self, element, option, painter, widget=None):
"""
Draw a line across the entire row rather than just the column
we're hovering over. This may not always work depending on global
style.
we're hovering over.
"""
if element == self.PE_IndicatorItemViewItemDrop and not option.rect.isNull():
option_new = QtWidgets.QStyleOption(option)
@ -26,7 +25,9 @@ class MProxyStyle(QtWidgets.QProxyStyle):
super().drawPrimitive(element, option, painter, widget)
class MTableView(QtWidgets.QTableView):
"""
Subclass the built in TableView to customise it.
"""
def __init__(self, parent):
super().__init__(parent)
@ -46,13 +47,18 @@ class MTableView(QtWidgets.QTableView):
self.setStyle(MProxyStyle())
class ChordTableView(MTableView):
"""
Subclass MTableView to add properties just for the chord table.
"""
def __init__(self, parent):
super().__init__(parent)
self.model.setHorizontalHeaderLabels(['Chord', 'Voicing'])
def populate(self, cList):
"""
Fill the table from a list of Chord objects.
"""
self.model.removeRows(0, self.model.rowCount())
for c in cList:
rowList = [QtGui.QStandardItem(c.name), QtGui.QStandardItem(",".join(c.voicings['guitar'] if 'guitar' in c.voicings.keys() else ""))]
@ -64,13 +70,18 @@ class ChordTableView(MTableView):
class BlockTableView(MTableView):
"""
Subclass MTableView to add properties just for the block table.
"""
def __init__(self, parent):
super().__init__(parent)
self.model.setHorizontalHeaderLabels(['Chord', 'Length', 'Notes'])
def populate(self, bList):
"""
Fill the table from a list of Block objects.
"""
self.model.removeRows(0, self.model.rowCount())
for b in bList:
rowList = [QtGui.QStandardItem((b.chord.name if b.chord else "")), QtGui.QStandardItem(str(b.length)), QtGui.QStandardItem(b.notes)]

114
gui.py

@ -24,7 +24,7 @@ from chordsheet.document import Document, Style, Chord, Block
from chordsheet.render import savePDF
from chordsheet.parsers import parseFingering, parseName
from _version import version
import _version
# set the directory where our files are depending on whether we're running a pyinstaller binary or not
if getattr(sys, 'frozen', False):
@ -191,10 +191,12 @@ class DocumentWindow(QMainWindow):
self.style.useIncludedFont = False
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())
def blockClickedAction(self, index):
# set the controls to the values from the selected block
self.window.blockChordComboBox.setCurrentText(self.window.blockTableView.model.item(index.row(), 0).text())
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())
@ -212,18 +214,21 @@ class DocumentWindow(QMainWindow):
return settings.setValue(value, os.path.dirname(fullpath))
def menuFileNewAction(self):
self.doc = Document()
self.lastDoc = copy(self.doc)
self.currentFilePath = None
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.UIInitDocument()
self.updatePreview()
def menuFileOpenAction(self):
filePath = QFileDialog.getOpenFileName(self.window.tabWidget, 'Open file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")
if filePath[0]:
self.openFile(filePath[0])
filePath = QFileDialog.getOpenFileName(self.window.tabWidget, 'Open file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
if filePath:
self.openFile(filePath)
def openFile(self, filePath):
"""
Opens a file from a file path and sets up the window accordingly.
"""
self.currentFilePath = filePath
self.doc.loadXML(self.currentFilePath)
self.lastDoc = copy(self.doc)
@ -233,30 +238,35 @@ class DocumentWindow(QMainWindow):
def menuFileSaveAction(self):
self.updateDocument()
if not (hasattr(self, 'currentFilePath') and self.currentFilePath):
filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")
self.currentFilePath = filePath[0]
self.doc.saveXML(self.currentFilePath)
self.lastDoc = copy(self.doc)
self.setPath("workingPath", self.currentFilePath)
if not self.currentFilePath:
filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
else:
filePath = self.currentFilePath
saveFile(filePath)
def menuFileSaveAsAction(self):
self.updateDocument()
filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")
if filePath[0]:
self.currentFilePath = filePath[0]
self.doc.saveXML(self.currentFilePath)
self.lastDoc = copy(self.doc)
self.setPath("workingPath", self.currentFilePath)
filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
if filePath:
saveFile(filePath)
self.updateTitleBar() # as we now have a new filename
def saveFile(self, filePath):
"""
Saves a file to given file path and sets up environment.
"""
self.currentFilePath = filePath
self.doc.saveXML(self.currentFilePath)
self.lastDoc = copy(self.doc)
self.setPath("workingPath", self.currentFilePath)
def menuFileSavePDFAction(self):
self.updateDocument()
self.updatePreview()
filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath("lastExportPath"), "PDF files (*.pdf)")
if filePath[0]:
savePDF(d, s, filePath[0])
self.setPath("lastExportPath", filePath[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)
def menuFilePrintAction(self):
if sys.platform == "darwin":
@ -274,9 +284,9 @@ class DocumentWindow(QMainWindow):
def menuEditUndoAction(self):
try:
QApplication.focusWidget().undo()
QApplication.focusWidget().undo() # see if the built in widget supports it
except:
pass
pass # if not just fail silently
def menuEditRedoAction(self):
try:
@ -335,9 +345,12 @@ class DocumentWindow(QMainWindow):
self.window.chordNameLineEdit.clear()
self.window.guitarVoicingLineEdit.clear()
self.window.chordNameLineEdit.repaint() # necessary on Mojave with PyInstaller (or previous contents will be shown)
self.window.guitarVoicingLineEdit.clear()
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.window.blockChordComboBox.clear()
@ -354,7 +367,7 @@ class DocumentWindow(QMainWindow):
self.updateChordDict()
def addChordAction(self):
success = False
success = False # initialise
self.updateChords()
cName = parseName(self.window.chordNameLineEdit.text())
@ -363,22 +376,22 @@ class DocumentWindow(QMainWindow):
if self.window.guitarVoicingLineEdit.text():
try:
self.doc.chordList[-1].voicings['guitar'] = parseFingering(self.window.guitarVoicingLineEdit.text(), 'guitar')
success = True
success = True # chord successfully parsed
except:
VoicingWarningMessageBox().exec()
VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
else:
success = True
success = True # chord successfully parsed
else:
NameWarningMessageBox().exec()
NameWarningMessageBox().exec() # Chord has no name, warn user
if success == True:
if success == True: # if chord was parsed properly
self.window.chordTableView.populate(self.doc.chordList)
self.clearChordLineEdits()
self.updateChordDict()
def updateChordAction(self):
success = False
if self.window.chordTableView.selectionModel().hasSelection():
success = False # see comments above
if self.window.chordTableView.selectionModel().hasSelection(): # check for selection
self.updateChords()
row = self.window.chordTableView.selectionModel().currentIndex().row()
cName = parseName(self.window.chordNameLineEdit.text())
@ -418,21 +431,21 @@ class DocumentWindow(QMainWindow):
self.updateBlocks()
try:
bLength = int(self.window.blockLengthLineEdit.text())
bLength = int(self.window.blockLengthLineEdit.text()) # can the value entered for block length be cast as an integer
except:
bLength = False
if bLength:
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)))
self.window.blockTableView.populate(self.doc.blockList)
self.clearBlockLineEdits()
else:
LengthWarningMessageBox().exec()
LengthWarningMessageBox().exec() # show warning that length was not entered or in wrong format
def updateBlockAction(self):
if self.window.blockTableView.selectionModel().hasSelection():
if self.window.blockTableView.selectionModel().hasSelection(): # check for selection
self.updateBlocks()
try:
@ -455,6 +468,9 @@ class DocumentWindow(QMainWindow):
self.updatePreview()
def updatePreview(self):
"""
Update the preview shown by rendering a new PDF and drawing it to the scroll area.
"""
self.currentPreview = io.BytesIO()
savePDF(self.doc, self.style, self.currentPreview)
@ -469,13 +485,18 @@ class DocumentWindow(QMainWindow):
self.window.imageLabel.repaint() # necessary on Mojave with PyInstaller (or previous contents will be shown)
def updateTitleBar(self):
appName = "Chordsheet"
"""
Update the application's title bar to reflect the current document.
"""
if self.currentFilePath:
self.setWindowTitle(appName + " – " + os.path.basename(self.currentFilePath))
self.setWindowTitle(_version.appName + " – " + os.path.basename(self.currentFilePath))
else:
self.setWindowTitle(appName)
self.setWindowTitle(_version.appName)
def updateChords(self):
"""
Update the chord list by reading the table.
"""
chordTableList = []
for i in range(self.window.chordTableView.model.rowCount()):
chordTableList.append(Chord(parseName(self.window.chordTableView.model.item(i, 0).text()))),
@ -485,6 +506,9 @@ class DocumentWindow(QMainWindow):
self.doc.chordList = chordTableList
def updateBlocks(self):
"""
Update the block list by reading the table.
"""
blockTableList = []
for i in range(self.window.blockTableView.model.rowCount()):
blockLength = int(self.window.blockTableView.model.item(i, 1).text())
@ -495,6 +519,9 @@ class DocumentWindow(QMainWindow):
self.doc.blockList = blockTableList
def updateDocument(self):
"""
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.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)
@ -529,6 +556,9 @@ class GuitarDialog(QDialog):
ui_file.close()
def getVoicing(self):
"""
Show the dialogue and return the voicing that has been entered.
"""
if self.dialog.exec_() == QDialog.Accepted:
result = [self.dialog.ELineEdit.text(),
self.dialog.ALineEdit.text(),
@ -552,7 +582,7 @@ class AboutDialog(QDialog):
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)
self.dialog.versionLabel.setText("Version " + _version.version)
self.dialog.exec()

Loading…
Cancel
Save