diff --git a/_version.py b/_version.py
index 46e40bb..02f7836 100644
--- a/_version.py
+++ b/_version.py
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
appName = "Chordsheet"
-version = '0.4.6'
+version = '0.5.0'
diff --git a/chordsheet/common.py b/chordsheet/common.py
new file mode 100644
index 0000000..5a972fe
--- /dev/null
+++ b/chordsheet/common.py
@@ -0,0 +1,7 @@
+import sys, os
+
+# set the directory where our files are depending on whether we're running a pyinstaller binary or not
+if getattr(sys, 'frozen', False):
+ scriptDir = sys._MEIPASS
+else:
+ scriptDir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
\ No newline at end of file
diff --git a/chordsheet/dialogs.py b/chordsheet/dialogs.py
new file mode 100644
index 0000000..dcc03ed
--- /dev/null
+++ b/chordsheet/dialogs.py
@@ -0,0 +1,67 @@
+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
+from PyQt5.QtCore import QFile, QObject, Qt
+from PyQt5.QtGui import QImage, QPixmap
+from PyQt5 import uic
+
+from chordsheet.common import scriptDir
+import _version
+
+class GuitarDialog(QDialog):
+ """
+ Dialogue to allow the user to enter a guitar chord voicing. Not particularly advanced at present!
+ May be extended in future.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader(
+ str(os.path.join(scriptDir, 'ui', 'guitardialog.ui')))
+
+ def UIFileLoader(self, ui_file):
+ ui_file = QFile(ui_file)
+ ui_file.open(QFile.ReadOnly)
+
+ self.dialog = uic.loadUi(ui_file)
+ 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(),
+ self.dialog.DLineEdit.text(),
+ self.dialog.GLineEdit.text(),
+ self.dialog.BLineEdit.text(),
+ self.dialog.eLineEdit.text()]
+ resultJoined = ",".join(result)
+ return resultJoined
+ else:
+ return None
+
+
+class AboutDialog(QDialog):
+ """
+ Dialogue showing information about the program.
+ """
+
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'aboutdialog.ui')))
+
+ icon = QImage(str(os.path.join(scriptDir, 'ui', 'icon.png')))
+ self.dialog.iconLabel.setPixmap(QPixmap.fromImage(icon).scaled(self.dialog.iconLabel.width(
+ ), self.dialog.iconLabel.height(), Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation))
+
+ self.dialog.versionLabel.setText("Version " + _version.version)
+
+ self.dialog.exec()
+
+ def UIFileLoader(self, ui_file):
+ ui_file = QFile(ui_file)
+ ui_file.open(QFile.ReadOnly)
+
+ self.dialog = uic.loadUi(ui_file)
+ ui_file.close()
\ No newline at end of file
diff --git a/chordsheet/messageBox.py b/chordsheet/messageBox.py
new file mode 100644
index 0000000..87a620e
--- /dev/null
+++ b/chordsheet/messageBox.py
@@ -0,0 +1,118 @@
+from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut
+
+class UnsavedMessageBox(QMessageBox):
+ """
+ Message box to alert the user of unsaved changes and allow them to choose how to act.
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Question)
+ self.setWindowTitle("Unsaved changes")
+ self.setText("The document has been modified.")
+ self.setInformativeText("Do you want to save your changes?")
+ self.setStandardButtons(
+ QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
+ self.setDefaultButton(QMessageBox.Save)
+
+
+class UnreadableMessageBox(QMessageBox):
+ """
+ Message box to warn the user that the chosen file cannot be opened.
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("File 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)
+ self.setDefaultButton(QMessageBox.Ok)
+
+
+class ChordNameWarningMessageBox(QMessageBox):
+ """
+ Message box to warn the user that a chord must have a name
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("Unnamed chord")
+ self.setText("Chords must have a name.")
+ self.setInformativeText("Please give your chord a name and try again.")
+ self.setStandardButtons(QMessageBox.Ok)
+ self.setDefaultButton(QMessageBox.Ok)
+
+
+class SectionNameWarningMessageBox(QMessageBox):
+ """
+ Message box to warn the user that a section must have a name
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("Unnamed section")
+ self.setText("Sections must have a unique name.")
+ self.setInformativeText(
+ "Please give your section a unique name and try again.")
+ self.setStandardButtons(QMessageBox.Ok)
+ self.setDefaultButton(QMessageBox.Ok)
+
+
+class BlockMustHaveSectionWarningMessageBox(QMessageBox):
+ """
+ Message box to warn the user that a block must belong to a section
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("No sections found")
+ self.setText("Each block must belong to a section, but no sections have yet been created.")
+ self.setInformativeText(
+ "Please create a section before adding blocks.")
+ self.setStandardButtons(QMessageBox.Ok)
+ self.setDefaultButton(QMessageBox.Ok)
+
+
+class VoicingWarningMessageBox(QMessageBox):
+ """
+ Message box to warn the user that the voicing entered could not be parsed
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("Malformed voicing")
+ self.setText(
+ "The voicing you entered was not understood and has not been applied.")
+ self.setInformativeText(
+ "Please try re-entering it in the correct format.")
+ self.setStandardButtons(QMessageBox.Ok)
+ self.setDefaultButton(QMessageBox.Ok)
+
+
+class LengthWarningMessageBox(QMessageBox):
+ """
+ Message box to warn the user that a block must have a length
+ """
+
+ def __init__(self):
+ super().__init__()
+
+ self.setIcon(QMessageBox.Warning)
+ self.setWindowTitle("Block without valid length")
+ self.setText("Blocks must have a length.")
+ self.setInformativeText(
+ "Please enter a valid length for your block and try again.")
+ self.setStandardButtons(QMessageBox.Ok)
+ self.setDefaultButton(QMessageBox.Ok)
diff --git a/chordsheet/panels.py b/chordsheet/panels.py
new file mode 100644
index 0000000..41f0427
--- /dev/null
+++ b/chordsheet/panels.py
@@ -0,0 +1,54 @@
+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.QtCore import QFile, QObject, Qt
+from PyQt5.QtGui import QImage, QPixmap
+from PyQt5 import uic
+
+from chordsheet.common import scriptDir
+
+class UIFileDockWidget(QDockWidget):
+ def __init__(self):
+ super().__init__()
+
+ def UIFileLoader(self, ui_file):
+ ui_file = QFile(os.path.join(scriptDir, 'ui', ui_file))
+ ui_file.open(QFile.ReadOnly)
+
+ self.setWidget(uic.loadUi(ui_file))
+ ui_file.close()
+
+class DocInfoDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('docinfo.ui')
+ self.setWindowTitle("Document information")
+
+class PageSetupDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('psetup.ui')
+ self.setWindowTitle("Page setup")
+
+class ChordsDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('chords.ui')
+ self.setWindowTitle("Chords")
+
+class SectionsDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('sections.ui')
+ self.setWindowTitle("Sections")
+
+class BlocksDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('blocks.ui')
+ self.setWindowTitle("Blocks")
+
+class PreviewDockWidget(UIFileDockWidget):
+ def __init__(self):
+ super().__init__()
+ self.UIFileLoader('preview.ui')
+ self.setWindowTitle("Preview")
\ No newline at end of file
diff --git a/chordsheet/pdfViewer.py b/chordsheet/pdfViewer.py
index 6c63d80..0db75df 100644
--- a/chordsheet/pdfViewer.py
+++ b/chordsheet/pdfViewer.py
@@ -1,9 +1,29 @@
-from PyQt5.QtWidgets import QScrollArea, QLabel, QVBoxLayout, QWidget
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QImage
+from PyQt5.QtWidgets import QScrollArea, QLabel, QVBoxLayout, QWidget, QSizePolicy
+from PyQt5.QtCore import Qt, QPoint, QSize
+from PyQt5.QtGui import QPixmap, QImage, QResizeEvent, QPainter
import fitz
+class PDFLabel(QLabel):
+ def __init__(self, parent):
+ super().__init__(parent)
+ self.parent = parent
+ self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
+
+ def paintEvent(self, event):
+ self.adjustSize()
+ if self.pixmap() is not None:
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing)
+ idealWidth = self.parent.width()-45
+ pixSize = self.pixmap().size()
+
+ pixSize.scale(idealWidth, 1000000, Qt.KeepAspectRatio)
+
+ scaledPix = self.pixmap().scaled(pixSize, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ painter.drawPixmap(QPoint(), scaledPix)
+ self.setMaximumSize(pixSize)
+
class PDFViewer(QScrollArea):
def __init__(self, parent):
super().__init__(parent)
@@ -14,13 +34,9 @@ class PDFViewer(QScrollArea):
self.setWidgetResizable(True)
self.scrollAreaContents.setLayout(self.scrollAreaLayout)
- self.pixmapList = []
-
- def resizeEvent(self, event):
- pass
- # do something about this later
+ self.pixmapList = []
- def update(self, pdf):
+ def update_pdf(self, pdf):
self.render(pdf)
self.clear()
self.show()
@@ -32,9 +48,9 @@ class PDFViewer(QScrollArea):
self.pixmapList = []
pdfView = fitz.Document(stream=pdf, filetype='pdf')
- # render at 4x resolution and scale
+ # render at 8x resolution and scale
for page in pdfView:
- self.pixmapList.append(page.getPixmap(matrix=fitz.Matrix(4, 4), alpha=False))
+ self.pixmapList.append(page.getPixmap(matrix=fitz.Matrix(8, 8), alpha=False))
def clear(self):
while self.scrollAreaLayout.count():
@@ -45,12 +61,14 @@ class PDFViewer(QScrollArea):
def show(self):
for p in self.pixmapList:
- label = QLabel(parent=self.scrollAreaContents)
+ 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).scaled(self.width()-45, self.height()*2, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation))
+ 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
diff --git a/gui.py b/gui.py
index ed53da6..dc81a3d 100755
--- a/gui.py
+++ b/gui.py
@@ -14,7 +14,7 @@ import os
import time
from copy import copy
-from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut
+from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut, QMdiSubWindow
from PyQt5.QtCore import QFile, QObject, Qt, pyqtSlot, QSettings
from PyQt5.QtGui import QPixmap, QImage, QKeySequence
from PyQt5 import uic
@@ -27,18 +27,16 @@ 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
+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
-# set the directory where our files are depending on whether we're running a pyinstaller binary or not
-if getattr(sys, 'frozen', False):
- scriptDir = sys._MEIPASS
-else:
- scriptDir = os.path.abspath(os.path.dirname(os.path.abspath(__file__)))
-
# enable automatic high DPI scaling on Windows
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
QApplication.setOrganizationName("Ivan Holmes")
@@ -58,12 +56,12 @@ pageSizeDict = {'A4': A4, 'A5': A5, 'Letter': LETTER, 'Legal': LEGAL}
unitDict = {'mm': mm, 'cm': cm, 'inch': inch, 'point': 1, 'pica': pica}
-class DocumentWindow(QMainWindow):
+class MainWindow(QMainWindow):
"""
Class for the main window of the application.
"""
- def __init__(self, doc, style, filename=None):
+ def __init__(self, filename=None):
"""
Initialisation function for the main window of the application.
@@ -73,18 +71,7 @@ class DocumentWindow(QMainWindow):
"""
super().__init__()
- self.doc = doc
- self.style = style
- self.renderer = Renderer(self.doc, self.style)
-
- self.lastDoc = copy(self.doc)
- self.currentFilePath = filename
-
- self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'mainwindow.ui')))
- self.UIInitStyle()
- self.updateChordDict()
- self.updateSectionDict()
- self.currentSection = None
+ self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'new.ui')))
self.setCentralWidget(self.window.centralWidget)
self.setMenuBar(self.window.menuBar)
@@ -100,7 +87,7 @@ class DocumentWindow(QMainWindow):
"""
Reimplement the built in closeEvent to allow asking the user to save.
"""
- if self.saveWarning():
+ if not self.window.mdiArea.subWindowList():
self.close()
def UIFileLoader(self, ui_file):
@@ -112,10 +99,25 @@ class DocumentWindow(QMainWindow):
self.window = uic.loadUi(ui_file)
ui_file.close()
-
+
+ self.docinfo = DocInfoDockWidget()
+ self.psetup = PageSetupDockWidget()
+ self.chordsw = ChordsDockWidget()
+ self.sectionsw = SectionsDockWidget()
+ self.blocksw = BlocksDockWidget()
+ self.previeww = PreviewDockWidget()
+
+ 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)
+
# link all the UI elements
+ self.window.mdiArea.subWindowActivated.connect(self.switchDocument)
+
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)
@@ -143,152 +145,370 @@ class DocumentWindow(QMainWindow):
self.window.actionCopy.setShortcut(QKeySequence.Copy)
self.window.actionPaste.setShortcut(QKeySequence.Paste)
- self.window.pageSizeComboBox.currentIndexChanged.connect(
+ self.psetup.widget().pageSizeComboBox.currentIndexChanged.connect(
self.pageSizeAction)
- self.window.documentUnitsComboBox.currentIndexChanged.connect(
+ 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])
+
+ self.psetup.widget().documentUnitsComboBox.addItems(list(unitDict.keys()))
+ self.psetup.widget().documentUnitsComboBox.setCurrentText(
+ list(unitDict.keys())[0])
- self.window.includedFontCheckBox.stateChanged.connect(
+ self.psetup.widget().includedFontCheckBox.stateChanged.connect(
self.includedFontAction)
- self.window.generateButton.clicked.connect(self.generateAction)
+ self.previeww.widget().updatePreviewButton.clicked.connect(self.generateAction)
# update whole document when any tab is selected
- self.window.tabWidget.tabBarClicked.connect(self.tabBarUpdateAction)
+ # self.window.tabWidget.tabBarClicked.connect(self.tabBarUpdateAction)
- self.window.guitarVoicingButton.clicked.connect(
+ self.chordsw.widget().guitarVoicingButton.clicked.connect(
self.guitarVoicingAction)
- self.window.addChordButton.clicked.connect(self.addChordAction)
- self.window.removeChordButton.clicked.connect(self.removeChordAction)
- self.window.updateChordButton.clicked.connect(self.updateChordAction)
+ self.chordsw.widget().addChordButton.clicked.connect(self.addChordAction)
+ self.chordsw.widget().removeChordButton.clicked.connect(self.removeChordAction)
+ self.chordsw.widget().updateChordButton.clicked.connect(self.updateChordAction)
# connecting clicked only works for this combo box because it's my own modified version (MComboBox)
- self.window.blockSectionComboBox.clicked.connect(
+ self.blocksw.widget().blockSectionComboBox.clicked.connect(
self.blockSectionClickedAction)
- self.window.blockSectionComboBox.currentIndexChanged.connect(
+ self.blocksw.widget().blockSectionComboBox.currentIndexChanged.connect(
self.blockSectionChangedAction)
- self.window.addBlockButton.clicked.connect(self.addBlockAction)
- self.window.removeBlockButton.clicked.connect(self.removeBlockAction)
- self.window.updateBlockButton.clicked.connect(self.updateBlockAction)
+ 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.window.addSectionButton.clicked.connect(self.addSectionAction)
- self.window.removeSectionButton.clicked.connect(
+ self.sectionsw.widget().addSectionButton.clicked.connect(self.addSectionAction)
+ self.sectionsw.widget().removeSectionButton.clicked.connect(
self.removeSectionAction)
- self.window.updateSectionButton.clicked.connect(
+ self.sectionsw.widget().updateSectionButton.clicked.connect(
self.updateSectionAction)
- self.window.chordTableView.clicked.connect(self.chordClickedAction)
- self.window.sectionTableView.clicked.connect(self.sectionClickedAction)
- self.window.blockTableView.clicked.connect(self.blockClickedAction)
+ self.chordsw.widget().chordTableView.clicked.connect(self.chordClickedAction)
+ self.sectionsw.widget().sectionTableView.clicked.connect(self.sectionClickedAction)
+ self.blocksw.widget().blockTableView.clicked.connect(self.blockClickedAction)
# Set the tab widget to Overview tab
- self.window.tabWidget.setCurrentIndex(0)
+ # self.window.tabWidget.setCurrentIndex(0)
- def UIInitDocument(self):
+ def UIInitDocument(self, doc):
"""
Fills the window's fields with the values from its document.
"""
- self.updateTitleBar()
+ # self.updateTitleBar()
# set all fields to appropriate values from document
- self.window.titleLineEdit.setText(self.doc.title)
- self.window.subtitleLineEdit.setText(self.doc.subtitle)
- self.window.composerLineEdit.setText(self.doc.composer)
- self.window.arrangerLineEdit.setText(self.doc.arranger)
- self.window.timeSignatureSpinBox.setValue(self.doc.timeSignature)
- self.window.tempoLineEdit.setText(self.doc.tempo)
-
- self.window.chordTableView.populate(self.doc.chordList)
- self.window.sectionTableView.populate(self.doc.sectionList)
+ self.docinfo.widget().titleLineEdit.setText(doc.title)
+ self.docinfo.widget().subtitleLineEdit.setText(doc.subtitle)
+ self.docinfo.widget().composerLineEdit.setText(doc.composer)
+ self.docinfo.widget().arrangerLineEdit.setText(doc.arranger)
+ self.docinfo.widget().timeSignatureSpinBox.setValue(doc.timeSignature)
+ self.docinfo.widget().tempoLineEdit.setText(doc.tempo)
+
+ self.chordsw.widget().chordTableView.populate(doc.chordList)
+ self.sectionsw.widget().sectionTableView.populate(doc.sectionList)
# populate the block table with the first section, account for a document with no sections
- self.currentSection = self.doc.sectionList[0] if len(
- self.doc.sectionList) else None
- self.window.blockTableView.populate(
+ self.currentSection = doc.sectionList[0] if len(
+ doc.sectionList) else None
+ self.blocksw.widget().blockTableView.populate(
self.currentSection.blockList if self.currentSection else [])
- self.updateSectionDict()
- self.updateChordDict()
+ self.updateChordDict(doc)
+ self.updateSectionDict(doc)
- def UIInitStyle(self):
+ def UIInitStyle(self, style):
"""
Fills the window's fields with the values from its style.
"""
- self.window.pageSizeComboBox.addItems(list(pageSizeDict.keys()))
- self.window.pageSizeComboBox.setCurrentText(
- list(pageSizeDict.keys())[0])
+ self.psetup.widget().pageSizeComboBox.setCurrentText(
+ [k for k, v in pageSizeDict.items() if v==style.pageSize][0])
- self.window.documentUnitsComboBox.addItems(list(unitDict.keys()))
- self.window.documentUnitsComboBox.setCurrentText(
- list(unitDict.keys())[0])
+ self.psetup.widget().documentUnitsComboBox.setCurrentText(
+ [k for k, v in unitDict.items() if v==style.unit][0])
+
+ self.psetup.widget().lineSpacingDoubleSpinBox.setValue(style.lineSpacing)
- self.window.lineSpacingDoubleSpinBox.setValue(self.style.lineSpacing)
+ self.psetup.widget().leftMarginLineEdit.setText(str(style.leftMargin))
+ self.psetup.widget().rightMarginLineEdit.setText(str(style.rightMargin))
+ self.psetup.widget().topMarginLineEdit.setText(str(style.topMargin))
+ self.psetup.widget().bottomMarginLineEdit.setText(str(style.bottomMargin))
- self.window.leftMarginLineEdit.setText(str(self.style.leftMargin))
- self.window.rightMarginLineEdit.setText(str(self.style.rightMargin))
- self.window.topMarginLineEdit.setText(str(self.style.topMargin))
- self.window.bottomMarginLineEdit.setText(str(self.style.bottomMargin))
+ self.psetup.widget().fontComboBox.setDisabled(True)
+ self.psetup.widget().includedFontCheckBox.setChecked(True)
+
+ self.psetup.widget().beatWidthLineEdit.setText(str(style.unitWidth))
+
+ def updateChordDict(self, doc):
+ """
+ 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 doc.chordList})
+ self.blocksw.widget().blockChordComboBox.clear()
+ self.blocksw.widget().blockChordComboBox.addItems(list(self.chordDict.keys()))
+
+ def updateSectionDict(self, doc):
+ """
+ Updates the dictionary used to generate the Section menu (on the block tab)
+ """
+ self.sectionDict = {s.name: s for s in doc.sectionList}
+ self.blocksw.widget().blockSectionComboBox.clear()
+ self.blocksw.widget().blockSectionComboBox.addItems(
+ list(self.sectionDict.keys()))
+
+ def switchDocument(self):
+ if self.window.mdiArea.currentSubWindow() is not None:
+ self.UIInitDocument(self.window.mdiArea.currentSubWindow().doc)
+ self.UIInitStyle(self.window.mdiArea.currentSubWindow().style)
+
+ def generateAction(self):
+ if self.window.mdiArea.currentSubWindow() is not None:
+ self.window.mdiArea.currentSubWindow().updateDocument()
+ self.window.mdiArea.currentSubWindow().updatePreview()
+
+ def removeChordAction(self):
+ if self.chordsw.widget().chordTableView.selectionModel().hasSelection(): # check for selection
+ self.window.mdiArea.currentSubWindow().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.window.fontComboBox.setDisabled(True)
- self.window.includedFontCheckBox.setChecked(True)
+ self.chordsw.widget().chordTableView.populate(self.window.mdiArea.currentSubWindow().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:
+ 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.clearChordLineEdits()
+ self.window.mdiArea.currentSubWindow().updateChordDict()
- self.window.beatWidthLineEdit.setText(str(self.style.unitWidth))
+ def addChordAction(self):
+ success = False # initialise
+ self.window.mdiArea.currentSubWindow().updateChords()
- def tabBarUpdateAction(self, index):
- self.updateDocument()
-
+ cName = parseName(self.chordsw.widget().chordNameLineEdit.text())
+ if cName:
+ self.window.mdiArea.currentSubWindow().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.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.chordsw.widget().pianoVoicingLineEdit.text(), 'piano')
+ success = True # chord successfully parsed
+ except Exception:
+ VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
+ else:
+ success = True # chord successfully parsed
+ else:
+ 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.clearChordLineEdits()
+ self.window.mdiArea.currentSubWindow().updateChordDict()
+
+ def updateChordAction(self):
+ success = False # see comments above
+ if self.chordsw.widget().chordTableView.selectionModel().hasSelection(): # check for selection
+ self.window.mdiArea.currentSubWindow().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
+ 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.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.chordsw.widget().pianoVoicingLineEdit.text(), 'piano')
+ success = True # chord successfully parsed
+ except Exception:
+ VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
+ else:
+ success = True # chord successfully parsed
+ else:
+ ChordNameWarningMessageBox().exec()
+
+ if success == True:
+ self.window.mdiArea.currentSubWindow().updateChordDict()
+ self.chordsw.widget().chordTableView.populate(self.window.mdiArea.currentSubWindow().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 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)
+ self.clearChordLineEdits()
+
+ def removeSectionAction(self):
+ if self.sectionsw.widget().sectionTableView.selectionModel().hasSelection(): # check for selection
+ self.window.mdiArea.currentSubWindow().updateSections()
+
+ row = self.sectionsw.widget().sectionTableView.selectionModel().currentIndex().row()
+ self.window.mdiArea.currentSubWindow().doc.sectionList.pop(row)
+
+ self.sectionsw.widget().sectionTableView.populate(self.window.mdiArea.currentSubWindow().doc.sectionList)
+ self.clearSectionLineEdits()
+ self.window.mdiArea.currentSubWindow().updateSectionDict()
+
+ def addSectionAction(self):
+ self.window.mdiArea.currentSubWindow().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)
+ self.clearSectionLineEdits()
+ self.window.mdiArea.currentSubWindow().updateSectionDict()
+ 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()
+ 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)
+ self.clearSectionLineEdits()
+ self.window.mdiArea.currentSubWindow().updateSectionDict()
+ 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)
+
+ row = self.blocksw.widget().blockTableView.selectionModel().currentIndex().row()
+ self.currentSection.blockList.pop(row)
+
+ self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
+
+ def addBlockAction(self):
+ self.window.mdiArea.currentSubWindow().updateBlocks(self.currentSection)
+
+ try:
+ # can the value entered for block length be cast as a float
+ bLength = float(self.blocksw.widget().blockLengthLineEdit.text())
+ except Exception:
+ bLength = False
+
+ if bLength: # create the block
+ self.currentSection.blockList.append(Block(bLength,
+ chord=self.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.clearBlockLineEdits()
+ 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)
+
+ try:
+ # can the value entered for block length be cast as a float
+ bLength = float(self.blocksw.widget().blockLengthLineEdit.text())
+ except Exception:
+ bLength = False
+
+ row = self.blocksw.widget().blockTableView.selectionModel().currentIndex().row()
+ if bLength:
+ self.currentSection.blockList[row] = (Block(bLength,
+ chord=self.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.clearBlockLineEdits()
+ else:
+ LengthWarningMessageBox().exec()
+
def pageSizeAction(self, index):
- self.pageSizeSelected = self.window.pageSizeComboBox.itemText(index)
+ self.pageSizeSelected = self.psetup.widget().pageSizeComboBox.itemText(index)
def unitAction(self, index):
- self.unitSelected = self.window.documentUnitsComboBox.itemText(index)
+ self.unitSelected = self.psetup.widget().documentUnitsComboBox.itemText(index)
def includedFontAction(self):
- if self.window.includedFontCheckBox.isChecked():
- self.style.useIncludedFont = True
- else:
- self.style.useIncludedFont = False
-
+ 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
+
def chordClickedAction(self, index):
# set the controls to the values from the selected chord
- self.window.chordNameLineEdit.setText(
- self.window.chordTableView.model.item(index.row(), 0).text())
- self.window.guitarVoicingLineEdit.setText(
- self.window.chordTableView.model.item(index.row(), 1).text())
- self.window.pianoVoicingLineEdit.setText(
- self.window.chordTableView.model.item(index.row(), 2).text())
+ self.chordsw.widget().chordNameLineEdit.setText(
+ self.chordsw.widget().chordTableView.model.item(index.row(), 0).text())
+ self.chordsw.widget().guitarVoicingLineEdit.setText(
+ self.chordsw.widget().chordTableView.model.item(index.row(), 1).text())
+ self.chordsw.widget().pianoVoicingLineEdit.setText(
+ self.chordsw.widget().chordTableView.model.item(index.row(), 2).text())
def sectionClickedAction(self, index):
# set the controls to the values from the selected section
- self.window.sectionNameLineEdit.setText(
- self.window.sectionTableView.model.item(index.row(), 0).text())
+ self.sectionsw.widget().sectionNameLineEdit.setText(
+ self.sectionsw.widget().sectionTableView.model.item(index.row(), 0).text())
# also set the combo box on the block page to make it flow well
- curSecName = self.window.sectionTableView.model.item(
+ curSecName = self.sectionsw.widget().sectionTableView.model.item(
index.row(), 0).text()
if curSecName:
- self.window.blockSectionComboBox.setCurrentText(
+ self.blocksw.widget().blockSectionComboBox.setCurrentText(
curSecName)
def blockSectionClickedAction(self, text):
if text:
- self.updateBlocks(self.sectionDict[text])
+ self.window.mdiArea.currentSubWindow().updateBlocks(self.sectionDict[text])
def blockSectionChangedAction(self, index):
- sName = self.window.blockSectionComboBox.currentText()
+ sName = self.blocksw.widget().blockSectionComboBox.currentText()
if sName:
- self.currentSection = self.sectionDict[sName]
- self.window.blockTableView.populate(self.currentSection.blockList)
+ if self.window.mdiArea.currentSubWindow() is not None:
+ self.currentSection = self.sectionDict.get(sName, None)
+ if self.currentSection is not None:
+ self.blocksw.widget().blockTableView.populate(self.currentSection.blockList)
else:
self.currentSection = None
def blockClickedAction(self, index):
# set the controls to the values from the selected block
- bChord = self.window.blockTableView.model.item(index.row(), 0).text()
- self.window.blockChordComboBox.setCurrentText(
+ bChord = self.blocksw.widget().blockTableView.model.item(index.row(), 0).text()
+ self.blocksw.widget().blockChordComboBox.setCurrentText(
bChord if bChord else "None")
- self.window.blockLengthLineEdit.setText(
- self.window.blockTableView.model.item(index.row(), 1).text())
- self.window.blockNotesLineEdit.setText(
- self.window.blockTableView.model.item(index.row(), 2).text())
+ self.blocksw.widget().blockLengthLineEdit.setText(
+ self.blocksw.widget().blockTableView.model.item(index.row(), 1).text())
+ self.blocksw.widget().blockNotesLineEdit.setText(
+ self.blocksw.widget().blockTableView.model.item(index.row(), 2).text())
def getPath(self, value):
"""
@@ -303,89 +523,56 @@ class DocumentWindow(QMainWindow):
return settings.setValue(value, os.path.dirname(fullpath))
def menuFileNewAction(self):
- if self.saveWarning(): # ask the user if they want to save
- self.doc = Document() # new document object
- # copy this object as reference to check against on quitting
- self.lastDoc = copy(self.doc)
- # reset file path (this document hasn't been saved yet)
- self.currentFilePath = None
- # new renderer
- self.renderer = Renderer(self.doc, self.style)
- self.UIInitDocument()
- self.updatePreview()
+ dw = DocumentWindow(Document(), Style(), None)
+ self.window.mdiArea.addSubWindow(dw)
+ self.UIInitDocument(dw.doc)
+ dw.show()
+
def menuFileOpenAction(self):
- if self.saveWarning(): # ask the user if they want to save
- filePath = QFileDialog.getOpenFileName(self.window.tabWidget, 'Open file', self.getPath(
- "workingPath"), "Chordsheet Markup Language files (*.xml *.cml);;Chordsheet Macro files (*.cma)")[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
-
- 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)
+ filePath = QFileDialog.getOpenFileName(self.window, 'Open file', self.getPath(
+ "workingPath"), "Chordsheet Markup Language files (*.xml *.cml);;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
- self.lastDoc = copy(self.doc)
- self.setPath("workingPath", self.currentFilePath)
- self.UIInitDocument()
- self.updatePreview()
+ dw.show()
def menuFileSaveAction(self):
- self.updateDocument()
+ 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)
+ 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)
- if self.currentFilePath:
- fileExt = os.path.splitext(self.currentFilePath)[1].lower()
- if fileExt != ".cma":
- # Chordsheet Macro files can't be saved at this time
- self.saveFile(self.currentFilePath)
- else:
- filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath(
+ 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.saveFile(filePath)
-
- def menuFileSaveAsAction(self):
- self.updateDocument()
- filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath(
- "workingPath"), "Chordsheet ML files (*.xml *.cml)")[0]
- if filePath:
- self.saveFile(filePath)
-
- 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)
- self.setPath("workingPath", self.currentFilePath)
- self.updateTitleBar() # as we may have a new filename
+ self.window.mdiArea.currentSubWindow().saveFile(filePath)
def menuFileSavePDFAction(self):
- self.updateDocument()
- self.updatePreview()
- filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', self.getPath(
- "lastExportPath"), "PDF files (*.pdf)")[0]
- if filePath:
- self.renderer.savePDF(filePath)
- self.setPath("lastExportPath", filePath)
+ 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)
def menuFilePrintAction(self):
if sys.platform == "darwin":
@@ -431,264 +618,134 @@ class DocumentWindow(QMainWindow):
except Exception:
pass
- 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:
- return True
- else:
- wantToSave = UnsavedMessageBox().exec()
-
- if wantToSave == QMessageBox.Save:
- if not self.currentFilePath:
- filePath = QFileDialog.getSaveFileName(self.window.tabWidget, 'Save file', str(
- os.path.expanduser("~")), "Chordsheet ML files (*.xml *.cml)")
- self.currentFilePath = filePath[0]
- self.doc.saveXML(self.currentFilePath)
- return True
-
- elif wantToSave == QMessageBox.Discard:
- return True
-
- else:
- return False
-
def guitarVoicingAction(self):
gdialog = GuitarDialog()
voicing = gdialog.getVoicing()
if voicing:
- self.window.guitarVoicingLineEdit.setText(voicing)
+ self.chordsw.widget().guitarVoicingLineEdit.setText(voicing)
def clearChordLineEdits(self):
- self.window.chordNameLineEdit.clear()
- self.window.guitarVoicingLineEdit.clear()
- self.window.pianoVoicingLineEdit.clear()
+ self.chordsw.widget().chordNameLineEdit.clear()
+ self.chordsw.widget().guitarVoicingLineEdit.clear()
+ self.chordsw.widget().pianoVoicingLineEdit.clear()
# necessary on Mojave with PyInstaller (or previous contents will be shown)
- self.window.chordNameLineEdit.repaint()
- self.window.guitarVoicingLineEdit.repaint()
- self.window.pianoVoicingLineEdit.repaint()
+ self.chordsw.widget().chordNameLineEdit.repaint()
+ self.chordsw.widget().guitarVoicingLineEdit.repaint()
+ self.chordsw.widget().pianoVoicingLineEdit.repaint()
def clearSectionLineEdits(self):
- self.window.sectionNameLineEdit.clear()
+ self.sectionsw.widget().sectionNameLineEdit.clear()
# necessary on Mojave with PyInstaller (or previous contents will be shown)
- self.window.sectionNameLineEdit.repaint()
+ self.sectionsw.widget().sectionNameLineEdit.repaint()
def clearBlockLineEdits(self):
- self.window.blockLengthLineEdit.clear()
- self.window.blockNotesLineEdit.clear()
+ self.blocksw.widget().blockLengthLineEdit.clear()
+ self.blocksw.widget().blockNotesLineEdit.clear()
# necessary on Mojave with PyInstaller (or previous contents will be shown)
- self.window.blockLengthLineEdit.repaint()
- self.window.blockNotesLineEdit.repaint()
+ self.blocksw.widget().blockLengthLineEdit.repaint()
+ self.blocksw.widget().blockNotesLineEdit.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()
- self.window.blockChordComboBox.addItems(list(self.chordDict.keys()))
- def updateSectionDict(self):
- """
- Updates the dictionary used to generate the Section menu (on the block tab)
- """
- self.sectionDict = {s.name: s for s in self.doc.sectionList}
- self.window.blockSectionComboBox.clear()
- self.window.blockSectionComboBox.addItems(
- list(self.sectionDict.keys()))
- def removeChordAction(self):
- if self.window.chordTableView.selectionModel().hasSelection(): # check for selection
- self.updateChords()
- row = self.window.chordTableView.selectionModel().currentIndex().row()
- oldName = self.window.chordTableView.model.item(row, 0).text()
- self.doc.chordList.pop(row)
- self.window.chordTableView.populate(self.doc.chordList)
- # remove the chord if any of the blocks have it attached
- if self.currentSection is not None:
- for s in self.doc.sectionList:
- for b in s.blockList:
- if b.chord:
- if b.chord.name == oldName:
- b.chord = None
- self.window.blockTableView.populate(self.currentSection.blockList)
- self.clearChordLineEdits()
- self.updateChordDict()
- def addChordAction(self):
- success = False # initialise
- self.updateChords()
- cName = parseName(self.window.chordNameLineEdit.text())
- if cName:
- self.doc.chordList.append(Chord(cName))
- if self.window.guitarVoicingLineEdit.text() or self.window.pianoVoicingLineEdit.text():
- if self.window.guitarVoicingLineEdit.text():
- try:
- self.doc.chordList[-1].voicings['guitar'] = parseFingering(
- self.window.guitarVoicingLineEdit.text(), 'guitar')
- success = True # chord successfully parsed
- except Exception:
- VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
- if self.window.pianoVoicingLineEdit.text():
- try:
- self.doc.chordList[-1].voicings['piano'] = parseFingering(
- self.window.pianoVoicingLineEdit.text(), 'piano')
- success = True # chord successfully parsed
- except Exception:
- VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
- else:
- success = True # chord successfully parsed
- else:
- ChordNameWarningMessageBox().exec() # Chord has no name, warn user
-
- if success == True: # if chord was parsed properly
- self.window.chordTableView.populate(self.doc.chordList)
- self.clearChordLineEdits()
- self.updateChordDict()
-
- def updateChordAction(self):
- success = False # see comments above
- if self.window.chordTableView.selectionModel().hasSelection(): # check for selection
- self.updateChords()
- row = self.window.chordTableView.selectionModel().currentIndex().row()
- oldName = self.window.chordTableView.model.item(row, 0).text()
- cName = parseName(self.window.chordNameLineEdit.text())
- if cName:
- self.doc.chordList[row].name = cName
- if self.window.guitarVoicingLineEdit.text() or self.window.pianoVoicingLineEdit.text():
- if self.window.guitarVoicingLineEdit.text():
- try:
- self.doc.chordList[row].voicings['guitar'] = parseFingering(
- self.window.guitarVoicingLineEdit.text(), 'guitar')
- success = True # chord successfully parsed
- except Exception:
- VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
- if self.window.pianoVoicingLineEdit.text():
- try:
- self.doc.chordList[row].voicings['piano'] = parseFingering(
- self.window.pianoVoicingLineEdit.text(), 'piano')
- success = True # chord successfully parsed
- except Exception:
- VoicingWarningMessageBox().exec() # Voicing is malformed, warn user
- else:
- success = True # chord successfully parsed
- else:
- ChordNameWarningMessageBox().exec()
-
- if success == True:
- self.updateChordDict()
- self.window.chordTableView.populate(self.doc.chordList)
- # update the names of chords in all blocklists in case they've already been used
- for s in self.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.window.blockTableView.populate(self.currentSection.blockList)
- self.clearChordLineEdits()
-
- def removeSectionAction(self):
- if self.window.sectionTableView.selectionModel().hasSelection(): # check for selection
- self.updateSections()
-
- row = self.window.sectionTableView.selectionModel().currentIndex().row()
- self.doc.sectionList.pop(row)
-
- self.window.sectionTableView.populate(self.doc.sectionList)
- self.clearSectionLineEdits()
- self.updateSectionDict()
-
- def addSectionAction(self):
- self.updateSections()
-
- sName = self.window.sectionNameLineEdit.text()
- if sName and sName not in [s.name for s in self.doc.sectionList]:
- self.doc.sectionList.append(Section(name=sName))
- self.window.sectionTableView.populate(self.doc.sectionList)
- self.clearSectionLineEdits()
- self.updateSectionDict()
- else:
- # Section has no name or non unique, warn user
- SectionNameWarningMessageBox().exec()
-
- def updateSectionAction(self):
- if self.window.sectionTableView.selectionModel().hasSelection(): # check for selection
- self.updateSections()
- row = self.window.sectionTableView.selectionModel().currentIndex().row()
-
- sName = self.window.sectionNameLineEdit.text()
- if sName and sName not in [s.name for s in self.doc.sectionList]:
- self.doc.sectionList[row].name = sName
- self.window.sectionTableView.populate(self.doc.sectionList)
- self.clearSectionLineEdits()
- self.updateSectionDict()
- else:
- # Section has no name or non unique, warn user
- SectionNameWarningMessageBox().exec()
-
- def removeBlockAction(self):
- if self.window.blockTableView.selectionModel().hasSelection(): # check for selection
- self.updateBlocks(self.currentSection)
- row = self.window.blockTableView.selectionModel().currentIndex().row()
- self.currentSection.blockList.pop(row)
+class DocumentWindow(QMdiSubWindow):
+ def __init__(self, doc, style, filename):
+ super().__init__()
+
+ self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'document.ui')))
+
+ self.doc = doc
+ self.style = style
+ self.renderer = Renderer(self.doc, self.style)
- self.window.blockTableView.populate(self.currentSection.blockList)
+ self.lastDoc = copy(self.doc)
+ self.currentFilePath = filename
+
+ mw.updateChordDict(self.doc)
+ mw.updateSectionDict(self.doc)
+ self.currentSection = None
+
+ def UIFileLoader(self, ui_file):
+ ui_file = QFile(ui_file)
+ ui_file.open(QFile.ReadOnly)
- def addBlockAction(self):
- self.updateBlocks(self.currentSection)
+ self.setWidget(uic.loadUi(ui_file))
+ ui_file.close()
- try:
- # can the value entered for block length be cast as a float
- bLength = float(self.window.blockLengthLineEdit.text())
- except Exception:
- bLength = False
+ @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.lastDoc = copy(self.doc)
+ mw.setPath("workingPath", self.currentFilePath)
+
+ mw.updateChordDict(self.doc)
+ mw.updateSectionDict(self.doc)
+ self.updateTitleBar()
+ self.updatePreview()
+
+ 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.updateTitleBar() # as we may have a new filename
+
+ 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 bLength: # create the block
- self.currentSection.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.currentSection.blockList)
- self.clearBlockLineEdits()
+ if self.lastDoc == self.doc:
+ return True
else:
- # show warning that length was not entered or in wrong format
- LengthWarningMessageBox().exec()
-
- def updateBlockAction(self):
- if self.window.blockTableView.selectionModel().hasSelection(): # check for selection
- self.updateBlocks(self.currentSection)
+ wantToSave = UnsavedMessageBox().exec()
- try:
- # can the value entered for block length be cast as a float
- bLength = float(self.window.blockLengthLineEdit.text())
- except Exception:
- bLength = False
+ 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)
+ return True
- row = self.window.blockTableView.selectionModel().currentIndex().row()
- if bLength:
- self.currentSection.blockList[row] = (Block(bLength,
- chord=self.chordDict[self.window.blockChordComboBox.currentText(
- )],
- notes=(self.window.blockNotesLineEdit.text() if not "" else None)))
- self.window.blockTableView.populate(
- self.currentSection.blockList)
- self.clearBlockLineEdits()
+ elif wantToSave == QMessageBox.Discard:
+ return True
+
else:
- LengthWarningMessageBox().exec()
-
- def generateAction(self):
- self.updateDocument()
- self.updatePreview()
+ return False
def updatePreview(self):
"""
@@ -699,33 +756,36 @@ class DocumentWindow(QMainWindow):
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.window.pdfArea.update(self.currentPreview)
+ self.widget().pdfArea.update_pdf(self.currentPreview)
def updateTitleBar(self):
"""
Update the application's title bar to reflect the current document.
"""
if self.currentFilePath:
- self.setWindowTitle(_version.appName + " – " +
- os.path.basename(self.currentFilePath))
+ self.widget().setWindowTitle(os.path.basename(self.currentFilePath))
else:
- self.setWindowTitle(_version.appName)
+ self.widget().setWindowTitle("Unsaved")
def updateChords(self):
"""
Update the chord list by reading the table.
"""
chordTableList = []
- for i in range(self.window.chordTableView.model.rowCount()):
+ for i in range(mw.chordsw.widget().chordTableView.model.rowCount()):
chordTableList.append(
- Chord(parseName(self.window.chordTableView.model.item(i, 0).text()))),
- if self.window.chordTableView.model.item(i, 1).text():
+ 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(
- self.window.chordTableView.model.item(i, 1).text(), 'guitar')
- if self.window.chordTableView.model.item(i, 2).text():
+ 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(
- self.window.chordTableView.model.item(i, 2).text(), 'piano')
+ mw.chordsw.widget().chordTableView.model.item(i, 2).text(), 'piano')
self.doc.chordList = chordTableList
@@ -748,9 +808,9 @@ class DocumentWindow(QMainWindow):
Update the section list by reading the table
"""
sectionTableList = []
- for i in range(self.window.sectionTableView.model.rowCount()):
+ for i in range(mw.sectionsw.widget().sectionTableView.model.rowCount()):
sectionTableList.append(self.matchSection(
- self.window.sectionTableView.model.item(i, 0).text()))
+ mw.sectionsw.widget().sectionTableView.model.item(i, 0).text()))
self.doc.sectionList = sectionTableList
@@ -762,53 +822,52 @@ class DocumentWindow(QMainWindow):
BlockMustHaveSectionWarningMessageBox().exec()
else:
blockTableList = []
- for i in range(self.window.blockTableView.model.rowCount()):
+ for i in range(mw.blocksw.widget().blockTableView.model.rowCount()):
blockLength = float(
- self.window.blockTableView.model.item(i, 1).text())
- blockChord = self.chordDict[(self.window.blockTableView.model.item(
- i, 0).text() if self.window.blockTableView.model.item(i, 0).text() else "None")]
- blockNotes = self.window.blockTableView.model.item(i, 2).text(
- ) if self.window.blockTableView.model.item(i, 2).text() else None
+ 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 = self.window.titleLineEdit.text(
+ self.doc.title = mw.docinfo.widget().titleLineEdit.text(
) # Title can be empty string but not None
- self.doc.subtitle = (self.window.subtitleLineEdit.text(
- ) if self.window.subtitleLineEdit.text() else None)
- self.doc.composer = (self.window.composerLineEdit.text(
- ) if self.window.composerLineEdit.text() else None)
- self.doc.arranger = (self.window.arrangerLineEdit.text(
- ) if self.window.arrangerLineEdit.text() else None)
- self.doc.tempo = (self.window.tempoLineEdit.text()
- if self.window.tempoLineEdit.text() else None)
- self.doc.timeSignature = int(self.window.timeSignatureSpinBox.value(
- )) if self.window.timeSignatureSpinBox.value() else self.doc.timeSignature
-
- self.style.pageSize = pageSizeDict[self.pageSizeSelected]
- self.style.unit = unitDict[self.unitSelected]
- self.style.leftMargin = float(self.window.leftMarginLineEdit.text(
- )) if self.window.leftMarginLineEdit.text() else self.style.leftMargin
- self.style.rightMargin = float(self.window.rightMarginLineEdit.text(
- )) if self.window.rightMarginLineEdit.text() else self.style.rightMargin
- self.style.topMargin = float(self.window.topMarginLineEdit.text(
- )) if self.window.topMarginLineEdit.text() else self.style.topMargin
- self.style.bottomMargin = float(self.window.bottomMarginLineEdit.text(
- )) if self.window.bottomMarginLineEdit.text() else self.style.bottomMargin
- self.style.lineSpacing = float(self.window.lineSpacingDoubleSpinBox.value(
- )) if self.window.lineSpacingDoubleSpinBox.value() else self.style.lineSpacing
+ 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 self.window.beatWidthLineEdit.text():
- if (self.style.pageSize[0] - 2 * self.style.leftMargin * mm) >= (float(self.window.beatWidthLineEdit.text()) * 2 * self.doc.timeSignature * mm):
+ if 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(
- self.window.beatWidthLineEdit.text())
+ mw.psetup.widget().beatWidthLineEdit.text())
else:
maxBeatWidth = (
self.style.pageSize[0] - 2 * self.style.leftMargin * mm) / (2 * self.doc.timeSignature * mm)
@@ -818,201 +877,18 @@ class DocumentWindow(QMainWindow):
# update chords, sections, blocks
self.updateChords()
self.updateSections()
- if self.currentSection:
- self.updateBlocks(self.currentSection)
+ if mw.currentSection:
+ self.updateBlocks(mw.currentSection)
self.style.font = (
'FreeSans' if self.style.useIncludedFont else 'HelveticaNeue')
# something for the font box here
-
-class GuitarDialog(QDialog):
- """
- Dialogue to allow the user to enter a guitar chord voicing. Not particularly advanced at present!
- May be extended in future.
- """
-
- def __init__(self):
- super().__init__()
- self.UIFileLoader(
- str(os.path.join(scriptDir, 'ui', 'guitardialog.ui')))
-
- def UIFileLoader(self, ui_file):
- ui_file = QFile(ui_file)
- ui_file.open(QFile.ReadOnly)
-
- self.dialog = uic.loadUi(ui_file)
- 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(),
- self.dialog.DLineEdit.text(),
- self.dialog.GLineEdit.text(),
- self.dialog.BLineEdit.text(),
- self.dialog.eLineEdit.text()]
- resultJoined = ",".join(result)
- return resultJoined
- else:
- return None
-
-
-class AboutDialog(QDialog):
- """
- Dialogue showing information about the program.
- """
-
- def __init__(self):
- super().__init__()
- self.UIFileLoader(str(os.path.join(scriptDir, 'ui', 'aboutdialog.ui')))
-
- icon = QImage(str(os.path.join(scriptDir, 'ui', 'icon.png')))
- self.dialog.iconLabel.setPixmap(QPixmap.fromImage(icon).scaled(self.dialog.iconLabel.width(
- ), self.dialog.iconLabel.height(), Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation))
-
- self.dialog.versionLabel.setText("Version " + _version.version)
-
- self.dialog.exec()
-
- def UIFileLoader(self, ui_file):
- ui_file = QFile(ui_file)
- ui_file.open(QFile.ReadOnly)
-
- self.dialog = uic.loadUi(ui_file)
- ui_file.close()
-
-
-class UnsavedMessageBox(QMessageBox):
- """
- Message box to alert the user of unsaved changes and allow them to choose how to act.
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Question)
- self.setWindowTitle("Unsaved changes")
- self.setText("The document has been modified.")
- self.setInformativeText("Do you want to save your changes?")
- self.setStandardButtons(
- QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
- self.setDefaultButton(QMessageBox.Save)
-
-
-class UnreadableMessageBox(QMessageBox):
- """
- Message box to warn the user that the chosen file cannot be opened.
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("File 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)
- self.setDefaultButton(QMessageBox.Ok)
-
-
-class ChordNameWarningMessageBox(QMessageBox):
- """
- Message box to warn the user that a chord must have a name
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("Unnamed chord")
- self.setText("Chords must have a name.")
- self.setInformativeText("Please give your chord a name and try again.")
- self.setStandardButtons(QMessageBox.Ok)
- self.setDefaultButton(QMessageBox.Ok)
-
-
-class SectionNameWarningMessageBox(QMessageBox):
- """
- Message box to warn the user that a section must have a name
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("Unnamed section")
- self.setText("Sections must have a unique name.")
- self.setInformativeText(
- "Please give your section a unique name and try again.")
- self.setStandardButtons(QMessageBox.Ok)
- self.setDefaultButton(QMessageBox.Ok)
-
-
-class BlockMustHaveSectionWarningMessageBox(QMessageBox):
- """
- Message box to warn the user that a block must belong to a section
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("No sections found")
- self.setText("Each block must belong to a section, but no sections have yet been created.")
- self.setInformativeText(
- "Please create a section before adding blocks.")
- self.setStandardButtons(QMessageBox.Ok)
- self.setDefaultButton(QMessageBox.Ok)
-
-
-class VoicingWarningMessageBox(QMessageBox):
- """
- Message box to warn the user that the voicing entered could not be parsed
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("Malformed voicing")
- self.setText(
- "The voicing you entered was not understood and has not been applied.")
- self.setInformativeText(
- "Please try re-entering it in the correct format.")
- self.setStandardButtons(QMessageBox.Ok)
- self.setDefaultButton(QMessageBox.Ok)
-
-
-class LengthWarningMessageBox(QMessageBox):
- """
- Message box to warn the user that a block must have a length
- """
-
- def __init__(self):
- super().__init__()
-
- self.setIcon(QMessageBox.Warning)
- self.setWindowTitle("Block without valid length")
- self.setText("Blocks must have a length.")
- self.setInformativeText(
- "Please enter a valid length for your block and try again.")
- self.setStandardButtons(QMessageBox.Ok)
- self.setDefaultButton(QMessageBox.Ok)
-
-
if __name__ == '__main__':
app = QApplication(sys.argv)
- d = Document()
- s = Style()
-
# pass first argument as filename
- w = DocumentWindow(d, s, filename=(
- sys.argv[1] if len(sys.argv) > 1 else None))
- w.show()
+ mw = MainWindow()
+ mw.show()
sys.exit(app.exec_())
diff --git a/preview.pdf b/preview.pdf
new file mode 100644
index 0000000..b51a7e9
Binary files /dev/null and b/preview.pdf differ
diff --git a/ui/blocks.ui b/ui/blocks.ui
new file mode 100644
index 0000000..82ab355
--- /dev/null
+++ b/ui/blocks.ui
@@ -0,0 +1,225 @@
+
+
+ blocksWidget
+
+
+
+ 0
+ 0
+ 437
+ 371
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+ BlockTableView
+ QTableView
+
+
+
+ MComboBox
+ QComboBox
+
+
+
+
+ blockSectionComboBox
+ blockTableView
+ blockLengthLineEdit
+ blockChordComboBox
+ blockNotesLineEdit
+ removeBlockButton
+ updateBlockButton
+ addBlockButton
+
+
+
+
diff --git a/ui/chords.ui b/ui/chords.ui
new file mode 100644
index 0000000..7f64fe4
--- /dev/null
+++ b/ui/chords.ui
@@ -0,0 +1,190 @@
+
+
+ chordsWidget
+
+
+
+ 0
+ 0
+ 443
+ 359
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+ ChordTableView
+ QTableView
+
+
+
+
+ chordTableView
+ chordNameLineEdit
+ guitarVoicingLineEdit
+ guitarVoicingButton
+ pianoVoicingLineEdit
+ removeChordButton
+ updateChordButton
+ addChordButton
+
+
+
+
diff --git a/ui/docinfo.ui b/ui/docinfo.ui
new file mode 100644
index 0000000..3f5b625
--- /dev/null
+++ b/ui/docinfo.ui
@@ -0,0 +1,144 @@
+
+
+ docInfoWidget
+
+
+ Qt::NonModal
+
+
+
+ 0
+ 0
+ 400
+ 202
+
+
+
+
+ 0
+ 0
+
+
+
+ Document information
+
+
+ -
+
+
+ QFormLayout::ExpandingFieldsGrow
+
+
-
+
+
+ Title
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Subtitle
+
+
+
+ -
+
+
+ -
+
+
+ Composer
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Arranger
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Tempo
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+
+ -
+
+
+ Time
+
+
+
+ -
+
+
+
+ 40
+ 16777215
+
+
+
+
+
+
+ 4
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/document.ui b/ui/document.ui
new file mode 100644
index 0000000..9ed6722
--- /dev/null
+++ b/ui/document.ui
@@ -0,0 +1,57 @@
+
+
+ docWindow
+
+
+
+ 0
+ 0
+ 424
+ 324
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 400
+ 300
+
+
+
+
+
+
+
+
+ PDFViewer
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/ui/new.ui b/ui/new.ui
new file mode 100644
index 0000000..60a8b94
--- /dev/null
+++ b/ui/new.ui
@@ -0,0 +1,172 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1061
+ 659
+
+
+
+ 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
+
+
+
+
+
+
diff --git a/ui/pdfarea.ui b/ui/pdfarea.ui
new file mode 100644
index 0000000..ce109c2
--- /dev/null
+++ b/ui/pdfarea.ui
@@ -0,0 +1,32 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ PDF Viewer
+
+
+ -
+
+
+
+
+
+
+ PDFViewer
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/ui/preview.ui b/ui/preview.ui
new file mode 100644
index 0000000..24e543a
--- /dev/null
+++ b/ui/preview.ui
@@ -0,0 +1,52 @@
+
+
+ previewPanel
+
+
+
+ 0
+ 0
+ 400
+ 40
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 40
+
+
+
+ Preview
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Update preview
+
+
+
+
+
+
+
+
diff --git a/ui/psetup.ui b/ui/psetup.ui
new file mode 100644
index 0000000..5ca2c8e
--- /dev/null
+++ b/ui/psetup.ui
@@ -0,0 +1,273 @@
+
+
+ psetupWidget
+
+
+
+ 0
+ 0
+ 400
+ 500
+
+
+
+
+ 0
+ 0
+
+
+
+ Page setup
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Page options
+
+
+
-
+
+
-
+
+
+ Page size
+
+
+
+ -
+
+
+ -
+
+
+ Document units
+
+
+
+ -
+
+
+ -
+
+
+ Left margin
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+
+ -
+
+
+ Top margin
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+
+ -
+
+
+ Right margin
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+
+ -
+
+
+ Bottom margin
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Font options
+
+
+
-
+
+
-
+
+
+
+ 40
+ 16777215
+
+
+
+ Font
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
+ Use included FreeSans
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Text options
+
+
+
-
+
+
-
+
+
+ Line spacing
+
+
+
+ -
+
+
+
+ 70
+ 0
+
+
+
+
+ 70
+ 16777215
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Block options
+
+
+
-
+
+
-
+
+
+ Beat width
+
+
+
+ -
+
+
+
+ 60
+ 16777215
+
+
+
+
+
+
+
+
+
+
+
+
+ pageSizeComboBox
+ documentUnitsComboBox
+ leftMarginLineEdit
+ rightMarginLineEdit
+ topMarginLineEdit
+ bottomMarginLineEdit
+ lineSpacingDoubleSpinBox
+ fontComboBox
+ includedFontCheckBox
+ beatWidthLineEdit
+
+
+
+
diff --git a/ui/sections.ui b/ui/sections.ui
new file mode 100644
index 0000000..9280c89
--- /dev/null
+++ b/ui/sections.ui
@@ -0,0 +1,127 @@
+
+
+ sectionsWidget
+
+
+
+ 0
+ 0
+ 431
+ 325
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+ SectionTableView
+ QTableView
+
+
+
+
+
+
diff --git a/version.rc b/version.rc
index 07aba0e..85a8fc9 100644
--- a/version.rc
+++ b/version.rc
@@ -6,8 +6,8 @@ VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
-filevers=(0, 4, 6, 0),
-prodvers=(0, 4, 6, 0),
+filevers=(0, 5, 0, 0),
+prodvers=(0, 5, 0, 0),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
@@ -31,12 +31,12 @@ StringFileInfo(
u'040904B0',
[StringStruct(u'CompanyName', u'Ivan Holmes'),
StringStruct(u'FileDescription', u'Chordsheet'),
- StringStruct(u'FileVersion', u'0.4.6'),
+ StringStruct(u'FileVersion', u'0.5.0'),
StringStruct(u'InternalName', u'Chordsheet'),
StringStruct(u'LegalCopyright', u'Copyright (c) Ivan Holmes, 2020. Some rights reserved.'),
StringStruct(u'OriginalFilename', u'chordsheet.exe'),
StringStruct(u'ProductName', u'Chordsheet'),
- StringStruct(u'ProductVersion', u'0.4.6')])
+ StringStruct(u'ProductVersion', u'0.5.0')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]