diff --git a/chordsheet/document.py b/chordsheet/document.py
index f76bb77..e7632ea 100644
--- a/chordsheet/document.py
+++ b/chordsheet/document.py
@@ -7,7 +7,6 @@ from reportlab.lib.pagesizes import A4
defaultTimeSignature = 4
-
class Style:
def __init__(self, **kwargs):
# set up the style using sane defaults
@@ -143,17 +142,8 @@ class Document:
'timesignature') is not None else defaultTimeSignature)
self.tempo = (root.find('tempo').text if root.find(
'tempo') is not None else None)
-
- @classmethod
- def newFromXML(cls, filepath):
- """
- Create a new Document object directly from an XML file.
- """
- doc = cls()
- doc.loadXML(filepath)
- return doc
-
- def saveXML(self, filepath):
+
+ def toXML(self):
"""
Write the contents of the Document object to an XML file.
"""
@@ -200,7 +190,16 @@ class Document:
ET.SubElement(blockElement, "notes").text = b.notes
tree = ET.ElementTree(root)
+ return tree
+
+ def saveXML(self, filepath):
+ tree = self.toXML()
tree.write(filepath)
+ return hash(ET.tostring(tree.getroot()))
+
+ def getHash(self):
+ tree = self.toXML()
+ return hash(ET.tostring(tree.getroot()))
def loadCSMacro(self, filepath):
"""
@@ -289,4 +288,4 @@ class Document:
# Simply ignore comments
pass
else:
- raise ValueError(f"Command {cmd} not understood.")
\ No newline at end of file
+ raise ValueError(f"Command {cmd} not understood.")
diff --git a/chordsheet/parsers.py b/chordsheet/parsers.py
index 06d5067..0759dc9 100644
--- a/chordsheet/parsers.py
+++ b/chordsheet/parsers.py
@@ -7,14 +7,13 @@ def parseFingering(fingering, instrument):
"""
if instrument == 'guitar':
numStrings = 6
- if len(fingering) == numStrings: # if the fingering is entered in concise format e.g. xx4455
+ if "," not in fingering and len(fingering) == numStrings: # if the fingering is entered in concise format e.g. xx4455
output = list(fingering)
else: # if entered in long format e.g. x,x,10,10,11,11
output = [f.strip() for f in fingering.split(",")]
- if len(output) == numStrings:
- return output
- else:
- raise Exception("Voicing <{}> is malformed.".format(fingering))
+ if len(output) != numStrings:
+ print("Voicing <{}> is malformed.".format(fingering))
+ return output
elif instrument == 'piano':
return [parseName(note).upper().strip() for note in fingering.split(",")]
else:
@@ -31,4 +30,4 @@ def parseName(chordName):
parsedName = chordName
for i, j in nameReplacements.items():
parsedName = parsedName.replace(i, j)
- return parsedName
\ No newline at end of file
+ return parsedName
diff --git a/chordsheet/render.py b/chordsheet/render.py
index 8fc3620..1adee2b 100644
--- a/chordsheet/render.py
+++ b/chordsheet/render.py
@@ -12,6 +12,7 @@ from reportlab.platypus import BaseDocTemplate, Spacer, Paragraph, Flowable, Fra
from chordsheet.document import Block
from chordsheet.rlStylesheet import getStyleSheet
+defaultSpacing = 1.15
def writeText(canvas, style, string, size, vpos, width, **kwargs):
"""
@@ -122,7 +123,7 @@ class GuitarChart(Flowable):
for i in range(self.nStrings+1): # i is the string line currently being drawn
writeText(canvas, self.style, ['e', 'B', 'G', 'D', 'A', 'E', 'Name'][i], fontsize, v_origin-(
- i*self.stringHeight), self.width, hpos=chartmargin, align='right')
+ i*self.stringHeight), self.width, hpos=chartmargin, align='right', spacing=defaultSpacing)
# j is which chord (0 is first chord, 1 is 2nd etc)
for j in range(len(stringList[-1])):
@@ -152,7 +153,7 @@ class GuitarChart(Flowable):
canvas.line(x, y, x+l, y)
writeText(canvas, self.style, stringList[i][j], fontsize, v_origin-(
- i*self.stringHeight), self.width, hpos=chartmargin+self.stringHzSp*(j+0.5))
+ i*self.stringHeight), self.width, hpos=chartmargin+self.stringHzSp*(j+0.5), spacing=defaultSpacing)
lastWidth = currentWidth
@@ -458,7 +459,7 @@ class ChordProgression(Flowable):
if u == maxWidth: # Avoid writing beat number after the final line
break
writeText(canvas, self.style, str((u % self.timeSignature)+1), self.style.beatsFontSize,
- v_origin+self.beatsHeight, self.width, hpos=x+unitWidth/2)
+ v_origin+self.beatsHeight, self.width, hpos=x+unitWidth/2, spacing=defaultSpacing)
parsedBlockList = self.wrapBlocks(self.blockList, maxWidth)
@@ -470,12 +471,12 @@ class ChordProgression(Flowable):
b.length*unitWidth, self.unitHeight)
if b.notes is not None:
writeText(canvas, self.style, b.notes, self.style.notesFontSize, v_origin-((v_loc+1)*self.unitHeight)+(
- 1.3*self.style.notesFontSize), self.width, hpos=h_offset+((h_loc+b.length/2)*unitWidth))
+ 1.3*self.style.notesFontSize), self.width, hpos=h_offset+((h_loc+b.length/2)*unitWidth), spacing=defaultSpacing)
v_offset = ((v_loc*self.unitHeight) +
self.unitHeight/2)-self.style.chordNameFontSize/2
if b.chord is not None:
writeText(canvas, self.style, b.chord.name, self.style.chordNameFontSize,
- v_origin-v_offset, self.width, hpos=h_offset+((h_loc+b.length/2)*unitWidth))
+ v_origin-v_offset, self.width, hpos=h_offset+((h_loc+b.length/2)*unitWidth), spacing=defaultSpacing)
h_loc += b.length
diff --git a/cli.py b/cli.py
new file mode 100755
index 0000000..e90eada
--- /dev/null
+++ b/cli.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import sys
+import os
+import time
+
+from reportlab.lib.units import mm, cm, inch, pica
+from reportlab.lib.pagesizes import A4, A5, LETTER, LEGAL
+from reportlab.pdfbase import pdfmetrics
+from reportlab.pdfbase.ttfonts import TTFont
+
+from chordsheet.common import scriptDir
+from chordsheet.document import Document, Style, Chord, Block, Section
+from chordsheet.render import Renderer
+from chordsheet.parsers import parseFingering, parseName
+
+import _version
+
+pdfmetrics.registerFont(
+ TTFont('FreeSans', os.path.join(scriptDir, 'fonts', 'FreeSans.ttf')))
+if sys.platform == "darwin":
+ pdfmetrics.registerFont(
+ TTFont('HelveticaNeue', 'HelveticaNeue.ttc', subfontIndex=0))
+
+if len(sys.argv) == 2:
+ inputFilePath = sys.argv[1]
+else:
+ print("Please provide a .cml, .xml, or .cma file to process.")
+ sys.exit()
+
+doc = Document()
+if inputFilePath[-4:] == ".cma":
+ doc.loadCSMacro(inputFilePath)
+else:
+ doc.loadXML(inputFilePath)
+
+style = Style()
+
+renderer = Renderer(doc, style)
+
+outputFilePath = ".".join(inputFilePath.split(".")[:-1]) + ".pdf"
+renderer.savePDF(outputFilePath)
diff --git a/chordsheet/comboBox.py b/csgui/comboBox.py
similarity index 100%
rename from chordsheet/comboBox.py
rename to csgui/comboBox.py
diff --git a/chordsheet/dialogs.py b/csgui/dialogs.py
similarity index 69%
rename from chordsheet/dialogs.py
rename to csgui/dialogs.py
index dcc03ed..3a0551a 100644
--- a/chordsheet/dialogs.py
+++ b/csgui/dialogs.py
@@ -25,17 +25,31 @@ class GuitarDialog(QDialog):
self.dialog = uic.loadUi(ui_file)
ui_file.close()
- def getVoicing(self):
+ def getVoicing(self, existingVoicing):
"""
Show the dialogue and return the voicing that has been entered.
"""
+ lineEditsList = [
+ self.dialog.ELineEdit,
+ self.dialog.ALineEdit,
+ self.dialog.DLineEdit,
+ self.dialog.GLineEdit,
+ self.dialog.BLineEdit,
+ self.dialog.eLineEdit
+ ]
+
+ # Read the present voicing
+ if type(existingVoicing) == list:
+ for count in range(len(existingVoicing)):
+ lineEditsList[count].setText(existingVoicing[count])
+
if self.dialog.exec_() == QDialog.Accepted:
- result = [self.dialog.ELineEdit.text(),
- self.dialog.ALineEdit.text(),
- self.dialog.DLineEdit.text(),
- self.dialog.GLineEdit.text(),
- self.dialog.BLineEdit.text(),
- self.dialog.eLineEdit.text()]
+ result = [self.dialog.ELineEdit.text() or 'x',
+ self.dialog.ALineEdit.text() or 'x',
+ self.dialog.DLineEdit.text() or 'x',
+ self.dialog.GLineEdit.text() or 'x',
+ self.dialog.BLineEdit.text() or 'x',
+ self.dialog.eLineEdit.text() or 'x']
resultJoined = ",".join(result)
return resultJoined
else:
@@ -64,4 +78,4 @@ class AboutDialog(QDialog):
ui_file.open(QFile.ReadOnly)
self.dialog = uic.loadUi(ui_file)
- ui_file.close()
\ No newline at end of file
+ ui_file.close()
diff --git a/chordsheet/messageBox.py b/csgui/messageBox.py
similarity index 94%
rename from chordsheet/messageBox.py
rename to csgui/messageBox.py
index 87a620e..8bd65a0 100644
--- a/chordsheet/messageBox.py
+++ b/csgui/messageBox.py
@@ -5,12 +5,12 @@ class UnsavedMessageBox(QMessageBox):
Message box to alert the user of unsaved changes and allow them to choose how to act.
"""
- def __init__(self):
+ def __init__(self, fileName):
super().__init__()
self.setIcon(QMessageBox.Question)
self.setWindowTitle("Unsaved changes")
- self.setText("The document has been modified.")
+ self.setText(f"The document \"{fileName}\" has been modified.")
self.setInformativeText("Do you want to save your changes?")
self.setStandardButtons(
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
@@ -22,11 +22,11 @@ class UnreadableMessageBox(QMessageBox):
Message box to warn the user that the chosen file cannot be opened.
"""
- def __init__(self):
+ def __init__(self, fileName):
super().__init__()
self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("File cannot be opened")
+ self.setWindowTitle(f"File \"{fileName}\" cannot be opened")
self.setText("The file you have selected cannot be opened.")
self.setInformativeText("Please make sure it is in the right format.")
self.setStandardButtons(QMessageBox.Ok)
diff --git a/chordsheet/panels.py b/csgui/panels.py
similarity index 80%
rename from chordsheet/panels.py
rename to csgui/panels.py
index 41f0427..4ffea6e 100644
--- a/chordsheet/panels.py
+++ b/csgui/panels.py
@@ -1,10 +1,11 @@
import os
-from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut, QDockWidget
+from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut, QDockWidget, QLineEdit, QTableView
from PyQt5.QtCore import QFile, QObject, Qt
from PyQt5.QtGui import QImage, QPixmap
from PyQt5 import uic
from chordsheet.common import scriptDir
+from csgui.tableView import MTableView
class UIFileDockWidget(QDockWidget):
def __init__(self):
@@ -17,6 +18,15 @@ class UIFileDockWidget(QDockWidget):
self.setWidget(uic.loadUi(ui_file))
ui_file.close()
+ def clear(self):
+ # Clear all the fields
+ for lineEdit in self.findChildren(QLineEdit):
+ lineEdit.clear()
+ for comboBox in self.findChildren(QComboBox):
+ comboBox.clear()
+ for tableView in self.findChildren(MTableView):
+ tableView.clear()
+
class DocInfoDockWidget(UIFileDockWidget):
def __init__(self):
super().__init__()
@@ -51,4 +61,4 @@ class PreviewDockWidget(UIFileDockWidget):
def __init__(self):
super().__init__()
self.UIFileLoader('preview.ui')
- self.setWindowTitle("Preview")
\ No newline at end of file
+ self.setWindowTitle("Preview")
diff --git a/chordsheet/pdfViewer.py b/csgui/pdfViewer.py
similarity index 72%
rename from chordsheet/pdfViewer.py
rename to csgui/pdfViewer.py
index 0db75df..9f30c31 100644
--- a/chordsheet/pdfViewer.py
+++ b/csgui/pdfViewer.py
@@ -14,8 +14,20 @@ class PDFLabel(QLabel):
self.adjustSize()
if self.pixmap() is not None:
painter = QPainter(self)
- painter.setRenderHint(QPainter.Antialiasing)
- idealWidth = self.parent.width()-45
+ # painter.setRenderHint(QPainter.Antialiasing)
+
+ parentLayoutMargins = self.parent.scrollAreaLayout.getContentsMargins()
+ parentMargins = self.parent.getContentsMargins()
+
+ if self.parent.verticalScrollBar().isVisible():
+ scrollBarWidth = self.parent.verticalScrollBar().sizeHint().width()
+ else:
+ scrollBarWidth = 0
+
+ totalMargin = parentLayoutMargins[0] + parentLayoutMargins[2] + \
+ parentMargins[0]*2 + scrollBarWidth
+
+ idealWidth = self.parent.width() - totalMargin
pixSize = self.pixmap().size()
pixSize.scale(idealWidth, 1000000, Qt.KeepAspectRatio)
@@ -32,6 +44,7 @@ class PDFViewer(QScrollArea):
self.setWidget(self.scrollAreaContents)
self.setWidgetResizable(True)
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scrollAreaContents.setLayout(self.scrollAreaLayout)
self.pixmapList = []
@@ -48,9 +61,8 @@ class PDFViewer(QScrollArea):
self.pixmapList = []
pdfView = fitz.Document(stream=pdf, filetype='pdf')
- # render at 8x resolution and scale
for page in pdfView:
- self.pixmapList.append(page.getPixmap(matrix=fitz.Matrix(8, 8), alpha=False))
+ self.pixmapList.append(page.get_pixmap(alpha=False))
def clear(self):
while self.scrollAreaLayout.count():
@@ -64,11 +76,7 @@ class PDFViewer(QScrollArea):
label = PDFLabel(parent=self)
label.setAlignment(Qt.AlignHCenter)
qtimg = QImage(p.samples, p.width, p.height, p.stride, QImage.Format_RGB888)
- # -45 because of various margins... value obtained by trial and error.
label.setPixmap(QPixmap.fromImage(qtimg))
self.scrollAreaLayout.addWidget(label)
- self.scrollAreaLayout.addStretch(1)
-
- # necessary on Mojave with PyInstaller (or previous contents will be shown)
- self.repaint()
\ No newline at end of file
+ # self.scrollAreaLayout.addStretch(1)
diff --git a/chordsheet/tableView.py b/csgui/tableView.py
similarity index 83%
rename from chordsheet/tableView.py
rename to csgui/tableView.py
index 09e34f7..7a9a7fd 100644
--- a/chordsheet/tableView.py
+++ b/csgui/tableView.py
@@ -1,16 +1,35 @@
from PyQt5 import QtWidgets, QtGui
-
+from PyQt5.QtCore import pyqtSignal, Qt, QModelIndex
class MItemModel(QtGui.QStandardItemModel):
"""
Special item model to ensure whole row is moved.
"""
+ itemsDropped = pyqtSignal()
+
+ def __init__(self):
+ super().__init__()
+ self.pendingRemoveRows = False
def dropMimeData(self, data, action, row, col, parent):
"""
Always move the entire row, and don't allow column "shifting"
"""
- return super().dropMimeData(data, action, row, 0, parent)
+ ret = super().dropMimeData(data, Qt.MoveAction, row, 0, parent)
+ if ret:
+ self.pendingRemoveRows = True
+ return ret
+
+ def removeRows(self, row, count, index=QModelIndex()):
+ """
+ Emit a signal after rows have been moved
+ """
+ ret = super().removeRows(row, count, index)
+ if self.pendingRemoveRows:
+ self.itemsDropped.emit()
+ self.pendingRemoveRows = False
+
+ return ret
class MProxyStyle(QtWidgets.QProxyStyle):
@@ -50,11 +69,13 @@ class MTableView(QtWidgets.QTableView):
self.setShowGrid(False)
# self.setDragDropMode(self.InternalMove)
- # self.setDragDropOverwriteMode(False)
+ self.setDragDropOverwriteMode(False)
# Set our custom style - this draws the drop indicator across the whole row
self.setStyle(MProxyStyle())
-
+
+ def clear(self):
+ self.model.removeRows(0, self.model.rowCount())
class ChordTableView(MTableView):
"""
diff --git a/gui.py b/gui.py
index baa08c7..92972e8 100755
--- a/gui.py
+++ b/gui.py
@@ -13,14 +13,19 @@ import subprocess
import os
import time
from copy import copy
+from xml.etree.ElementTree import ParseError
from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QFontComboBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut, QMdiSubWindow
-from PyQt5.QtCore import QFile, QObject, Qt, pyqtSlot, QSettings
+from PyQt5.QtCore import QFile, QObject, Qt, pyqtSlot, pyqtSignal, QSettings
from PyQt5.QtGui import QPixmap, QImage, QKeySequence, QFontInfo, QFont, QRawFont
from PyQt5 import uic
-from chordsheet.tableView import ChordTableView, BlockTableView
-from chordsheet.comboBox import MComboBox
-from chordsheet.pdfViewer import PDFViewer
+
+from csgui.tableView import ChordTableView, BlockTableView
+from csgui.comboBox import MComboBox
+from csgui.pdfViewer import PDFViewer
+from csgui.messageBox import UnsavedMessageBox, UnreadableMessageBox, ChordNameWarningMessageBox, SectionNameWarningMessageBox, BlockMustHaveSectionWarningMessageBox, VoicingWarningMessageBox, LengthWarningMessageBox
+from csgui.dialogs import GuitarDialog, AboutDialog
+from csgui.panels import DocInfoDockWidget, PageSetupDockWidget, ChordsDockWidget, SectionsDockWidget, BlocksDockWidget, PreviewDockWidget
from reportlab.lib.units import mm, cm, inch, pica
from reportlab.lib.pagesizes import A4, A5, LETTER, LEGAL
@@ -31,9 +36,7 @@ from chordsheet.common import scriptDir
from chordsheet.document import Document, Style, Chord, Block, Section
from chordsheet.render import Renderer
from chordsheet.parsers import parseFingering, parseName
-from chordsheet.messageBox import UnsavedMessageBox, UnreadableMessageBox, ChordNameWarningMessageBox, SectionNameWarningMessageBox, BlockMustHaveSectionWarningMessageBox, VoicingWarningMessageBox, LengthWarningMessageBox
-from chordsheet.dialogs import GuitarDialog, AboutDialog
-from chordsheet.panels import DocInfoDockWidget, PageSetupDockWidget, ChordsDockWidget, SectionsDockWidget, BlocksDockWidget, PreviewDockWidget
+
import _version
@@ -55,6 +58,17 @@ 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}
+def getPath(value):
+ """
+ Wrapper for Qt settings to return home directory if no setting exists.
+ """
+ return str((settings.value(value) if settings.value(value) else os.path.expanduser("~")))
+
+def setPath(value, fullpath):
+ """
+ Wrapper for Qt settings to set path to open/save from next time from current file location.
+ """
+ return settings.setValue(value, os.path.dirname(fullpath))
class MainWindow(QMainWindow):
"""
@@ -71,104 +85,90 @@ class MainWindow(QMainWindow):
"""
super().__init__()
- self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'new.ui')))
+ self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'mainwindow.ui')))
self.setCentralWidget(self.window.centralWidget)
self.setMenuBar(self.window.menuBar)
self.setWindowTitle("Chordsheet")
-
+
self.lastSubWindow = None
- self.currentSection = None
+
+ self.window.mdiArea.setActivationOrder(self.window.mdiArea.ActivationHistoryOrder)
if filename:
- try:
- self.openFile(filename)
- except Exception:
- UnreadableMessageBox().exec()
+ dw = DocumentWindow.openFile(filename)
+ if dw:
+ self.window.mdiArea.addSubWindow(dw)
+ # self.UIInitDocument(dw.doc)
+ # self.UIInitStyle(dw.style)
+ # self.currentSection = None
+
+ dw.show()
def closeEvent(self, event):
"""
Reimplement the built in closeEvent to allow asking the user to save.
"""
if not self.window.mdiArea.subWindowList():
- self.close()
-
- def UIFileLoader(self, ui_file):
- """
- Loads the .ui file for this window and connects the UI elements to their actions.
- """
- ui_file = QFile(ui_file)
- ui_file.open(QFile.ReadOnly)
+ event.accept()
+ else:
+ for subWindow in self.window.mdiArea.subWindowList():
+ if not subWindow.close():
+ event.ignore()
+ return
+ event.accept()
- self.window = uic.loadUi(ui_file)
- ui_file.close()
-
+ def UISetupPanels(self):
self.docinfo = DocInfoDockWidget()
self.psetup = PageSetupDockWidget()
self.chordsw = ChordsDockWidget()
self.sectionsw = SectionsDockWidget()
self.blocksw = BlocksDockWidget()
self.previeww = PreviewDockWidget()
-
+
+ self.panels_list = [self.docinfo, self.psetup, self.chordsw, self.sectionsw,
+ self.blocksw, self.previeww]
+
self.addDockWidget(Qt.LeftDockWidgetArea, self.docinfo)
self.addDockWidget(Qt.LeftDockWidgetArea, self.psetup)
self.addDockWidget(Qt.LeftDockWidgetArea, self.previeww)
self.addDockWidget(Qt.RightDockWidgetArea, self.chordsw)
self.addDockWidget(Qt.RightDockWidgetArea, self.sectionsw)
self.addDockWidget(Qt.RightDockWidgetArea, self.blocksw)
-
- # Check all the boxes in the window menu
- self.window.actionDocument_information.setChecked(True)
- self.window.actionPage_setup.setChecked(True)
- self.window.actionChords.setChecked(True)
- self.window.actionSections.setChecked(True)
- self.window.actionBlocks.setChecked(True)
- self.window.actionPreview.setChecked(True)
- # link all the UI elements
- self.window.mdiArea.subWindowActivated.connect(self.switchDocument)
+ self.previeww.hide()
- self.window.actionAbout.triggered.connect(self.menuFileAboutAction)
- self.window.actionNew.triggered.connect(self.menuFileNewAction)
- 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.actionPrint.triggered.connect(self.menuFilePrintAction)
- self.window.actionClose.triggered.connect(self.menuFileCloseAction)
- self.window.actionUndo.triggered.connect(self.menuEditUndoAction)
- self.window.actionRedo.triggered.connect(self.menuEditRedoAction)
- self.window.actionCut.triggered.connect(self.menuEditCutAction)
- self.window.actionCopy.triggered.connect(self.menuEditCopyAction)
- self.window.actionPaste.triggered.connect(self.menuEditPasteAction)
+ # connect the change signals to handlers
+ self.docinfo.widget().titleLineEdit.editingFinished.connect(
+ self.updateTitle)
+ self.docinfo.widget().subtitleLineEdit.editingFinished.connect(
+ self.updateSubtitle)
+ self.docinfo.widget().composerLineEdit.editingFinished.connect(
+ self.updateComposer)
+ self.docinfo.widget().arrangerLineEdit.editingFinished.connect(
+ self.updateArranger)
+ self.docinfo.widget().timeSignatureSpinBox.valueChanged.connect(
+ self.updateTimeSignature)
+ self.docinfo.widget().tempoLineEdit.editingFinished.connect(
+ self.updateTempo)
+
+ self.psetup.widget().pageSizeComboBox.textActivated.connect(
+ self.updatePageSize)
+ self.psetup.widget().documentUnitsComboBox.textActivated.connect(
+ self.updateUnit)
+ self.psetup.widget().leftMarginLineEdit.editingFinished.connect(
+ self.updateLeftMargin)
+ self.psetup.widget().rightMarginLineEdit.editingFinished.connect(
+ self.updateRightMargin)
+ self.psetup.widget().topMarginLineEdit.editingFinished.connect(
+ self.updateTopMargin)
+ self.psetup.widget().bottomMarginLineEdit.editingFinished.connect(
+ self.updateBottomMargin)
+ self.psetup.widget().lineSpacingDoubleSpinBox.valueChanged.connect(
+ self.updateLineSpacing)
+ self.psetup.widget().beatWidthLineEdit.editingFinished.connect(
+ self.updateBeatWidth)
- self.window.actionNew.setShortcut(QKeySequence.New)
- self.window.actionOpen.setShortcut(QKeySequence.Open)
- self.window.actionSave.setShortcut(QKeySequence.Save)
- self.window.actionSave_as.setShortcut(QKeySequence.SaveAs)
- self.window.actionSave_PDF.setShortcut(QKeySequence("Ctrl+E"))
- self.window.actionPrint.setShortcut(QKeySequence.Print)
- self.window.actionClose.setShortcut(QKeySequence.Close)
- self.window.actionUndo.setShortcut(QKeySequence.Undo)
- self.window.actionRedo.setShortcut(QKeySequence.Redo)
- self.window.actionCut.setShortcut(QKeySequence.Cut)
- self.window.actionCopy.setShortcut(QKeySequence.Copy)
- self.window.actionPaste.setShortcut(QKeySequence.Paste)
-
- self.window.actionDocument_information.triggered.connect(self.menuWindowDocInfoAction)
- self.window.actionPage_setup.triggered.connect(self.menuWindowPageSetupAction)
- self.window.actionChords.triggered.connect(self.menuWindowChordsAction)
- self.window.actionSections.triggered.connect(self.menuWindowSectionsAction)
- self.window.actionBlocks.triggered.connect(self.menuWindowBlocksAction)
- self.window.actionPreview.triggered.connect(self.menuWindowPreviewAction)
-
-
- self.psetup.widget().pageSizeComboBox.currentIndexChanged.connect(
- self.pageSizeAction)
- self.psetup.widget().documentUnitsComboBox.currentIndexChanged.connect(
- self.unitAction)
-
self.psetup.widget().pageSizeComboBox.addItems(list(pageSizeDict.keys()))
self.psetup.widget().pageSizeComboBox.setCurrentText(
list(pageSizeDict.keys())[0])
@@ -176,11 +176,12 @@ class MainWindow(QMainWindow):
self.psetup.widget().documentUnitsComboBox.addItems(list(unitDict.keys()))
self.psetup.widget().documentUnitsComboBox.setCurrentText(
list(unitDict.keys())[0])
-
+
self.psetup.widget().fontComboBox.currentFontChanged.connect(self.fontChangeAction)
self.psetup.widget().includedFontCheckBox.stateChanged.connect(
self.includedFontAction)
-
+
+ self.previeww.widget().autoUpdatePreviewCheckBox.stateChanged.connect(self.autoUpdateEnabledAction)
self.previeww.widget().updatePreviewButton.clicked.connect(self.generateAction)
# update whole document when any tab is selected
@@ -191,6 +192,7 @@ class MainWindow(QMainWindow):
self.chordsw.widget().addChordButton.clicked.connect(self.addChordAction)
self.chordsw.widget().removeChordButton.clicked.connect(self.removeChordAction)
self.chordsw.widget().updateChordButton.clicked.connect(self.updateChordAction)
+ self.chordsw.widget().chordTableView.model.itemsDropped.connect(self.updateChords)
# connecting clicked only works for this combo box because it's my own modified version (MComboBox)
self.blocksw.widget().blockSectionComboBox.clicked.connect(
@@ -200,26 +202,131 @@ class MainWindow(QMainWindow):
self.blocksw.widget().addBlockButton.clicked.connect(self.addBlockAction)
self.blocksw.widget().removeBlockButton.clicked.connect(self.removeBlockAction)
self.blocksw.widget().updateBlockButton.clicked.connect(self.updateBlockAction)
+ self.blocksw.widget().blockTableView.model.itemsDropped.connect(self.updateBlocksCurrentSection)
self.sectionsw.widget().addSectionButton.clicked.connect(self.addSectionAction)
self.sectionsw.widget().removeSectionButton.clicked.connect(
self.removeSectionAction)
self.sectionsw.widget().updateSectionButton.clicked.connect(
self.updateSectionAction)
+ self.sectionsw.widget().sectionTableView.model.itemsDropped.connect(self.updateSections)
self.chordsw.widget().chordTableView.clicked.connect(self.chordClickedAction)
self.sectionsw.widget().sectionTableView.clicked.connect(self.sectionClickedAction)
self.blocksw.widget().blockTableView.clicked.connect(self.blockClickedAction)
+ self.disablePanels()
+
+ def unfloatPanels(self):
+ for panel in self.panels_list:
+ panel.setFloating(False)
+
+ def disablePanels(self):
+ for panel in self.panels_list:
+ panel.setEnabled(False)
+ panel.clear()
+
+ def enablePanels(self):
+ for panel in self.panels_list:
+ panel.setEnabled(True)
+
+ def UIFileLoader(self, ui_file):
+ """
+ Loads the .ui file for this window and connects the UI elements to their actions.
+ """
+ ui_file = QFile(ui_file)
+ ui_file.open(QFile.ReadOnly)
+
+ self.window = uic.loadUi(ui_file)
+ ui_file.close()
+
+ self.csw = self.window.mdiArea.currentSubWindow
+
+ self.UISetupPanels()
+ self.disableMenuItems()
+
+ # link all the UI elements
+ self.window.mdiArea.subWindowActivated.connect(self.switchDocument)
+
+ self.window.actionAbout.triggered.connect(self.menuHelpAboutAction)
+
+ self.window.actionNew.triggered.connect(self.menuFileNewAction)
+ 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.actionPrint.triggered.connect(self.menuFilePrintAction)
+ self.window.actionClose.triggered.connect(self.menuFileCloseAction)
+ self.window.actionQuit.triggered.connect(self.menuFileQuitAction)
+
+ self.window.actionUndo.triggered.connect(self.menuEditUndoAction)
+ self.window.actionRedo.triggered.connect(self.menuEditRedoAction)
+ self.window.actionCut.triggered.connect(self.menuEditCutAction)
+ self.window.actionCopy.triggered.connect(self.menuEditCopyAction)
+ self.window.actionPaste.triggered.connect(self.menuEditPasteAction)
+
+ self.window.actionNew.setShortcut(QKeySequence.New)
+ self.window.actionOpen.setShortcut(QKeySequence.Open)
+ self.window.actionSave.setShortcut(QKeySequence.Save)
+ self.window.actionSave_as.setShortcut(QKeySequence.SaveAs)
+ self.window.actionSave_PDF.setShortcut(QKeySequence("Ctrl+E"))
+ self.window.actionPrint.setShortcut(QKeySequence.Print)
+ self.window.actionClose.setShortcut(QKeySequence.Close)
+ self.window.actionUndo.setShortcut(QKeySequence.Undo)
+ self.window.actionRedo.setShortcut(QKeySequence.Redo)
+ self.window.actionCut.setShortcut(QKeySequence.Cut)
+ self.window.actionCopy.setShortcut(QKeySequence.Copy)
+ self.window.actionPaste.setShortcut(QKeySequence.Paste)
+
+ self.window.actionCascadeSubWindows.triggered.connect(self.cascadeSubWindowsAction)
+ self.window.actionTileSubWindows.triggered.connect(self.tileSubWindowsAction)
+ self.window.actionActivateNextSubWindow.triggered.connect(self.activatePreviousSubWindowAction)
+ self.window.actionActivatePreviousSubWindow.triggered.connect(self.activateNextSubWindowAction)
+ self.window.actionRedockAllPanels.triggered.connect(self.unfloatPanels)
+ self.window.menuWindow.addSeparator()
+ self.window.menuWindow.addAction(self.docinfo.toggleViewAction())
+ self.window.menuWindow.addAction(self.psetup.toggleViewAction())
+ self.window.menuWindow.addAction(self.chordsw.toggleViewAction())
+ self.window.menuWindow.addAction(self.sectionsw.toggleViewAction())
+ self.window.menuWindow.addAction(self.blocksw.toggleViewAction())
+ self.window.menuWindow.addSeparator()
+ self.window.menuWindow.addAction(self.previeww.toggleViewAction())
+
# Set the tab widget to Overview tab
# self.window.tabWidget.setCurrentIndex(0)
-
- def UIInitDocument(self, doc):
+
+ def enableMenuItems(self):
+ self.window.actionSave.setEnabled(True)
+ self.window.actionSave_as.setEnabled(True)
+ self.window.actionSave_PDF.setEnabled(True)
+ # Comment this because we can't print
+ # self.window.actionPrint.setEnabled(True)
+ self.window.actionClose.setEnabled(True)
+ self.window.actionUndo.setEnabled(True)
+ self.window.actionRedo.setEnabled(True)
+ self.window.actionCut.setEnabled(True)
+ self.window.actionCopy.setEnabled(True)
+ self.window.actionPaste.setEnabled(True)
+
+ def disableMenuItems(self):
+ self.window.actionSave.setEnabled(False)
+ self.window.actionSave_as.setEnabled(False)
+ self.window.actionSave_PDF.setEnabled(False)
+ self.window.actionPrint.setEnabled(False)
+ self.window.actionClose.setEnabled(False)
+ self.window.actionUndo.setEnabled(False)
+ self.window.actionRedo.setEnabled(False)
+ self.window.actionCut.setEnabled(False)
+ self.window.actionCopy.setEnabled(False)
+ self.window.actionPaste.setEnabled(False)
+
+ def UIInitDocument(self):
"""
Fills the window's fields with the values from its document.
"""
- # self.updateTitleBar()
-
+ doc = self.csw().doc
+
# set all fields to appropriate values from document
self.docinfo.widget().titleLineEdit.setText(doc.title)
self.docinfo.widget().subtitleLineEdit.setText(doc.subtitle)
@@ -231,21 +338,27 @@ class MainWindow(QMainWindow):
self.chordsw.widget().chordTableView.populate(doc.chordList)
self.sectionsw.widget().sectionTableView.populate(doc.sectionList)
- self.updateChordDict(doc)
- self.updateSectionDict(doc)
+ self.csw().updateChordDict()
+ self.csw().updateSectionDict()
+
+ self.updateBlockChordComboBox()
+ self.updateBlockSectionComboBox()
+
# populate the block table with the first section, account for a document with no sections
- if self.currentSection is None:
- self.currentSection = doc.sectionList[0] if doc.sectionList else None
+ if self.csw().currentSection is None:
+ self.csw().currentSection = doc.sectionList[0] if doc.sectionList else None
else:
self.blocksw.widget().blockSectionComboBox.setCurrentText(
- self.currentSection.name)
+ self.csw().currentSection.name)
self.blocksw.widget().blockTableView.populate(
- self.currentSection.blockList if self.currentSection else [])
+ self.csw().currentSection.blockList if self.csw().currentSection else [])
- def UIInitStyle(self, style):
+ def UIInitStyle(self):
"""
Fills the window's fields with the values from its style.
"""
+ style = self.csw().style
+
self.psetup.widget().pageSizeComboBox.setCurrentText(
[k for k, v in pageSizeDict.items() if v==style.pageSize][0])
@@ -259,94 +372,244 @@ class MainWindow(QMainWindow):
self.psetup.widget().topMarginLineEdit.setText(str(style.topMargin))
self.psetup.widget().bottomMarginLineEdit.setText(str(style.bottomMargin))
- # self.psetup.widget().fontComboBox.setDisabled(True)
+ self.psetup.widget().fontComboBox.setDisabled(True)
self.psetup.widget().includedFontCheckBox.setChecked(True)
self.psetup.widget().beatWidthLineEdit.setText(str(style.unitWidth))
+
+ def fontChangeAction(self):
+# if self.csw() is not None:
+ qFont = self.psetup.widget().fontComboBox.currentFont()
+ qFontReal = QRawFont.fromFont(qFont)
+ #fontInfo = QFontInfo(qFont)
+ #qFontReal = QRawFont(fontInfo.family())
+ print(qFont.rawName())
- def updateChordDict(self, doc):
+ def autoUpdateEnabledAction(self, state):
+ self.previeww.widget().updatePreviewButton.setEnabled(not state)
+
+ def switchDocument(self, curWindow):
+ if curWindow is not None:
+ self.enablePanels()
+ self.enableMenuItems()
+
+ self.UIInitDocument()
+ self.UIInitStyle()
+ if self.csw().currentSection is not None:
+ self.blocksw.widget().blockSectionComboBox.setCurrentText(
+ self.csw().currentSection.name)
+
+ self.lastSubWindow = curWindow
+ else:
+ self.disablePanels()
+ self.disableMenuItems()
+
+ def updateBlockChordComboBox(self):
+ self.blocksw.widget().blockChordComboBox.clear()
+ self.blocksw.widget().blockChordComboBox.addItems(list(self.csw().chordDict.keys()))
+
+ def updateBlockSectionComboBox(self):
+ self.blocksw.widget().blockSectionComboBox.clear()
+ self.blocksw.widget().blockSectionComboBox.addItems(list(self.csw().sectionDict.keys()))
+
+ def updateTitle(self):
+ self.csw().doc.title = self.docinfo.widget().titleLineEdit.text()
+ # Title can be empty string but not None
+ self.autoUpdatePreview()
+
+ def updateSubtitle(self):
+ self.csw().doc.subtitle = (self.docinfo.widget().subtitleLineEdit.text() \
+ if self.docinfo.widget().subtitleLineEdit.text() else None)
+ self.autoUpdatePreview()
+
+ def updateComposer(self):
+ self.csw().doc.composer = (self.docinfo.widget().composerLineEdit.text() \
+ if self.docinfo.widget().composerLineEdit.text() else None)
+ self.autoUpdatePreview()
+
+ def updateArranger(self):
+ self.csw().doc.arranger = (self.docinfo.widget().arrangerLineEdit.text() \
+ if self.docinfo.widget().arrangerLineEdit.text() else None)
+ self.autoUpdatePreview()
+
+ def updateTempo(self):
+ self.csw().doc.tempo = (self.docinfo.widget().tempoLineEdit.text() \
+ if self.docinfo.widget().tempoLineEdit.text() else None)
+ self.autoUpdatePreview()
+
+ def updateTimeSignature(self):
+ if self.csw():
+ self.csw().doc.timeSignature = (int(self.docinfo.widget().timeSignatureSpinBox.value()) if self.docinfo.widget().timeSignatureSpinBox.value() else self.doc.timeSignature)
+ self.autoUpdatePreview()
+
+ def updatePageSize(self, index):
+ if self.csw():
+ self.csw().style.pageSize = pageSizeDict[self.psetup.widget().pageSizeComboBox.currentText()]
+ self.autoUpdatePreview()
+
+ def updateUnit(self, index):
+ self.csw().style.unit = unitDict[self.psetup.widget().documentUnitsComboBox.currentText()]
+ self.autoUpdatePreview()
+
+ def updateLeftMargin(self):
+ self.csw().style.leftMargin = float(self.psetup.widget().leftMarginLineEdit.text(
+ )) if self.psetup.widget().leftMarginLineEdit.text() else self.csw().style.leftMargin
+ self.autoUpdatePreview()
+
+ def updateRightMargin(self):
+ self.csw().style.rightMargin = float(self.psetup.widget().rightMarginLineEdit.text(
+ )) if self.psetup.widget().rightMarginLineEdit.text() else self.csw().style.rightMargin
+ self.autoUpdatePreview()
+
+ def updateTopMargin(self):
+ self.csw().style.topMargin = float(self.psetup.widget().topMarginLineEdit.text(
+ )) if self.psetup.widget().topMarginLineEdit.text() else self.csw().style.topMargin
+ self.autoUpdatePreview()
+
+ def updateBottomMargin(self):
+ self.csw().style.bottomMargin = float(self.psetup.widget().bottomMarginLineEdit.text(
+ )) if self.psetup.widget().bottomMarginLineEdit.text() else self.csw().style.bottomMargin
+ self.autoUpdatePreview()
+
+ def updateLineSpacing(self):
+ if self.csw():
+ self.csw().style.lineSpacing = float(self.psetup.widget().lineSpacingDoubleSpinBox.value()) if self.psetup.widget().lineSpacingDoubleSpinBox.value() else self.csw().style.lineSpacing
+ self.autoUpdatePreview()
+
+ def updateBeatWidth(self):
+ if self.psetup.widget().beatWidthLineEdit.text():
+ if (self.csw().style.pageSize[0] - 2 * self.csw().style.leftMargin * mm) >= (float(self.psetup.widget().beatWidthLineEdit.text()) * 2 * self.csw().doc.timeSignature * mm):
+ self.csw().style.unitWidth = float(
+ self.psetup.widget().beatWidthLineEdit.text())
+ else:
+ maxBeatWidth = (
+ self.csw().style.pageSize[0] - 2 * self.csw().style.leftMargin * mm) / (2 * self.csw().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.autoUpdatePreview()
+
+ def updateChords(self):
"""
- Updates the dictionary used to generate the Chord menu (on the block tab)
+ Update the chord list by reading the table.
"""
- self.chordDict = {'None': None}
- self.chordDict.update({c.name: c for c in doc.chordList})
- self.blocksw.widget().blockChordComboBox.clear()
- self.blocksw.widget().blockChordComboBox.addItems(list(self.chordDict.keys()))
+ chordTableList = []
+ for i in range(self.chordsw.widget().chordTableView.model.rowCount()):
+ chordTableList.append(
+ Chord(parseName(self.chordsw.widget().chordTableView.model.item(i, 0).text()))),
+ if self.chordsw.widget().chordTableView.model.item(i, 1).text():
+ chordTableList[-1].voicings['guitar'] = parseFingering(
+ self.chordsw.widget().chordTableView.model.item(i, 1).text(), 'guitar')
+ if self.chordsw.widget().chordTableView.model.item(i, 2).text():
+ chordTableList[-1].voicings['piano'] = parseFingering(
+ self.chordsw.widget().chordTableView.model.item(i, 2).text(), 'piano')
- def updateSectionDict(self, doc):
+ if self.csw():
+ self.csw().doc.chordList = chordTableList
+ self.autoUpdatePreview()
+
+ def updateSections(self):
"""
- Updates the dictionary used to generate the Section menu (on the block tab)
+ Update the section list by reading the table
"""
- self.sectionDict = {s.name: s for s in doc.sectionList}
- self.blocksw.widget().blockSectionComboBox.clear()
- self.blocksw.widget().blockSectionComboBox.addItems(
- list(self.sectionDict.keys()))
+ sectionTableList = []
+ for i in range(self.sectionsw.widget().sectionTableView.model.rowCount()):
+ sectionTableList.append(self.csw().matchSection(
+ self.sectionsw.widget().sectionTableView.model.item(i, 0).text()))
+
+ self.csw().doc.sectionList = sectionTableList
+ self.autoUpdatePreview()
+
+ def updateBlocks(self, section):
+ """
+ Update the block list by reading the table.
+ """
+
+ if section is None:
+ BlockMustHaveSectionWarningMessageBox().exec()
+ else:
+ blockTableList = []
+ for i in range(self.blocksw.widget().blockTableView.model.rowCount()):
+ blockLength = float(
+ self.blocksw.widget().blockTableView.model.item(i, 1).text())
+ blockChord = self.csw().chordDict[(self.blocksw.widget().blockTableView.model.item(
+ i, 0).text() if self.blocksw.widget().blockTableView.model.item(i, 0).text() else "None")]
+ blockNotes = self.blocksw.widget().blockTableView.model.item(i, 2).text(
+ ) if self.blocksw.widget().blockTableView.model.item(i, 2).text() else None
+ blockTableList.append(
+ Block(blockLength, chord=blockChord, notes=blockNotes))
+
+ section.blockList = blockTableList
+ self.autoUpdatePreview()
- def fontChangeAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- qFont = self.psetup.widget().fontComboBox.currentFont()
- qFontReal = QRawFont.fromFont(qFont)
- #fontInfo = QFontInfo(qFont)
- #qFontReal = QRawFont(fontInfo.family())
- print(qFont.rawName())
+ def updateBlocksCurrentSection(self):
+ if self.csw():
+ section = self.csw().currentSection
+ else:
+ section = None
+ self.updateBlocks(section)
- def switchDocument(self, curWindow):
- if curWindow is not None:
- if self.lastSubWindow is not None:
- self.lastSubWindow.currentSection = self.currentSection
-
- self.UIInitDocument(curWindow.doc)
- self.UIInitStyle(curWindow.style)
- self.currentSection = curWindow.currentSection
- if self.currentSection is not None:
- self.blocksw.widget().blockSectionComboBox.setCurrentText(
- self.currentSection.name)
- self.lastSubWindow = curWindow
+ def autoUpdatePreview(self):
+ if self.previeww.widget().autoUpdatePreviewCheckBox.isChecked():
+ self.csw().updatePreview()
+
+ def cascadeSubWindowsAction(self):
+ self.window.mdiArea.cascadeSubWindows()
- def generateAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- self.window.mdiArea.currentSubWindow().updateDocument()
- self.window.mdiArea.currentSubWindow().updatePreview()
+ def tileSubWindowsAction(self):
+ self.window.mdiArea.tileSubWindows()
+
+ def activatePreviousSubWindowAction(self):
+ self.window.mdiArea.activatePreviousSubWindow()
+ def activateNextSubWindowAction(self):
+ self.window.mdiArea.activateNextSubWindow()
+
+ def generateAction(self):
+ # if self.csw() is not None:
+# self.csw().updateDocument()
+ self.csw().updatePreview()
+
def removeChordAction(self):
if self.chordsw.widget().chordTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateChords()
+ self.updateChords()
row = self.chordsw.widget().chordTableView.selectionModel().currentIndex().row()
oldName = self.chordsw.widget().chordTableView.model.item(row, 0).text()
- self.window.mdiArea.currentSubWindow().doc.chordList.pop(row)
+ self.csw().doc.chordList.pop(row)
- self.chordsw.widget().chordTableView.populate(self.window.mdiArea.currentSubWindow().doc.chordList)
+ self.chordsw.widget().chordTableView.populate(self.csw().doc.chordList)
# remove the chord if any of the blocks have it attached
- if self.currentSection is not None:
- for s in self.window.mdiArea.currentSubWindow().doc.sectionList:
+ if self.csw().currentSection is not None:
+ for s in self.csw().doc.sectionList:
for b in s.blockList:
if b.chord:
if b.chord.name == oldName:
b.chord = None
- self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+ self.blocksw.widget().blockTableView.populate(self.csw().currentSection.blockList)
self.clearChordLineEdits()
- self.updateChordDict(self.window.mdiArea.currentSubWindow().doc)
-
+ self.csw().updateChordDict()
+ self.updateBlockChordComboBox()
+ self.autoUpdatePreview()
+
def addChordAction(self):
success = False # initialise
- self.window.mdiArea.currentSubWindow().updateChords()
+ self.updateChords()
cName = parseName(self.chordsw.widget().chordNameLineEdit.text())
if cName:
- self.window.mdiArea.currentSubWindow().doc.chordList.append(Chord(cName))
+ self.csw().doc.chordList.append(Chord(cName))
if self.chordsw.widget().guitarVoicingLineEdit.text() or self.chordsw.widget().pianoVoicingLineEdit.text():
if self.chordsw.widget().guitarVoicingLineEdit.text():
try:
- self.window.mdiArea.currentSubWindow().doc.chordList[-1].voicings['guitar'] = parseFingering(
+ self.csw().doc.chordList[-1].voicings['guitar'] = parseFingering(
self.chordsw.widget().guitarVoicingLineEdit.text(), 'guitar')
success = True # chord successfully parsed
except Exception:
VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
if self.chordsw.widget().pianoVoicingLineEdit.text():
try:
- self.window.mdiArea.currentSubWindow().doc.chordList[-1].voicings['piano'] = parseFingering(
+ self.csw().doc.chordList[-1].voicings['piano'] = parseFingering(
self.chordsw.widget().pianoVoicingLineEdit.text(), 'piano')
success = True # chord successfully parsed
except Exception:
@@ -357,30 +620,32 @@ class MainWindow(QMainWindow):
ChordNameWarningMessageBox().exec() # Chord has no name, warn user
if success == True: # if chord was parsed properly
- self.chordsw.widget().chordTableView.populate(self.window.mdiArea.currentSubWindow().doc.chordList)
+ self.chordsw.widget().chordTableView.populate(self.csw().doc.chordList)
self.clearChordLineEdits()
- self.updateChordDict(self.window.mdiArea.currentSubWindow().doc)
+ self.csw().updateChordDict()
+ self.updateBlockChordComboBox()
+ self.autoUpdatePreview()
def updateChordAction(self):
success = False # see comments above
if self.chordsw.widget().chordTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateChords()
+ self.updateChords()
row = self.chordsw.widget().chordTableView.selectionModel().currentIndex().row()
oldName = self.chordsw.widget().chordTableView.model.item(row, 0).text()
cName = parseName(self.chordsw.widget().chordNameLineEdit.text())
if cName:
- self.window.mdiArea.currentSubWindow().doc.chordList[row].name = cName
+ self.csw().doc.chordList[row].name = cName
if self.chordsw.widget().guitarVoicingLineEdit.text() or self.chordsw.widget().pianoVoicingLineEdit.text():
if self.chordsw.widget().guitarVoicingLineEdit.text():
try:
- self.window.mdiArea.currentSubWindow().doc.chordList[row].voicings['guitar'] = parseFingering(
+ self.csw().doc.chordList[row].voicings['guitar'] = parseFingering(
self.chordsw.widget().guitarVoicingLineEdit.text(), 'guitar')
success = True # chord successfully parsed
except Exception:
VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
if self.chordsw.widget().pianoVoicingLineEdit.text():
try:
- self.window.mdiArea.currentSubWindow().doc.chordList[row].voicings['piano'] = parseFingering(
+ self.csw().doc.chordList[row].voicings['piano'] = parseFingering(
self.chordsw.widget().pianoVoicingLineEdit.text(), 'piano')
success = True # chord successfully parsed
except Exception:
@@ -391,68 +656,77 @@ class MainWindow(QMainWindow):
ChordNameWarningMessageBox().exec()
if success == True:
- self.updateChordDict(self.window.mdiArea.currentSubWindow().doc)
- self.chordsw.widget().chordTableView.populate(self.window.mdiArea.currentSubWindow().doc.chordList)
+ self.csw().updateChordDict()
+ self.updateBlockChordComboBox()
+ self.chordsw.widget().chordTableView.populate(self.csw().doc.chordList)
# update the names of chords in all blocklists in case they've already been used
- for s in self.window.mdiArea.currentSubWindow().doc.sectionList:
+ for s in self.csw().doc.sectionList:
for b in s.blockList:
if b.chord:
if b.chord.name == oldName:
b.chord.name = cName
- if self.currentSection and self.currentSection.blockList:
- self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+ if self.csw().currentSection and self.csw().currentSection.blockList:
+ self.blocksw.widget().blockTableView.populate(self.csw().currentSection.blockList)
self.clearChordLineEdits()
+ self.autoUpdatePreview()
def removeSectionAction(self):
if self.sectionsw.widget().sectionTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateSections()
+ self.updateSections()
row = self.sectionsw.widget().sectionTableView.selectionModel().currentIndex().row()
- self.window.mdiArea.currentSubWindow().doc.sectionList.pop(row)
+ self.csw().doc.sectionList.pop(row)
- self.sectionsw.widget().sectionTableView.populate(self.window.mdiArea.currentSubWindow().doc.sectionList)
+ self.sectionsw.widget().sectionTableView.populate(self.csw().doc.sectionList)
self.clearSectionLineEdits()
- self.updateSectionDict(self.window.mdiArea.currentSubWindow().doc)
+ self.csw().updateSectionDict()
+ self.updateBlockSectionComboBox()
+ self.autoUpdatePreview()
def addSectionAction(self):
- self.window.mdiArea.currentSubWindow().updateSections()
+ self.updateSections()
sName = self.sectionsw.widget().sectionNameLineEdit.text()
- if sName and sName not in [s.name for s in self.window.mdiArea.currentSubWindow().doc.sectionList]:
- self.window.mdiArea.currentSubWindow().doc.sectionList.append(Section(name=sName))
- self.sectionsw.widget().sectionTableView.populate(self.window.mdiArea.currentSubWindow().doc.sectionList)
+ if sName and sName not in [s.name for s in self.csw().doc.sectionList]:
+ self.csw().doc.sectionList.append(Section(name=sName))
+ self.sectionsw.widget().sectionTableView.populate(self.csw().doc.sectionList)
self.clearSectionLineEdits()
- self.updateSectionDict(self.window.mdiArea.currentSubWindow().doc)
+ self.csw().updateSectionDict()
+ self.updateBlockSectionComboBox()
+ self.autoUpdatePreview()
else:
# Section has no name or non unique, warn user
SectionNameWarningMessageBox().exec()
def updateSectionAction(self):
if self.sectionsw.widget().sectionTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateSections()
+ self.updateSections()
row = self.sectionsw.widget().sectionTableView.selectionModel().currentIndex().row()
sName = self.sectionsw.widget().sectionNameLineEdit.text()
- if sName and sName not in [s.name for s in self.window.mdiArea.currentSubWindow().doc.sectionList]:
- self.window.mdiArea.currentSubWindow().doc.sectionList[row].name = sName
- self.sectionsw.widget().sectionTableView.populate(self.window.mdiArea.currentSubWindow().doc.sectionList)
+ if sName and sName not in [s.name for s in self.csw().doc.sectionList]:
+ self.csw().doc.sectionList[row].name = sName
+ self.sectionsw.widget().sectionTableView.populate(self.csw().doc.sectionList)
self.clearSectionLineEdits()
- self.updateSectionDict(self.window.mdiArea.currentSubWindow().doc)
+ self.csw().updateSectionDict()
+ self.updateBlockSectionComboBox()
+ self.autoUpdatePreview()
else:
# Section has no name or non unique, warn user
SectionNameWarningMessageBox().exec()
def removeBlockAction(self):
if self.blocksw.widget().blockTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateBlocks(self.currentSection)
+ self.updateBlocks(self.csw().currentSection)
row = self.blocksw.widget().blockTableView.selectionModel().currentIndex().row()
- self.currentSection.blockList.pop(row)
+ self.csw().currentSection.blockList.pop(row)
- self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+ self.blocksw.widget().blockTableView.populate(self.csw().currentSection.blockList)
+ self.autoUpdatePreview()
def addBlockAction(self):
- self.window.mdiArea.currentSubWindow().updateBlocks(self.currentSection)
+ self.updateBlocks(self.csw().currentSection)
try:
# can the value entered for block length be cast as a float
@@ -461,19 +735,20 @@ class MainWindow(QMainWindow):
bLength = False
if bLength: # create the block
- self.currentSection.blockList.append(Block(bLength,
- chord=self.chordDict[self.blocksw.widget().blockChordComboBox.currentText(
+ self.csw().currentSection.blockList.append(Block(bLength,
+ chord=self.csw().chordDict[self.blocksw.widget().blockChordComboBox.currentText(
)],
notes=(self.blocksw.widget().blockNotesLineEdit.text() if not "" else None)))
- self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+ self.blocksw.widget().blockTableView.populate(self.csw().currentSection.blockList)
self.clearBlockLineEdits()
+ self.autoUpdatePreview()
else:
# show warning that length was not entered or in wrong format
LengthWarningMessageBox().exec()
def updateBlockAction(self):
if self.blocksw.widget().blockTableView.selectionModel().hasSelection(): # check for selection
- self.window.mdiArea.currentSubWindow().updateBlocks(self.currentSection)
+ self.updateBlocks(self.csw().currentSection)
try:
# can the value entered for block length be cast as a float
@@ -483,29 +758,24 @@ class MainWindow(QMainWindow):
row = self.blocksw.widget().blockTableView.selectionModel().currentIndex().row()
if bLength:
- self.currentSection.blockList[row] = (Block(bLength,
- chord=self.chordDict[self.blocksw.widget().blockChordComboBox.currentText(
+ self.csw().currentSection.blockList[row] = (Block(bLength,
+ chord=self.csw().chordDict[self.blocksw.widget().blockChordComboBox.currentText(
)],
notes=(self.blocksw.widget().blockNotesLineEdit.text() if not "" else None)))
self.blocksw.widget().blockTableView.populate(
- self.currentSection.blockList)
+ self.csw().currentSection.blockList)
self.clearBlockLineEdits()
+ self.autoUpdatePreview()
else:
LengthWarningMessageBox().exec()
-
- def pageSizeAction(self, index):
- self.pageSizeSelected = self.psetup.widget().pageSizeComboBox.itemText(index)
-
- def unitAction(self, index):
- self.unitSelected = self.psetup.widget().documentUnitsComboBox.itemText(index)
def includedFontAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- if self.psetup.widget().includedFontCheckBox.isChecked():
- self.window.mdiArea.currentSubWindow().style.useIncludedFont = True
- else:
- self.window.mdiArea.currentSubWindow().style.useIncludedFont = False
-
+ # if self.csw() is not None:
+ if self.psetup.widget().includedFontCheckBox.isChecked():
+ self.csw().style.font = 'FreeSans'
+ else:
+ self.csw().style.font = 'HelveticaNeue'
+
def chordClickedAction(self, index):
# set the controls to the values from the selected chord
self.chordsw.widget().chordNameLineEdit.setText(
@@ -528,16 +798,16 @@ class MainWindow(QMainWindow):
def blockSectionClickedAction(self, text):
if text:
- self.window.mdiArea.currentSubWindow().updateBlocks(self.sectionDict[text])
+ self.updateBlocks(self.csw().sectionDict[text])
def blockSectionChangedAction(self, index):
sName = self.blocksw.widget().blockSectionComboBox.currentText()
if sName:
- if self.window.mdiArea.currentSubWindow() is not None:
- self.currentSection = self.sectionDict.get(sName, None)
- # self.window.mdiArea.currentSubWindow().currentSection = self.currentSection
- if self.currentSection is not None:
- self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+ # if self.csw() is not None:
+ self.csw().currentSection = self.csw().sectionDict.get(sName, None)
+ # self.csw().currentSection = self.currentSection
+ if self.csw().currentSection is not None:
+ self.blocksw.widget().blockTableView.populate(self.csw().currentSection.blockList)
else:
pass # self.currentSection = None
@@ -551,69 +821,43 @@ class MainWindow(QMainWindow):
self.blocksw.widget().blockNotesLineEdit.setText(
self.blocksw.widget().blockTableView.model.item(index.row(), 2).text())
- def getPath(self, value):
- """
- Wrapper for Qt settings to return home directory if no setting exists.
- """
- return str((settings.value(value) if settings.value(value) else os.path.expanduser("~")))
-
- def setPath(self, value, fullpath):
- """
- Wrapper for Qt settings to set path to open/save from next time from current file location.
- """
- return settings.setValue(value, os.path.dirname(fullpath))
-
def menuFileNewAction(self):
dw = DocumentWindow(Document(), Style(), None)
self.window.mdiArea.addSubWindow(dw)
- self.UIInitDocument(dw.doc)
dw.show()
-
def menuFileOpenAction(self):
- filePath = QFileDialog.getOpenFileName(self.window, 'Open file', self.getPath(
- "workingPath"), "Chordsheet Markup Language files (*.xml *.cml);;Chordsheet Macro files (*.cma)")[0]
+ filePath = QFileDialog.getOpenFileName(self.window, 'Open file', getPath(
+ "workingPath"), "Chordsheet Markup Language files (*.cml *.xml);;Chordsheet Macro files (*.cma)")[0]
if filePath:
dw = DocumentWindow.openFile(filePath)
- self.window.mdiArea.addSubWindow(dw)
- self.UIInitDocument(dw.doc)
- self.UIInitStyle(dw.style)
- # self.currentSection = None
-
- dw.show()
+ if dw:
+ self.window.mdiArea.addSubWindow(dw)
+ dw.show()
def menuFileSaveAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- self.window.mdiArea.currentSubWindow().updateDocument()
-
- if self.window.mdiArea.currentSubWindow().currentFilePath:
- fileExt = os.path.splitext(self.window.mdiArea.currentSubWindow().currentFilePath)[1].lower()
- if fileExt != ".cma":
- # Chordsheet Macro files can't be saved at this time
- self.window.mdiArea.currentSubWindow().saveFile(self.window.mdiArea.currentSubWindow().currentFilePath)
+ pathRequired = False
+
+ if self.csw().currentFilePath:
+ fileExt = os.path.splitext(self.csw().currentFilePath)[1].lower()
+ if fileExt != ".cma":
+ # Chordsheet Macro files can't be saved at this time
+ self.csw().saveFile(self.csw().currentFilePath)
else:
- filePath = QFileDialog.getSaveFileName(self.window, 'Save file', self.getPath(
- "workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
- if filePath:
- self.window.mdiArea.currentSubWindow().saveFile(filePath)
+ pathRequired = True
+ else:
+ pathRequired = True
+
+ if pathRequired:
+ self.csw().saveFileChoosePath()
def menuFileSaveAsAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- self.window.mdiArea.currentSubWindow().updateDocument()
- filePath = QFileDialog.getSaveFileName(self.window, 'Save file', self.getPath(
- "workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
- if filePath:
- self.window.mdiArea.currentSubWindow().saveFile(filePath)
+ self.csw().saveFileChoosePath()
def menuFileSavePDFAction(self):
- if self.window.mdiArea.currentSubWindow() is not None:
- self.window.mdiArea.currentSubWindow().updateDocument()
- self.window.mdiArea.currentSubWindow().updatePreview()
- filePath = QFileDialog.getSaveFileName(self.window, 'Save file', self.getPath(
- "lastExportPath"), "PDF files (*.pdf)")[0]
- if filePath:
- self.window.mdiArea.currentSubWindow().renderer.savePDF(filePath)
- self.window.mdiArea.currentSubWindow().setPath("lastExportPath", filePath)
+ filePath = self.csw().savePDFFileChoosePath()
+ if filePath:
+ setPath("lastExportPath", filePath)
def menuFilePrintAction(self):
if sys.platform == "darwin":
@@ -622,11 +866,13 @@ class MainWindow(QMainWindow):
else:
pass
- @pyqtSlot()
def menuFileCloseAction(self):
- self.saveWarning()
+ self.csw().close()
+
+ def menuFileQuitAction(self):
+ self.close()
- def menuFileAboutAction(self):
+ def menuHelpAboutAction(self):
AboutDialog()
def menuEditUndoAction(self):
@@ -660,57 +906,18 @@ class MainWindow(QMainWindow):
pass
- # self.docinfo = DocInfoDockWidget()
- # self.psetup = PageSetupDockWidget()
- # self.chordsw = ChordsDockWidget()
- # self.sectionsw = SectionsDockWidget()
- # self.blocksw = BlocksDockWidget()
- # self.previeww = PreviewDockWidget()
-
- def menuWindowDocInfoAction(self):
- if self.window.actionDocument_information.isChecked():
- self.docinfo.show()
- else:
- self.docinfo.hide()
-
- def menuWindowPageSetupAction(self):
- if self.window.actionPage_setup.isChecked():
- self.psetup.show()
- else:
- self.psetup.hide()
-
- def menuWindowChordsAction(self):
- if self.window.actionChords.isChecked():
- self.chordsw.show()
- else:
- self.chordsw.hide()
-
-
- def menuWindowSectionsAction(self):
- if self.window.actionSections.isChecked():
- self.sectionsw.show()
- else:
- self.sectionsw.hide()
-
-
- def menuWindowBlocksAction(self):
- if self.window.actionBlocks.isChecked():
- self.blocksw.show()
- else:
- self.blocksw.hide()
-
+ def guitarVoicingAction(self):
+ gdialog = GuitarDialog()
- def menuWindowPreviewAction(self):
- if self.window.actionPreview.isChecked():
- self.previeww.show()
+ if self.chordsw.widget().guitarVoicingLineEdit.text():
+ existingVoicing = parseFingering(
+ self.chordsw.widget().guitarVoicingLineEdit.text(), 'guitar'
+ )
else:
- self.previeww.hide()
+ existingVoicing = None
+ voicing = gdialog.getVoicing(existingVoicing)
- def guitarVoicingAction(self):
- gdialog = GuitarDialog()
-
- voicing = gdialog.getVoicing()
if voicing:
self.chordsw.widget().guitarVoicingLineEdit.setText(voicing)
@@ -738,28 +945,27 @@ class MainWindow(QMainWindow):
-
-
-
-
class DocumentWindow(QMdiSubWindow):
+
def __init__(self, doc, style, filename):
super().__init__()
-
+
self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'document.ui')))
-
+ # Set to delete on close
+ self.setAttribute(Qt.WA_DeleteOnClose)
+
self.doc = doc
self.style = style
self.renderer = Renderer(self.doc, self.style)
- self.lastDoc = copy(self.doc)
+ self.lastHash = self.doc.getHash()
self.currentFilePath = filename
-
+
self.currentSection = None
-
- mw.updateChordDict(self.doc)
- mw.updateSectionDict(self.doc)
-
+
+ self.updateChordDict()
+ self.updateSectionDict()
+
def UIFileLoader(self, ui_file):
ui_file = QFile(ui_file)
ui_file.open(QFile.ReadOnly)
@@ -767,77 +973,104 @@ class DocumentWindow(QMdiSubWindow):
self.setWidget(uic.loadUi(ui_file))
ui_file.close()
+ def closeEvent(self, event):
+ if self.saveWarning():
+ event.accept()
+ else:
+ event.ignore()
+
@classmethod
def openFile(cls, filePath):
- dw = cls(Document(), Style(), None)
- dw.loadFile(filePath)
- return dw
-
- def loadFile(self, filePath):
"""
Opens a file from a file path and sets up the window accordingly.
"""
- self.currentFilePath = filePath
-
- fileExt = os.path.splitext(self.currentFilePath)[1].lower()
-
- if fileExt == ".cma":
- self.doc.loadCSMacro(self.currentFilePath)
- else: # if fileExt in [".xml", ".cml"]:
- self.doc.loadXML(self.currentFilePath)
-
- self.updatePreview()
+ dw = cls(Document(), Style(), None)
+ dw.currentFilePath = filePath
+
+ fileExt = os.path.splitext(dw.currentFilePath)[1].lower()
+
+ try:
+ if fileExt == ".cma":
+ dw.doc.loadCSMacro(dw.currentFilePath)
+ else: # if fileExt in [".xml", ".cml"]:
+ dw.doc.loadXML(dw.currentFilePath)
+ except (ParseError, ValueError):
+ UnreadableMessageBox(os.path.basename(dw.currentFilePath)).exec()
+ dw.close()
+ return None
+ else:
+ dw.updatePreview()
+
+ setPath("workingPath", dw.currentFilePath)
+
+ dw.updateChordDict()
+ dw.updateSectionDict()
+
+ dw.currentSection = (dw.doc.sectionList[0] if dw.doc.sectionList else None)
+
+ dw.updateTitleBar()
- self.lastDoc = copy(self.doc)
- mw.setPath("workingPath", self.currentFilePath)
-
- mw.updateChordDict(self.doc)
- mw.updateSectionDict(self.doc)
-
- self.currentSection = (self.doc.sectionList[0] if self.doc.sectionList else None)
-
- self.updateTitleBar()
-
+ dw.lastHash = dw.doc.getHash()
+ return dw
+
def saveFile(self, filePath):
"""
Saves a file to given file path and sets up environment.
"""
self.currentFilePath = filePath
-
+
fileExt = os.path.splitext(self.currentFilePath)[1].lower()
-
+
if fileExt == ".cma":
# At this stage we should never get here
pass
else: # if fileExt in [".xml", ".cml"]:
- self.doc.saveXML(self.currentFilePath)
-
- self.lastDoc = copy(self.doc)
- mw.setPath("workingPath", self.currentFilePath)
+ self.lastHash = self.doc.saveXML(self.currentFilePath)
+
+ setPath("workingPath", self.currentFilePath)
self.updateTitleBar() # as we may have a new filename
+ def saveFileChoosePath(self):
+ filePath, selectedFilter = QFileDialog.getSaveFileName(self.parent(), 'Save file', getPath(
+ "workingPath"), "Chordsheet ML files (*.cml *.xml)", ".cml")
+ if filePath:
+ if filePath.split(".")[-1] not in ["cml", "xml"]:
+ filePath += ".cml"
+ self.saveFile(filePath)
+ return filePath
+ else:
+ return None
+
+ def savePDFFileChoosePath(self):
+ filePath, selectedFilter = QFileDialog.getSaveFileName(self.parent(), 'Save file', getPath(
+ "lastExportPath"), "PDF files (*.pdf)")
+ if filePath:
+ if filePath.split(".")[-1] != "pdf":
+ filePath += ".pdf"
+ self.renderer.savePDF(filePath)
+ return filePath
+ else:
+ return None
+
def saveWarning(self):
"""
Function to check if the document has unsaved data in it and offer to save it.
"""
- self.updateDocument() # update the document to catch all changes
-
- if self.lastDoc == self.doc:
+ if self.lastHash == self.doc.getHash():
return True
else:
- wantToSave = UnsavedMessageBox().exec()
+ wantToSave = UnsavedMessageBox(os.path.basename(self.currentFilePath)).exec()
if wantToSave == QMessageBox.Save:
if not self.currentFilePath:
- filePath = QFileDialog.getSaveFileName(self.window, 'Save file', str(
- os.path.expanduser("~")), "Chordsheet ML files (*.xml *.cml)")
- self.currentFilePath = filePath[0]
- self.doc.saveXML(self.currentFilePath)
+ self.saveFileChoosePath()
+ else:
+ self.saveFile(self.currentFilePath)
return True
elif wantToSave == QMessageBox.Discard:
return True
-
+
else:
return False
@@ -850,42 +1083,21 @@ class DocumentWindow(QMdiSubWindow):
except Exception:
QMessageBox.warning(self, "Preview failed", "Could not update the preview.",
buttons=QMessageBox.Ok, defaultButton=QMessageBox.Ok)
-
-
- with open('preview.pdf', 'wb') as f:
- f.write(self.currentPreview.getbuffer())
self.widget().pdfArea.update_pdf(self.currentPreview)
def updateTitleBar(self):
"""
- Update the application's title bar to reflect the current document.
+ Update the window's title bar to reflect the current document.
"""
if self.currentFilePath:
self.widget().setWindowTitle(os.path.basename(self.currentFilePath))
else:
self.widget().setWindowTitle("Unsaved")
- def updateChords(self):
- """
- Update the chord list by reading the table.
- """
- chordTableList = []
- for i in range(mw.chordsw.widget().chordTableView.model.rowCount()):
- chordTableList.append(
- Chord(parseName(mw.chordsw.widget().chordTableView.model.item(i, 0).text()))),
- if mw.chordsw.widget().chordTableView.model.item(i, 1).text():
- chordTableList[-1].voicings['guitar'] = parseFingering(
- mw.chordsw.widget().chordTableView.model.item(i, 1).text(), 'guitar')
- if mw.chordsw.widget().chordTableView.model.item(i, 2).text():
- chordTableList[-1].voicings['piano'] = parseFingering(
- mw.chordsw.widget().chordTableView.model.item(i, 2).text(), 'piano')
-
- self.doc.chordList = chordTableList
-
def matchSection(self, nameToMatch):
"""
- Given the name of a section, this function checks if it is already present in the document.
+ Given the name of a section, this function checks if it is already present in the document.
If it is, it's returned. If not, a new section with the given name is returned.
"""
section = None
@@ -897,86 +1109,18 @@ class DocumentWindow(QMdiSubWindow):
section = Section(name=nameToMatch)
return section
- def updateSections(self):
+ def updateChordDict(self):
"""
- Update the section list by reading the table
+ Updates the dictionary used to generate the Chord menu (on the block tab)
"""
- sectionTableList = []
- for i in range(mw.sectionsw.widget().sectionTableView.model.rowCount()):
- sectionTableList.append(self.matchSection(
- mw.sectionsw.widget().sectionTableView.model.item(i, 0).text()))
-
- self.doc.sectionList = sectionTableList
+ self.chordDict = {'None': None}
+ self.chordDict.update({c.name: c for c in self.doc.chordList})
- def updateBlocks(self, section):
+ def updateSectionDict(self):
"""
- Update the block list by reading the table.
+ Updates the dictionary used to generate the Section menu (on the block tab)
"""
- if section is None:
- BlockMustHaveSectionWarningMessageBox().exec()
- else:
- blockTableList = []
- for i in range(mw.blocksw.widget().blockTableView.model.rowCount()):
- blockLength = float(
- mw.blocksw.widget().blockTableView.model.item(i, 1).text())
- blockChord = mw.chordDict[(mw.blocksw.widget().blockTableView.model.item(
- i, 0).text() if mw.blocksw.widget().blockTableView.model.item(i, 0).text() else "None")]
- blockNotes = mw.blocksw.widget().blockTableView.model.item(i, 2).text(
- ) if mw.blocksw.widget().blockTableView.model.item(i, 2).text() else None
- blockTableList.append(
- Block(blockLength, chord=blockChord, notes=blockNotes))
-
- section.blockList = blockTableList
- def updateDocument(self):
- """
- Update the Document object by reading values from the UI.
- """
- self.doc.title = mw.docinfo.widget().titleLineEdit.text(
- ) # Title can be empty string but not None
- self.doc.subtitle = (mw.docinfo.widget().subtitleLineEdit.text(
- ) if mw.docinfo.widget().subtitleLineEdit.text() else None)
- self.doc.composer = (mw.docinfo.widget().composerLineEdit.text(
- ) if mw.docinfo.widget().composerLineEdit.text() else None)
- self.doc.arranger = (mw.docinfo.widget().arrangerLineEdit.text(
- ) if mw.docinfo.widget().arrangerLineEdit.text() else None)
- self.doc.tempo = (mw.docinfo.widget().tempoLineEdit.text(
- ) if mw.docinfo.widget().tempoLineEdit.text() else None)
- self.doc.timeSignature = int(mw.docinfo.widget().timeSignatureSpinBox.value(
- )) if mw.docinfo.widget().timeSignatureSpinBox.value() else self.doc.timeSignature
-
- self.style.pageSize = pageSizeDict[mw.pageSizeSelected]
- self.style.unit = unitDict[mw.unitSelected]
- self.style.leftMargin = float(mw.psetup.widget().leftMarginLineEdit.text(
- )) if mw.psetup.widget().leftMarginLineEdit.text() else self.style.leftMargin
- self.style.rightMargin = float(mw.psetup.widget().rightMarginLineEdit.text(
- )) if mw.psetup.widget().rightMarginLineEdit.text() else self.style.rightMargin
- self.style.topMargin = float(mw.psetup.widget().topMarginLineEdit.text(
- )) if mw.psetup.widget().topMarginLineEdit.text() else self.style.topMargin
- self.style.bottomMargin = float(mw.psetup.widget().bottomMarginLineEdit.text(
- )) if mw.psetup.widget().bottomMarginLineEdit.text() else self.style.bottomMargin
- self.style.lineSpacing = float(mw.psetup.widget().lineSpacingDoubleSpinBox.value(
- )) if mw.psetup.widget().lineSpacingDoubleSpinBox.value() else self.style.lineSpacing
-
- # make sure the unit width isn't too wide to draw!
- if mw.psetup.widget().beatWidthLineEdit.text():
- if (self.style.pageSize[0] - 2 * self.style.leftMargin * mm) >= (float(mw.psetup.widget().beatWidthLineEdit.text()) * 2 * self.doc.timeSignature * mm):
- self.style.unitWidth = float(
- mw.psetup.widget().beatWidthLineEdit.text())
- else:
- 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)
-
- # update chords, sections, blocks
- self.updateChords()
- self.updateSections()
- if mw.currentSection:
- self.updateBlocks(mw.currentSection)
-
- self.style.font = (
- 'FreeSans' if self.style.useIncludedFont else 'HelveticaNeue')
- # something for the font box here
+ self.sectionDict = {s.name: s for s in self.doc.sectionList}
if __name__ == '__main__':
app = QApplication(sys.argv)
diff --git a/preview.pdf b/preview.pdf
deleted file mode 100644
index e946d38..0000000
Binary files a/preview.pdf and /dev/null differ
diff --git a/ui/aboutdialog.ui b/ui/aboutdialog.ui
index 8b57c97..14280ee 100644
--- a/ui/aboutdialog.ui
+++ b/ui/aboutdialog.ui
@@ -14,7 +14,7 @@
-
+
0
0
@@ -25,10 +25,25 @@
200
+
+
+ 436
+ 200
+
+
About Chordsheet
+
+ false
+
+
+ true
+
+
+ QLayout::SetFixedSize
+
-
-
diff --git a/ui/blocks.ui b/ui/blocks.ui
index 5040468..2eb5bf0 100644
--- a/ui/blocks.ui
+++ b/ui/blocks.ui
@@ -270,12 +270,12 @@
BlockTableView
QTableView
-
+
MComboBox
QComboBox
-
+
diff --git a/ui/chords.ui b/ui/chords.ui
index 40a4f67..e8ad895 100644
--- a/ui/chords.ui
+++ b/ui/chords.ui
@@ -220,7 +220,7 @@
ChordTableView
QTableView
-
+
diff --git a/ui/document.ui b/ui/document.ui
index 3577480..aa177b1 100644
--- a/ui/document.ui
+++ b/ui/document.ui
@@ -36,7 +36,7 @@
- 400
+ 200
300
@@ -48,7 +48,7 @@
PDFViewer
QWidget
-
+
1
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 34c8f20..df9365b 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -7,7 +7,7 @@
0
0
1061
- 659
+ 639
@@ -20,960 +20,33 @@
QTabWidget::Rounded
-
-
-
-
-
- QFrame::NoFrame
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ true
-
- Qt::Horizontal
+
+ true
+
+
+ true
+
+
+ true
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- 430
- 600
-
-
-
-
- 500
- 16777215
-
-
-
-
-
-
-
- QLayout::SetDefaultConstraint
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 500
- 16777215
-
-
-
- 4
-
-
-
- Overview
-
-
-
-
-
-
-
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
-
- Title
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Subtitle
-
-
-
- -
-
-
- -
-
-
- Composer
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Arranger
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Tempo
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 60
- 16777215
-
-
-
-
- -
-
-
- Time
-
-
-
- -
-
-
-
- 40
- 16777215
-
-
-
-
-
-
- 4
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
-
-
- Page
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Page options
-
-
-
-
-
-
-
-
-
- Page size
-
-
-
- -
-
-
- -
-
-
- Document units
-
-
-
- -
-
-
- -
-
-
- Left margin
-
-
-
- -
-
-
-
- 60
- 16777215
-
-
-
-
- -
-
-
- Top margin
-
-
-
- -
-
-
-
- 60
- 16777215
-
-
-
-
- -
-
-
- Right margin
-
-
-
- -
-
-
-
- 60
- 16777215
-
-
-
-
- -
-
-
- Bottom margin
-
-
-
- -
-
-
-
- 60
- 16777215
-
-
-
-
-
-
-
-
-
- -
-
-
- Text options
-
-
-
-
-
-
-
-
-
- Line spacing
-
-
-
- -
-
-
-
- 70
- 0
-
-
-
-
- 70
- 16777215
-
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Font options
-
-
-
-
-
-
-
-
-
-
- 40
- 16777215
-
-
-
- Font
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
- -
-
-
- Use included FreeSans
-
-
-
-
-
-
- -
-
-
- Block options
-
-
-
-
-
-
-
-
-
- Beat width
-
-
-
- -
-
-
-
- 60
- 16777215
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
-
-
-
- Chords
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- QAbstractItemView::NoEditTriggers
-
-
- true
-
-
- false
-
-
- QAbstractItemView::InternalMove
-
-
- Qt::IgnoreAction
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
- false
-
-
- false
-
-
-
- -
-
-
-
-
-
- Chord name
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Guitar voicing
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 100
- 16777215
-
-
-
-
- -
-
-
-
- 16777215
- 16777215
-
-
-
- Editor...
-
-
-
- -
-
-
- -
-
-
- Piano voicing
-
-
-
-
-
- -
-
-
-
-
-
- Remove chord
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Update chord
-
-
-
- -
-
-
- Add chord
-
-
-
-
-
-
-
-
-
-
-
- Sections
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- QAbstractItemView::NoEditTriggers
-
-
- true
-
-
- false
-
-
- QAbstractItemView::InternalMove
-
-
- Qt::TargetMoveAction
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
- false
-
-
-
- -
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
-
- Name
-
-
-
- -
-
-
-
-
- -
-
-
-
-
-
- Remove section
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 0
- 20
-
-
-
-
- -
-
-
- Update section
-
-
-
- -
-
-
- Add section
-
-
-
-
-
-
-
-
-
-
-
- Blocks
-
-
- -
-
-
-
-
-
- QFormLayout::ExpandingFieldsGrow
-
-
-
-
-
- Section
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- QAbstractItemView::NoEditTriggers
-
-
- true
-
-
- false
-
-
- QAbstractItemView::InternalMove
-
-
- Qt::TargetMoveAction
-
-
- QAbstractItemView::SingleSelection
-
-
- QAbstractItemView::SelectRows
-
-
- false
-
-
- false
-
-
-
- -
-
-
-
-
-
- Length
-
-
-
- -
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Notes
-
-
-
- -
-
-
- Chord
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 40
- 16777215
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
-
-
-
- Remove block
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Update block
-
-
-
- -
-
-
- Add block
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Generate chordsheet
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::MinimumExpanding
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1
- 0
-
-
-
-
- 300
- 400
-
-
-
- true
-
-
@@ -1001,6 +74,8 @@
+
+
+
+
+
+
@@ -1093,76 +188,32 @@
About
+
+
+ Cascade windows
+
+
+
+
+ Tile windows
+
+
+
+
+ Next window
+
+
+
+
+ Previous window
+
+
+
+
+ Redock all panels
+
+
-
-
- ChordTableView
- QTableView
-
-
-
- BlockTableView
- QTableView
-
-
-
- SectionTableView
- QTableView
-
-
-
- MComboBox
- QComboBox
-
-
-
- PDFViewer
- QWidget
-
- 1
-
-
-
- generateButton
- tabWidget
- titleLineEdit
- subtitleLineEdit
- composerLineEdit
- arrangerLineEdit
- tempoLineEdit
- timeSignatureSpinBox
- pageSizeComboBox
- documentUnitsComboBox
- leftMarginLineEdit
- rightMarginLineEdit
- topMarginLineEdit
- bottomMarginLineEdit
- lineSpacingDoubleSpinBox
- fontComboBox
- includedFontCheckBox
- beatWidthLineEdit
- chordTableView
- chordNameLineEdit
- guitarVoicingLineEdit
- pianoVoicingLineEdit
- guitarVoicingButton
- removeChordButton
- updateChordButton
- addChordButton
- sectionTableView
- sectionNameLineEdit
- removeSectionButton
- updateSectionButton
- addSectionButton
- blockSectionComboBox
- blockTableView
- blockLengthLineEdit
- blockChordComboBox
- blockNotesLineEdit
- removeBlockButton
- updateBlockButton
- addBlockButton
-
diff --git a/ui/new.ui b/ui/new.ui
deleted file mode 100644
index eb82610..0000000
--- a/ui/new.ui
+++ /dev/null
@@ -1,233 +0,0 @@
-
-
- MainWindow
-
-
-
- 0
- 0
- 1061
- 639
-
-
-
- Chordsheet
-
-
- false
-
-
- QTabWidget::Rounded
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- true
-
-
- true
-
-
- true
-
-
- true
-
-
-
-
-
-
-
-
- New...
-
-
-
-
- Open...
-
-
-
-
- Save
-
-
-
-
- Save PDF...
-
-
-
-
- Print...
-
-
-
-
- Close
-
-
-
-
- Save as...
-
-
-
-
- Quit
-
-
-
-
- Undo
-
-
-
-
- Redo
-
-
-
-
- Cut
-
-
-
-
- Copy
-
-
-
-
- Paste
-
-
-
-
- Preferences
-
-
-
-
- About
-
-
-
-
- true
-
-
- Document information
-
-
-
-
- true
-
-
- Page setup
-
-
-
-
- true
-
-
- Chords
-
-
-
-
- true
-
-
- Sections
-
-
-
-
- true
-
-
- Blocks
-
-
-
-
- true
-
-
- Preview
-
-
-
-
-
-
diff --git a/ui/pdfarea.ui b/ui/pdfarea.ui
deleted file mode 100644
index ce109c2..0000000
--- a/ui/pdfarea.ui
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- Form
-
-
-
- 0
- 0
- 400
- 300
-
-
-
- PDF Viewer
-
-
- -
-
-
-
-
-
-
- PDFViewer
- QWidget
-
- 1
-
-
-
-
-
diff --git a/ui/preview.ui b/ui/preview.ui
index 712ac9d..beef3bf 100644
--- a/ui/preview.ui
+++ b/ui/preview.ui
@@ -6,8 +6,8 @@
0
0
- 248
- 40
+ 202
+ 72
@@ -18,31 +18,50 @@
- 0
- 40
+ 202
+ 72
Preview
-
+
6
- 0
+ 6
- 0
+ 6
- 0
+ 6
- 0
+ 6
- -
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Automatically update preview
+
+
+ true
+
+
+
+ -
+
+ false
+
0
@@ -52,6 +71,9 @@
Update preview
+
+ false
+
diff --git a/ui/sections.ui b/ui/sections.ui
index 80d35d2..b04ddbb 100644
--- a/ui/sections.ui
+++ b/ui/sections.ui
@@ -157,7 +157,7 @@
SectionTableView
QTableView
-
+