48 Commits
0.4 ... master

Author SHA1 Message Date
Ivan Holmes 70e2d4e7d3 remove debug print statements and make bg grey 2 years ago
Ivan Holmes de4b2bfe3d slightly improve pagesetup ui 3 years ago
Ivan Holmes 6fc46a46b8 change style of save warning dialog buttons on Haiku for better integration 3 years ago
Ivan Holmes 6f3da3b2c4 change icons on messageboxes to info rather than warning 3 years ago
Ivan Holmes 1d313f420e merge remote changes 3 years ago
Ivan Holmes 44147406c8 fix save warning crash on new document and change save warning dialog icon 3 years ago
Ivan Holmes 41a67c5e55 fix the fix and add vertical scroll position hack 3 years ago
Ivan Holmes c9a93b5e93 fix for flashing/cut off preview and pagesetup ui slimmed 3 years ago
Ivan Holmes d31a926f92 get MDI working properly, fix some longstanding bugs 3 years ago
Ivan Holmes 699312cb5c Update linux.spec to add pyqt5.sip hidden import 3 years ago
Ivan Holmes ce3ed11f12 add MDI 3 years ago
Ivan Holmes 022112fba5 Merge readme Merge branch 'master' of git.radivan.net:ivan/chordsheet 4 years ago
Ivan Holmes fcf1e72c96 MDI pretty much working 4 years ago
Ivan Holmes d315f13868 Update the readme 4 years ago
Ivan Holmes 6e9ab42997 update version 4 years ago
Ivan Holmes b340c1e64d fix crash on add block with no sections 4 years ago
Ivan Holmes 058d89cff6 actually fix 4 years ago
Ivan Holmes 7b2b793006 hopefully fix remove chord crash 4 years ago
Ivan Holmes db2d30f738 Fix top note of piano chord not being rendered 4 years ago
Ivan Holmes 7a5a89644f Fix control order in UI and clarify error messages 4 years ago
Ivan Holmes 5a078a1bca increment version 4 years ago
Ivan Holmes dc955cdf7c Fix crash on saving new file (severe) 4 years ago
Ivan Holmes 1e54f4c05a Update Linux spec file 4 years ago
Ivan Holmes b8aa49465a Edit win spec file once more 4 years ago
Ivan Holmes 5aafc6f604 Edit windows spec file 4 years ago
Ivan Holmes 9f1ad34719 Update readme (minor fix) 4 years ago
Ivan Holmes aa4228f436 Update readme 4 years ago
Ivan Holmes eb55561f31 Fix link in about window 4 years ago
Ivan Holmes f52e9679d6 Update to 0.4.2 4 years ago
Ivan Holmes 25779ddbbb Update readme 4 years ago
Ivan Holmes 1ce8757de4 fix 'fontName' issue 5 years ago
Ivan Holmes 67ed03f992 disable UPX for mac 5 years ago
Ivan Holmes ade2228bc7 fix save now that .cma files are supported 5 years ago
Ivan Holmes 0c556d098f update windows spec file 5 years ago
Ivan Holmes 0434fae8cf fix small spelling mistake 5 years ago
Ivan Holmes 522dfd5d1d update version.rc again 5 years ago
Ivan Holmes a2b173b2b6 update version.rc.template to actually work 5 years ago
Ivan Holmes f22fe5c33d add version.rc 5 years ago
Ivan Holmes 3e071312a0 script to generate version.rc for windows 5 years ago
Ivan Holmes 62fbbcb505 update windows spec file 5 years ago
Ivan Holmes dd6cda5348 update mac builld script to add version number 5 years ago
Ivan Holmes 73372066cc increment version number 5 years ago
Ivan Holmes 5aac931f51 add read support for simple macro language 5 years ago
Ivan Holmes 5291b6e404 add read support for simple macro language 5 years ago
Ivan Holmes c9c45f66d5 add space before credits 5 years ago
Ivan Holmes 1d526d3a09 strip whitespace off entered fingering notes 5 years ago
Ivan Holmes 3fa216a149 update heading style to make sure it's attached 5 years ago
Ivan Holmes ce3d79bb6c update piano chord wrapping 5 years ago
  1. 3
      .gitignore
  2. 30
      README.md
  3. 2
      _version.py
  4. 7
      chordsheet/common.py
  5. 111
      chordsheet/document.py
  6. 13
      chordsheet/parsers.py
  7. 56
      chordsheet/pdfViewer.py
  8. 24
      chordsheet/render.py
  9. 5
      chordsheet/rlStylesheet.py
  10. 43
      cli.py
  11. 0
      csgui/comboBox.py
  12. 81
      csgui/dialogs.py
  13. 124
      csgui/messageBox.py
  14. 64
      csgui/panels.py
  15. 96
      csgui/pdfViewer.py
  16. 27
      csgui/tableView.py
  17. 24
      examples/kissoflife.cma
  18. 2
      examples/kissoflife.xml
  19. 15
      generate_version_rc.py
  20. 1451
      gui.py
  21. 47
      linux.spec
  22. 17
      mac.spec
  23. 4
      requirements.txt
  24. 15
      test.py
  25. 22
      ui/aboutdialog.ui
  26. 291
      ui/blocks.ui
  27. 237
      ui/chords.ui
  28. 162
      ui/docinfo.ui
  29. 57
      ui/document.ui
  30. 1076
      ui/mainwindow.ui
  31. 83
      ui/preview.ui
  32. 326
      ui/psetup.ui
  33. 165
      ui/sections.ui
  34. 43
      version.rc
  35. 43
      version.rc.template
  36. 19
      win.spec

3
.gitignore

@ -52,3 +52,6 @@ docs/_build/
# Mac stuff # Mac stuff
.DS_Store .DS_Store
.vscode/ .vscode/
# Syncthing conflicts
*.sync-conflict-*

30
README.md

@ -1,26 +1,32 @@
# Chordsheet # Chordsheet
Chordsheet is a piece of software that generates a chord sheet, with a simple GUI. Chordsheet is a piece of software that generates a chord sheet, with a simple GUI.
It can load and save chordsheets in its own XML-based format, and can save them in PDF format for printing. It can load and save chordsheets in its own XML-based format, and can save them in PDF format for printing.
It's now possible to write a chordsheet in a custom macro language, and import this for further editing in the GUI. See the examples folder for details.
I wrote Chordsheet because no other software offered what I was looking for. I did not want to have to enter lyrics (most of the music I am charting is instrumental anyway), needed definite timing information, and wanted to be able to represent guitar chords (with a view to supporting other instruments in future).
I wrote Chordsheet because no other software offered what I was looking for. I did not want to have to enter lyrics (most of the music I am charting is instrumental anyway), needed definite timing information, and wanted to be able to represent guitar and piano chords (with a view to supporting other instruments in future).
Chordsheet works on a system of blocks, each with a certain length. Blocks may have an assigned chord, or additional information. These blocks can then be ordered and the output generated. Chordsheet works on a system of blocks, each with a certain length. Blocks may have an assigned chord, or additional information. These blocks can then be ordered and the output generated.
## Get started ## Get started
To run Chordsheet, go to the Releases tab and download the most recent version for your OS. Releases are currently provided for macOS and Windows. These binaries are created with PyInstaller and so can't be used for development. You do not need to install anything else to run Chordsheet this way.
To run Chordsheet, go to the Releases tab and download the most recent version for your OS. Releases are currently provided for macOS, Linux and Windows. These binaries are created with PyInstaller and so can't be used for development. You do not need to install anything else to run Chordsheet this way.
To develop Chordsheet, clone this repository and run gui.py using a recent Python 3 interpreter. Make sure you have the dependencies installed!
To develop Chordsheet, clone this repository and run gui.py using Python 3.7 or newer. Make sure you have the dependencies installed!
## Current status ## Current status
Chordsheet is alpha-grade software. At present, the program will crash readily given user input it doesn't expect.
Chordsheet is alpha-grade software. Most things should work, but I haven't tested it too extensively.
### Features
- Guitar and piano chords can be entered and rendered
- Multiple sections of the same piece are supported
- Supports reading Chordsheet macro files (custom macro language designed to make it easy to quickly generate chordsheets, see examples folder)
- High quality PDF output
### Limitations ### Limitations
- Only guitar chords can be entered and shown
- No support for lyrics or melody (use something else!) - No support for lyrics or melody (use something else!)
- PDF preview is blurry on high DPI monitors - PDF preview is blurry on high DPI monitors
- Chord names and notes can spill out of their block if it's not big enough (partially remedied by allowing the user to change the beat width) - Chord names and notes can spill out of their block if it's not big enough (partially remedied by allowing the user to change the beat width)
- Poor font handling (choice of either FreeSans or Helvetica Neue if installed) - Poor font handling (choice of either FreeSans or Helvetica Neue if installed)
- No support for printing
- No internal support for printing
## Dependencies ## Dependencies
Chordsheet depends on pymupdf (to show the preview), reportlab (to generate the PDF), and PyQt5 (for the GUI). Chordsheet depends on pymupdf (to show the preview), reportlab (to generate the PDF), and PyQt5 (for the GUI).
@ -30,5 +36,17 @@ This command should sort you out:
pip3 install pymupdf reportlab pyqt5 pip3 install pymupdf reportlab pyqt5
``` ```
Note that if you have downloaded a binary from the Releases tab, these libraries are included already so you don't need to install them separately.
## Building (freezing)
To build the binary distribution using PyInstaller, simply run:
```bash
pyinstaller {mac|linux|win}.spec
```
The generated binary will be in the 'dist' subfolder.
### Version numbering on Windows
If you are building on Windows and alter the version number, make sure to run `generate_version_rc.py` to update the version.rc file. This file is included in the generated executable and contains the version information viewable in Windows Explorer properties.
## License ## License
Chordsheet is licensed under the AGPLv3, included in full in 'LICENSE'. Chordsheet is licensed under the AGPLv3, included in full in 'LICENSE'.

2
_version.py

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

7
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))

111
chordsheet/document.py

@ -7,7 +7,6 @@ from reportlab.lib.pagesizes import A4
defaultTimeSignature = 4 defaultTimeSignature = 4
class Style: class Style:
def __init__(self, **kwargs): def __init__(self, **kwargs):
# set up the style using sane defaults # set up the style using sane defaults
@ -119,7 +118,7 @@ class Document:
blockChord = c blockChord = c
break break
if blockChord is None: if blockChord is None:
exit("Chord {c} does not match any chord in {l}.".format(
raise ValueError("Chord {c} does not match any chord in {l}.".format(
c=blockChordName, l=self.chordList)) c=blockChordName, l=self.chordList))
else: else:
blockChord = None blockChord = None
@ -144,15 +143,7 @@ class Document:
self.tempo = (root.find('tempo').text if root.find( self.tempo = (root.find('tempo').text if root.find(
'tempo') is not None else None) 'tempo') is not None else None)
def newFromXML(filepath):
"""
Create a new Document object directly from an XML file.
"""
doc = Document()
doc.loadXML(filepath)
return doc
def saveXML(self, filepath):
def toXML(self):
""" """
Write the contents of the Document object to an XML file. Write the contents of the Document object to an XML file.
""" """
@ -199,4 +190,102 @@ class Document:
ET.SubElement(blockElement, "notes").text = b.notes ET.SubElement(blockElement, "notes").text = b.notes
tree = ET.ElementTree(root) tree = ET.ElementTree(root)
return tree
def saveXML(self, filepath):
tree = self.toXML()
tree.write(filepath) 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):
"""
Read a Chordsheet Macro file and import its contents.
"""
self.chordList = []
self.sectionList = []
aliasTable = {}
def chord(args):
argList = args.split(" ")
chordName = argList.pop(0)
self.chordList.append(Chord(parseName(chordName)))
argIter = iter(argList)
subCmdsArgs = list(zip(argIter, argIter))
for subCmd, arg in subCmdsArgs:
if subCmd == "alias":
aliasTable[arg] = chordName
else:
self.chordList[-1].voicings[subCmd] = parseFingering(arg, subCmd)
def section(args):
blockList = []
sectionName, blocks = [arg.strip() for arg in args.split("\n", 1)]
self.sectionList.append(Section(name=sectionName))
for b in blocks.split():
blockParams = b.split(",")
blockLength = float(blockParams[1])
if blockParams[0] in aliasTable:
blockChordName = aliasTable[blockParams[0]]
else:
blockChordName = blockParams[0]
blockChordName = parseName(blockChordName) if blockChordName not in ["NC", "X"] else None
blockChord = None
if blockChordName:
for c in self.chordList:
if c.name == blockChordName:
blockChord = c
break
if blockChord is None:
raise ValueError("Chord {c} does not match any chord in {l}.".format(
c=blockChordName, l=self.chordList))
blockList.append(Block(blockLength, chord=blockChord))
self.sectionList[-1].blockList = blockList
with open(filepath, 'r') as f:
cmatext = f.read()
cmaCmdsArgs = [statement.split(" ", 1) for statement in \
(rawStatement.strip() for rawStatement in cmatext.split("\\")[1:])]
for cmd, args in cmaCmdsArgs:
if cmd == "chordsheet":
# There's only one version so no need to do anything with this
pass
elif cmd == "title":
self.title = args
elif cmd == "subtitle":
self.subtitle = args
elif cmd == "arranger":
self.arranger = args
elif cmd == "composer":
self.composer = args
elif cmd == "timesig":
self.timeSignature = int(args)
elif cmd == "tempo":
self.tempo = args
elif cmd == "chord":
chord(args)
elif cmd == "section":
section(args)
elif cmd in ["!", "rem"]:
# Simply ignore comments
pass
else:
raise ValueError(f"Command {cmd} not understood.")

13
chordsheet/parsers.py

@ -7,16 +7,15 @@ def parseFingering(fingering, instrument):
""" """
if instrument == 'guitar': if instrument == 'guitar':
numStrings = 6 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) output = list(fingering)
else: # if entered in long format e.g. x,x,10,10,11,11 else: # if entered in long format e.g. x,x,10,10,11,11
output = fingering.split(",")
if len(output) == numStrings:
return output
else:
raise Exception("Voicing <{}> is malformed.".format(fingering))
output = [f.strip() for f in fingering.split(",")]
if len(output) != numStrings:
print("Voicing <{}> is malformed.".format(fingering))
return output
elif instrument == 'piano': elif instrument == 'piano':
return [parseName(note).upper() for note in fingering.split(",")]
return [parseName(note).upper().strip() for note in fingering.split(",")]
else: else:
return [fingering] return [fingering]

56
chordsheet/pdfViewer.py

@ -1,56 +0,0 @@
from PyQt5.QtWidgets import QScrollArea, QLabel, QVBoxLayout, QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QImage
import fitz
class PDFViewer(QScrollArea):
def __init__(self, parent):
super().__init__(parent)
self.scrollAreaContents = QWidget()
self.scrollAreaLayout = QVBoxLayout()
self.setWidget(self.scrollAreaContents)
self.setWidgetResizable(True)
self.scrollAreaContents.setLayout(self.scrollAreaLayout)
self.pixmapList = []
def resizeEvent(self, event):
pass
# do something about this later
def update(self, pdf):
self.render(pdf)
self.clear()
self.show()
def render(self, pdf):
"""
Update the preview shown by rendering a new PDF and drawing it to the scroll area.
"""
self.pixmapList = []
pdfView = fitz.Document(stream=pdf, filetype='pdf')
# render at 4x resolution and scale
for page in pdfView:
self.pixmapList.append(page.getPixmap(matrix=fitz.Matrix(4, 4), alpha=False))
def clear(self):
while self.scrollAreaLayout.count():
item = self.scrollAreaLayout.takeAt(0)
w = item.widget()
if w:
w.deleteLater()
def show(self):
for p in self.pixmapList:
label = QLabel(parent=self.scrollAreaContents)
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))
self.scrollAreaLayout.addWidget(label)
# necessary on Mojave with PyInstaller (or previous contents will be shown)
self.repaint()

24
chordsheet/render.py

@ -12,6 +12,7 @@ from reportlab.platypus import BaseDocTemplate, Spacer, Paragraph, Flowable, Fra
from chordsheet.document import Block from chordsheet.document import Block
from chordsheet.rlStylesheet import getStyleSheet from chordsheet.rlStylesheet import getStyleSheet
defaultSpacing = 1.15
def writeText(canvas, style, string, size, vpos, width, **kwargs): def writeText(canvas, style, string, size, vpos, width, **kwargs):
""" """
@ -49,7 +50,7 @@ class Tempo(Flowable):
self.tempo = tempo self.tempo = tempo
self.text = "♩ = {t} bpm".format(t=self.tempo) self.text = "♩ = {t} bpm".format(t=self.tempo)
self.fontSize = paraStyle.fontSize self.fontSize = paraStyle.fontSize
self.fontname = paraStyle.fontname
self.fontName = paraStyle.fontName
self.leading = paraStyle.leading self.leading = paraStyle.leading
def wrap(self, availWidth, availHeight): def wrap(self, availWidth, availHeight):
@ -59,7 +60,7 @@ class Tempo(Flowable):
def draw(self): def draw(self):
canvas = self.canv canvas = self.canv
canvas.setFont(self.fontname, self.fontSize)
canvas.setFont(self.fontName, self.fontSize)
canvas.drawString(0, self.leading * 0.25, self.text) canvas.drawString(0, self.leading * 0.25, self.text)
@ -122,7 +123,7 @@ class GuitarChart(Flowable):
for i in range(self.nStrings+1): # i is the string line currently being drawn 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-( 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) # j is which chord (0 is first chord, 1 is 2nd etc)
for j in range(len(stringList[-1])): for j in range(len(stringList[-1])):
@ -152,7 +153,7 @@ class GuitarChart(Flowable):
canvas.line(x, y, x+l, y) canvas.line(x, y, x+l, y)
writeText(canvas, self.style, stringList[i][j], fontsize, v_origin-( 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 lastWidth = currentWidth
@ -194,7 +195,7 @@ class PianoChart(Flowable):
widest = 0 widest = 0
for index, c in enumerate(self.pianoChordList): for index, c in enumerate(self.pianoChordList):
cKL, vL, fKN, iconWidth = self.calculate(c) cKL, vL, fKN, iconWidth = self.calculate(c)
if currentWidth + iconWidth >= availWidth + self.iconHzSpacing:
if currentWidth + iconWidth >= availWidth:
vUnits += 1 vUnits += 1
currentWidth = self.chartMargin currentWidth = self.chartMargin
else: else:
@ -262,10 +263,10 @@ class PianoChart(Flowable):
lastIndex = self.keyList.index(lastNote) lastIndex = self.keyList.index(lastNote)
curIndex = self.keyList.index(note) curIndex = self.keyList.index(note)
if curIndex > lastIndex:
if curIndex > lastIndex and curIndex != 11:
chartKeyList.extend( chartKeyList.extend(
self.keyList[lastIndex+1:((curIndex+1) % len(self.keyList))]) self.keyList[lastIndex+1:((curIndex+1) % len(self.keyList))])
elif curIndex < lastIndex:
elif curIndex < lastIndex or curIndex == 11:
chartKeyList.extend(self.keyList[lastIndex+1:]) chartKeyList.extend(self.keyList[lastIndex+1:])
chartKeyList.extend( chartKeyList.extend(
self.keyList[0:((curIndex+1) % len(self.keyList))]) self.keyList[0:((curIndex+1) % len(self.keyList))])
@ -458,7 +459,7 @@ class ChordProgression(Flowable):
if u == maxWidth: # Avoid writing beat number after the final line if u == maxWidth: # Avoid writing beat number after the final line
break break
writeText(canvas, self.style, str((u % self.timeSignature)+1), self.style.beatsFontSize, 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) parsedBlockList = self.wrapBlocks(self.blockList, maxWidth)
@ -470,12 +471,12 @@ class ChordProgression(Flowable):
b.length*unitWidth, self.unitHeight) b.length*unitWidth, self.unitHeight)
if b.notes is not None: if b.notes is not None:
writeText(canvas, self.style, b.notes, self.style.notesFontSize, v_origin-((v_loc+1)*self.unitHeight)+( 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) + v_offset = ((v_loc*self.unitHeight) +
self.unitHeight/2)-self.style.chordNameFontSize/2 self.unitHeight/2)-self.style.chordNameFontSize/2
if b.chord is not None: if b.chord is not None:
writeText(canvas, self.style, b.chord.name, self.style.chordNameFontSize, 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 h_loc += b.length
@ -517,6 +518,9 @@ class Renderer:
rlDocList.append( rlDocList.append(
Paragraph(self.document.subtitle, styles['Subtitle'])) Paragraph(self.document.subtitle, styles['Subtitle']))
if self.document.composer or self.document.arranger:
rlDocList.append(Spacer(0, 2*mm))
if self.document.composer: if self.document.composer:
rlDocList.append(Paragraph("Composer: {c}".format( rlDocList.append(Paragraph("Composer: {c}".format(
c=self.document.composer), styles['Credits'])) c=self.document.composer), styles['Credits']))

5
chordsheet/rlStylesheet.py

@ -10,7 +10,7 @@ def getStyleSheet(csStyle):
stylesheet = StyleSheet1() stylesheet = StyleSheet1()
stylesheet.add(ParagraphStyle(name='Master', stylesheet.add(ParagraphStyle(name='Master',
fontname=csStyle.font))
fontName=csStyle.font))
stylesheet.add(ParagraphStyle(name='Title', stylesheet.add(ParagraphStyle(name='Title',
leading=csStyle.lineSpacing*csStyle.titleFontSize, leading=csStyle.lineSpacing*csStyle.titleFontSize,
@ -44,7 +44,8 @@ def getStyleSheet(csStyle):
fontSize=csStyle.headingFontSize, fontSize=csStyle.headingFontSize,
alignment=TA_LEFT, alignment=TA_LEFT,
parent=stylesheet['Master'], parent=stylesheet['Master'],
spaceAfter=2*mm)
spaceAfter=2*mm,
keepWithNext=1)
) )
return stylesheet return stylesheet

43
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)

0
chordsheet/comboBox.py → csgui/comboBox.py

81
csgui/dialogs.py

@ -0,0 +1,81 @@
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, 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() 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:
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()

124
csgui/messageBox.py

@ -0,0 +1,124 @@
from PyQt5.QtWidgets import QApplication, QAction, QLabel, QDialogButtonBox, QDialog, QFileDialog, QMessageBox, QPushButton, QLineEdit, QCheckBox, QSpinBox, QDoubleSpinBox, QTableWidgetItem, QTabWidget, QComboBox, QWidget, QScrollArea, QMainWindow, QShortcut, QDialogButtonBox
import sys
class UnsavedMessageBox(QMessageBox):
"""
Message box to alert the user of unsaved changes and allow them to choose how to act.
"""
def __init__(self, fileName):
super().__init__()
self.setIcon(QMessageBox.Information)
self.setWindowTitle("Unsaved changes")
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)
self.setDefaultButton(QMessageBox.Save)
# If we are running on Haiku, use the MacOS button style to fit in more
# with native applications
if sys.platform.startswith("haiku"):
buttonBox = self.findChild(QDialogButtonBox)
buttonBox.setStyleSheet("* { button-layout: 1}")
class UnreadableMessageBox(QMessageBox):
"""
Message box to warn the user that the chosen file cannot be opened.
"""
def __init__(self, fileName):
super().__init__()
self.setIcon(QMessageBox.Information)
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)
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.Information)
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.Information)
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.Information)
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.Information)
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.Information)
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)

64
csgui/panels.py

@ -0,0 +1,64 @@
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, 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):
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()
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__()
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")

96
csgui/pdfViewer.py

@ -0,0 +1,96 @@
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)
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
# print(idealWidth)
pixSize = self.pixmap().size()
# print(pixSize)
# print((self.parent.scrollAreaContents.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)
self.scrollAreaContents = QWidget()
self.scrollAreaLayout = QVBoxLayout()
self.setWidget(self.scrollAreaContents)
self.setWidgetResizable(True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
# set a fixed background colour
# on some OSes (Mac, Haiku) it is grey automatically but not on KDE
self.setStyleSheet('PDFViewer {background-color: #D8D8D8}')
self.scrollAreaContents.setLayout(self.scrollAreaLayout)
self.pixmapList = []
self.lastVScrollPosition = None
def update_pdf(self, pdf):
self.render(pdf)
self.clear()
self.show()
def render(self, pdf):
"""
Update the preview shown by rendering a new PDF and drawing it to the scroll area.
"""
self.pixmapList = []
pdfView = fitz.Document(stream=pdf, filetype='pdf')
for page in pdfView:
# needs to be high enough for big monitors. 300 should do it...
self.pixmapList.append(page.get_pixmap(dpi=300, alpha=False))
def clear(self):
self.lastVScrollPosition = self.verticalScrollBar().value()
while self.scrollAreaLayout.count():
item = self.scrollAreaLayout.takeAt(0)
w = item.widget()
if w:
w.deleteLater()
def show(self):
for p in self.pixmapList:
label = PDFLabel(parent=self)
label.setAlignment(Qt.AlignHCenter)
qtimg = QImage(p.samples, p.width, p.height, p.stride, QImage.Format_RGB888)
label.setPixmap(QPixmap.fromImage(qtimg))
self.scrollAreaLayout.addWidget(label)
self.scrollAreaLayout.addStretch(1)
# Somewhat of a hack. Should really replace pixmaps in place
# rather than removing labels elsewhere in code.
if self.lastVScrollPosition:
self.verticalScrollBar().setValue(self.lastVScrollPosition)

27
chordsheet/tableView.py → csgui/tableView.py

@ -1,16 +1,35 @@
from PyQt5 import QtWidgets, QtGui from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import pyqtSignal, Qt, QModelIndex
class MItemModel(QtGui.QStandardItemModel): class MItemModel(QtGui.QStandardItemModel):
""" """
Special item model to ensure whole row is moved. 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): def dropMimeData(self, data, action, row, col, parent):
""" """
Always move the entire row, and don't allow column "shifting" 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): class MProxyStyle(QtWidgets.QProxyStyle):
@ -50,11 +69,13 @@ class MTableView(QtWidgets.QTableView):
self.setShowGrid(False) self.setShowGrid(False)
# self.setDragDropMode(self.InternalMove) # self.setDragDropMode(self.InternalMove)
# self.setDragDropOverwriteMode(False)
self.setDragDropOverwriteMode(False)
# Set our custom style - this draws the drop indicator across the whole row # Set our custom style - this draws the drop indicator across the whole row
self.setStyle(MProxyStyle()) self.setStyle(MProxyStyle())
def clear(self):
self.model.removeRows(0, self.model.rowCount())
class ChordTableView(MTableView): class ChordTableView(MTableView):
""" """

24
examples/kissoflife.cma

@ -0,0 +1,24 @@
\chordsheet 1
\rem This is a comment to say that the first line isn't really needed (as there's only one version of Chordsheet Macro so far)
\title Kiss of Life
\subtitle Sade
\arranger Ivan Holmes
\composer Sade Adu, Paul S. Denman, Andrew Hale, Stuart Matthewman
\timesig 4
\tempo 120
\chord AM9 piano A,B,C#,E
\chord F#m11 alias F piano A,B,C#,E
\chord DM7 piano A,C#,F#
\chord C#m7 piano G#,B,E
\chord Bm7 piano A,D,F#
\section Intro and verse
AM9,8 F#m11,8 DM7,1.5
C#m7,2 Bm7,4.5 F#m11,8
\! This is another comment. Comments may not exist in the middle of other statements.
\section Chorus and bridge
Bm7,3.5 F,4.5 Bm7,3.5 F#m11,4.5

2
examples/kissoflife.xml

@ -1 +1 @@
<chordsheet><title>Kiss of Life</title><subtitle>Sade</subtitle><arranger>Ivan Holmes</arranger><composer>Sade Adu, Paul S. Denman, Andrew Hale, Stuart Matthewman</composer><timesignature>4</timesignature><chords><chord><name>AM9</name><voicing instrument="piano">A,B,C&#9839;,E</voicing></chord><chord><name>F&#9839;m11</name><voicing instrument="piano">A,B,C&#9839;,E</voicing></chord><chord><name>DM7</name><voicing instrument="piano">A,C&#9839;,F&#9839;</voicing></chord><chord><name>C&#9839;m7</name><voicing instrument="piano">G&#9839;,B,E</voicing></chord><chord><name>Bm7</name><voicing instrument="piano">A,D,F&#9839;</voicing></chord></chords><section name="Intro and verse"><block><length>8.0</length><chord>AM9</chord></block><block><length>8.0</length><chord>F&#9839;m11</chord></block><block><length>1.5</length><chord>DM7</chord></block><block><length>2.0</length><chord>C&#9839;m7</chord></block><block><length>4.5</length><chord>Bm7</chord></block><block><length>8.0</length><chord>F&#9839;m11</chord></block></section><section name="Chorus and bridge"><block><length>3.5</length><chord>Bm7</chord></block><block><length>4.5</length><chord>F&#9839;m11</chord></block><block><length>3.5</length><chord>Bm7</chord></block><block><length>4.5</length><chord>F&#9839;m11</chord></block></section></chordsheet>
<chordsheet><title>Kiss of Life</title><subtitle>Sade</subtitle><arranger>Ivan Holmes</arranger><composer>Sade Adu, Paul S. Denman, Andrew Hale, Stuart Matthewman</composer><timesignature>4</timesignature><tempo>120</tempo><chords><chord><name>AM9</name><voicing instrument="piano">A,B,C&#9839;,E</voicing></chord><chord><name>F&#9839;m11</name><voicing instrument="piano">A,B,C&#9839;,E</voicing></chord><chord><name>DM7</name><voicing instrument="piano">A,C&#9839;,F&#9839;</voicing></chord><chord><name>C&#9839;m7</name><voicing instrument="piano">G&#9839;,B,E</voicing></chord><chord><name>Bm7</name><voicing instrument="piano">A,D,F&#9839;</voicing></chord></chords><section name="Intro and verse"><block><length>8.0</length><chord>AM9</chord></block><block><length>8.0</length><chord>F&#9839;m11</chord></block><block><length>1.5</length><chord>DM7</chord></block><block><length>2.0</length><chord>C&#9839;m7</chord></block><block><length>4.5</length><chord>Bm7</chord></block><block><length>8.0</length><chord>F&#9839;m11</chord></block></section><section name="Chorus and bridge"><block><length>3.5</length><chord>Bm7</chord></block><block><length>4.5</length><chord>F&#9839;m11</chord></block><block><length>3.5</length><chord>Bm7</chord></block><block><length>4.5</length><chord>F&#9839;m11</chord></block></section></chordsheet>

15
generate_version_rc.py

@ -0,0 +1,15 @@
import _version
with open('version.rc.template', 'r') as verstf:
verstemp = verstf.read()
major, minor, patch = _version.version.split(".")
verstemp = verstemp.replace("%APPNAME%", _version.appName)
verstemp = verstemp.replace("%VERSION%", _version.version)
verstemp = verstemp.replace("%MAJOR_VERSION%", major)
verstemp = verstemp.replace("%MINOR_VERSION%", minor)
verstemp = verstemp.replace("%PATCH_VERSION%", patch)
with open('version.rc', 'w') as versf:
versf.write(verstemp)

1451
gui.py
File diff suppressed because it is too large
View File

47
linux.spec

@ -0,0 +1,47 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
with open('_version.py', 'r') as versinfo:
exec(versinfo.read())
a = Analysis(['gui.py'],
pathex=['/home/ivan/Code/chordsheet'],
binaries=[],
datas=[
('fonts', 'fonts'),
('ui', 'ui')
],
hiddenimports=['PyQt5.sip'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='chordsheet',
icon='ui/icon.png',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
version='version.rc')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='chordsheet-'+version)

17
mac.spec

@ -1,7 +1,10 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
block_cipher = None
# Nasty hack to get the version number included automatically
with open('_version.py', 'r') as versinfo:
exec(versinfo.read())
block_cipher = None
a = Analysis(['/Users/ivan/Code/chordsheet/gui.py'], a = Analysis(['/Users/ivan/Code/chordsheet/gui.py'],
pathex=['/Users/ivan/Code/chordsheet'], pathex=['/Users/ivan/Code/chordsheet'],
@ -26,20 +29,22 @@ exe = EXE(pyz,
a.zipfiles, a.zipfiles,
a.datas, a.datas,
[], [],
name='Chordsheet',
name=appName,
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=True,
upx=False,
upx_exclude=[], upx_exclude=[],
runtime_tmpdir=None, runtime_tmpdir=None,
console=False ) console=False )
app = BUNDLE(exe, app = BUNDLE(exe,
name='Chordsheet.app',
name=exe.name + '.app',
icon='ui/icon.icns', icon='ui/icon.icns',
bundle_identifier=None,
bundle_identifier="uk.co.ivanholmes.chordsheet",
info_plist={ info_plist={
'CFBundleShortVersionString': version,
'NSPrincipalClass': 'NSApplication', 'NSPrincipalClass': 'NSApplication',
'NSHighResolutionCapable': 'True'
'NSHighResolutionCapable': 'True',
'NSHumanReadableCopyright': "© Ivan Holmes, 2020. Some rights reserved."
} }
) )

4
requirements.txt

@ -0,0 +1,4 @@
reportlab>=3.5.67
PyMuPDF>=1.18.13
PyQt5>=5.15.4
PyQt5.Sip

15
test.py

@ -1,15 +0,0 @@
import os
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from chordsheet.document import Document, Style
from chordsheet.render import Renderer
pdfmetrics.registerFont(TTFont('FreeSans', os.path.join('fonts', 'FreeSans.ttf')))
doc = Document.newFromXML('examples/test.xml')
style = Style(unitWidth=10)
ren = Renderer(doc, style)
ren.savePDF('test.pdf')

22
ui/aboutdialog.ui

@ -14,7 +14,7 @@
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -25,10 +25,25 @@
<height>200</height> <height>200</height>
</size> </size>
</property> </property>
<property name="maximumSize">
<size>
<width>436</width>
<height>200</height>
</size>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>About Chordsheet</string> <string>About Chordsheet</string>
</property> </property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item> <item>
<layout class="QVBoxLayout" name="leftPane"> <layout class="QVBoxLayout" name="leftPane">
<item> <item>
@ -89,7 +104,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:24pt; font-weight:600;&quot;&gt;Chordsheet&lt;/span&gt;&lt;br/&gt;by Ivan Holmes&lt;/p&gt;&lt;p&gt;Chordsheet is a piece of software that generates a chord sheet with a simple GUI. &lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/ivanholmes/chordsheet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Github&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:24pt; font-weight:600;&quot;&gt;Chordsheet&lt;/span&gt;&lt;br/&gt;by Ivan Holmes&lt;/p&gt;&lt;p&gt;Chordsheet is a piece of software that generates a chord sheet with a simple GUI. &lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git.radivan.net/ivan/chordsheet&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Git repository&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
@ -97,6 +112,9 @@
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>

291
ui/blocks.ui

@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>blocksWidget</class>
<widget class="QWidget" name="blocksWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>387</width>
<height>206</height>
</rect>
</property>
<property name="windowTitle">
<string>Blocks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout_5">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="blockSectionLabel">
<property name="text">
<string>Section</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="MComboBox" name="blockSectionComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="BlockTableView" name="blockTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::TargetMoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="blockGridLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="blockLengthLabel">
<property name="text">
<string>Length</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QComboBox" name="blockChordComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="blockNotesLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Notes</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="blockChordLabel">
<property name="text">
<string>Chord</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="blockLengthLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1" colspan="4">
<widget class="QLineEdit" name="blockNotesLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="bottomBlockHorizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="removeBlockButton">
<property name="text">
<string>Remove block</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="updateBlockButton">
<property name="text">
<string>Update block</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addBlockButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Add block</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BlockTableView</class>
<extends>QTableView</extends>
<header>csgui/tableView.h</header>
</customwidget>
<customwidget>
<class>MComboBox</class>
<extends>QComboBox</extends>
<header>csgui/comboBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>blockLengthLineEdit</tabstop>
<tabstop>blockChordComboBox</tabstop>
<tabstop>blockNotesLineEdit</tabstop>
<tabstop>removeBlockButton</tabstop>
<tabstop>updateBlockButton</tabstop>
<tabstop>addBlockButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

237
ui/chords.ui

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>chordsWidget</class>
<widget class="QWidget" name="chordsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>393</width>
<height>236</height>
</rect>
</property>
<property name="windowTitle">
<string>Chords</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ChordTableView" name="chordTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QGridLayout" name="chordGridLayout">
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1">
<widget class="QLineEdit" name="guitarVoicingLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="guitarVoicingLabel">
<property name="text">
<string>Guitar voicing</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="pianoVoicingLineEdit"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="chordNameLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="pianoVoicingLabel">
<property name="text">
<string>Piano voicing</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="chordNameLabel">
<property name="text">
<string>Chord name</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="guitarVoicingButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Editor...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="bottomChordHorizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="removeChordButton">
<property name="text">
<string>Remove chord</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="updateChordButton">
<property name="text">
<string>Update chord</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addChordButton">
<property name="text">
<string>Add chord</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ChordTableView</class>
<extends>QTableView</extends>
<header>csgui/tableView.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>chordNameLineEdit</tabstop>
<tabstop>guitarVoicingLineEdit</tabstop>
<tabstop>guitarVoicingButton</tabstop>
<tabstop>pianoVoicingLineEdit</tabstop>
<tabstop>removeChordButton</tabstop>
<tabstop>updateChordButton</tabstop>
<tabstop>addChordButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

162
ui/docinfo.ui

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>docInfoWidget</class>
<widget class="QWidget" name="docInfoWidget">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>291</width>
<height>170</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Document information</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QFormLayout" name="formLayoutOverview">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="titleLabel">
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="titleLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="subtitleLabel">
<property name="text">
<string>Subtitle</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="subtitleLineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="composerLabel">
<property name="text">
<string>Composer</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="composerLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="arrangerLabel">
<property name="text">
<string>Arranger</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="arrangerLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="tempoLabel">
<property name="text">
<string>Tempo</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="tempoLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="timeSignatureLabel">
<property name="text">
<string>Time</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="timeSignatureSpinBox">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

57
ui/document.ui

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>docWindow</class>
<widget class="QWidget" name="docWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>324</height>
</rect>
</property>
<property name="windowTitle">
<string>Unsaved</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="PDFViewer" name="pdfArea" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>300</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PDFViewer</class>
<extends>QWidget</extends>
<header>csgui/pdfViewer.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

1076
ui/mainwindow.ui
File diff suppressed because it is too large
View File

83
ui/preview.ui

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>previewPanel</class>
<widget class="QWidget" name="previewPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>202</width>
<height>72</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>202</width>
<height>72</height>
</size>
</property>
<property name="windowTitle">
<string>Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item alignment="Qt::AlignHCenter">
<widget class="QCheckBox" name="autoUpdatePreviewCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatically update preview</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="updatePreviewButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Update preview</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

326
ui/psetup.ui

@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>psetupWidget</class>
<widget class="QWidget" name="psetupWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>173</width>
<height>306</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Page setup</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0,0,0,0" columnstretch="0,0">
<property name="spacing">
<number>6</number>
</property>
<item row="6" column="0" colspan="2">
<widget class="QFontComboBox" name="fontComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="editable">
<bool>false</bool>
</property>
<property name="maxVisibleItems">
<number>12</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="writingSystem">
<enum>QFontDatabase::Any</enum>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="includedFontCheckBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>FreeSans</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="rightMarginLineEdit">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="bottomMarginLineEdit">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="fontLabel">
<property name="maximumSize">
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="pageSizeComboBox">
<property name="maximumSize">
<size>
<width>70</width>
<height>26</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="beatWidthLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Beat width</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="topMarginLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Top margin</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="documentUnitsComboBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>26</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="leftMarginLineEdit">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="topMarginLineEdit">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="pageSizeLabel">
<property name="text">
<string>Page size</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="beatWidthLineEdit">
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="leftMarginLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Left margin</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="bottomMarginLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Bottom margin</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="lineSpacingLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Line spacing</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="rightMarginLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Right margin</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="documentUnitsLabel">
<property name="text">
<string>Units</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QDoubleSpinBox" name="lineSpacingDoubleSpinBox">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>pageSizeComboBox</tabstop>
<tabstop>documentUnitsComboBox</tabstop>
<tabstop>leftMarginLineEdit</tabstop>
<tabstop>rightMarginLineEdit</tabstop>
<tabstop>topMarginLineEdit</tabstop>
<tabstop>bottomMarginLineEdit</tabstop>
<tabstop>fontComboBox</tabstop>
<tabstop>includedFontCheckBox</tabstop>
<tabstop>lineSpacingDoubleSpinBox</tabstop>
<tabstop>beatWidthLineEdit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

165
ui/sections.ui

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>sectionsWidget</class>
<widget class="QWidget" name="sectionsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>405</width>
<height>170</height>
</rect>
</property>
<property name="windowTitle">
<string>Sections</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="SectionTableView" name="sectionTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::TargetMoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="sectionNameLabel">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="sectionNameLineEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="bottomSectionHorizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="removeSectionButton">
<property name="text">
<string>Remove section</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="updateSectionButton">
<property name="text">
<string>Update section</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addSectionButton">
<property name="text">
<string>Add section</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SectionTableView</class>
<extends>QTableView</extends>
<header>csgui/tableView.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

43
version.rc

@ -0,0 +1,43 @@
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
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, 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.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x4,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904B0',
[StringStruct(u'CompanyName', u'Ivan Holmes'),
StringStruct(u'FileDescription', u'Chordsheet'),
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.5.0')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)

43
version.rc.template

@ -0,0 +1,43 @@
# UTF-8
#
# For more details about fixed file info 'ffi' see:
# http://msdn.microsoft.com/en-us/library/ms646997.aspx
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=(%MAJOR_VERSION%, %MINOR_VERSION%, %PATCH_VERSION%, 0),
prodvers=(%MAJOR_VERSION%, %MINOR_VERSION%, %PATCH_VERSION%, 0),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
flags=0x0,
# The operating system for which this file was designed.
# 0x4 - NT and there is no need to change it.
OS=0x4,
# The general type of file.
# 0x1 - the file is an application.
fileType=0x1,
# The function of the file.
# 0x0 - the function is not defined for this fileType
subtype=0x0,
# Creation date and time stamp.
date=(0, 0)
),
kids=[
StringFileInfo(
[
StringTable(
u'040904B0',
[StringStruct(u'CompanyName', u'Ivan Holmes'),
StringStruct(u'FileDescription', u'%APPNAME%'),
StringStruct(u'FileVersion', u'%VERSION%'),
StringStruct(u'InternalName', u'%APPNAME%'),
StringStruct(u'LegalCopyright', u'Copyright (c) Ivan Holmes, 2020. Some rights reserved.'),
StringStruct(u'OriginalFilename', u'chordsheet.exe'),
StringStruct(u'ProductName', u'%APPNAME%'),
StringStruct(u'ProductVersion', u'%VERSION%')])
]),
VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
]
)

19
win.spec

@ -2,7 +2,6 @@
block_cipher = None block_cipher = None
a = Analysis(['gui.py'], a = Analysis(['gui.py'],
pathex=['C:\\Users\\Ivan Holmes\\code\\chordsheet'], pathex=['C:\\Users\\Ivan Holmes\\code\\chordsheet'],
binaries=[], binaries=[],
@ -22,16 +21,24 @@ pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher) cipher=block_cipher)
exe = EXE(pyz, exe = EXE(pyz,
a.scripts, a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[], [],
exclude_binaries=True,
name='Chordsheet', name='Chordsheet',
icon='ui\\icon.ico', icon='ui\\icon.ico',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=True,
upx=False,
upx_exclude=[], upx_exclude=[],
runtime_tmpdir=None, runtime_tmpdir=None,
console=False )
console=False,
version='version.rc')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
upx_exclude=[],
name='Chordsheet')
Loading…
Cancel
Save