Browse Source

input validation for chord and block editors

add icon
add about dialogue
improve preview rendering
master
Ivan Holmes 5 years ago
parent
commit
5f334d1f14
  1. 3
      _version.py
  2. 15
      chordsheet/parsers.py
  3. 5
      chordsheet/tableView.py
  4. BIN
      examples/example.pdf
  5. BIN
      examples/example.png
  6. 47
      examples/example.xml
  7. 232
      gui.py
  8. 2
      mac.spec
  9. 121
      ui/aboutdialog.ui
  10. BIN
      ui/icon.afdesign
  11. BIN
      ui/icon.icns
  12. BIN
      ui/icon.ico
  13. BIN
      ui/icon.png
  14. 1
      win.spec

3
_version.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
version = '0.4'

15
chordsheet/parsers.py

@ -1,24 +1,29 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from sys import exit
def parseFingering(fingering, instrument): def parseFingering(fingering, instrument):
"""
Converts fingerings into the list format that Chord objects understand.
"""
if instrument == 'guitar': if instrument == 'guitar':
numStrings = 6 numStrings = 6
if len(fingering) == numStrings:
if len(fingering) == numStrings: # if the fingering is entered in concise format e.g. xx4455
output = list(fingering) output = list(fingering)
else:
else: # if entered in long format e.g. x,x,10,10,11,11
output = [x for x in fingering.split(',')] output = [x for x in fingering.split(',')]
if len(output) == numStrings: if len(output) == numStrings:
return output return output
else: else:
exit("Voicing <{v}> is malformed.".format(v=fingering))
raise Exception("Voicing <{}> is malformed.".format(fingering))
else: else:
return [fingering] return [fingering]
# dictionary holding text to be replaced in chord names
nameReplacements = { "b":"", "#":"" } nameReplacements = { "b":"", "#":"" }
def parseName(chordName): def parseName(chordName):
"""
Replaces symbols in chord names.
"""
parsedName = chordName parsedName = chordName
for i, j in nameReplacements.items(): for i, j in nameReplacements.items():
parsedName = parsedName.replace(i, j) parsedName = parsedName.replace(i, j)

5
chordsheet/tableView.py

@ -62,7 +62,6 @@ class ChordTableView(MTableView):
self.model.appendRow(rowList) self.model.appendRow(rowList)
self.resizeColumnsToContents()
class BlockTableView(MTableView): class BlockTableView(MTableView):
@ -79,6 +78,4 @@ class BlockTableView(MTableView):
item.setEditable(False) item.setEditable(False)
item.setDropEnabled(False) item.setDropEnabled(False)
self.model.appendRow(rowList)
self.resizeColumnsToContents()
self.model.appendRow(rowList)

BIN
examples/example.pdf

BIN
examples/example.png

After

Width: 2480  |  Height: 3507  |  Size: 274 KiB

47
examples/example.xml

@ -1,46 +1 @@
<chordsheet>
<title>Composition</title>
<composer>A. Person</composer>
<timesignature>4</timesignature>
<chords>
<chord>
<name>B</name>
<voicing instrument="guitar">xx2341</voicing>
<voicing instrument="piano">abcdefg</voicing>
</chord>
<chord>
<name>E</name>
<voicing instrument="guitar">022100</voicing>
</chord>
<chord>
<name>Cm9</name>
<voicing instrument="guitar">x,x,8,8,8,10</voicing>
</chord>
<chord>
<name>D7b5#9</name>
</chord>
</chords>
<progression>
<block>
<length>4</length>
<chord>B</chord>
<notes>These are notes</notes>
</block>
<block>
<length>4</length>
<chord>E</chord>
</block>
<block>
<length>12</length>
<chord>Cm9</chord>
</block>
<block>
<length>6</length>
<chord>D7b5#9</chord>
</block>
<block>
<length>6</length>
<notes>For quiet contemplation.</notes>
</block>
</progression>
</chordsheet>
<chordsheet><title>Composition</title><composer>A. Person</composer><timesignature>4</timesignature><chords><chord><name>B</name><voicing instrument="guitar">x,x,2,3,4,1</voicing></chord><chord><name>E</name><voicing instrument="guitar">0,2,2,1,0,0</voicing></chord><chord><name>Cm9</name><voicing instrument="guitar">x,x,8,8,8,10</voicing></chord><chord><name>D7&#9837;5&#9839;9</name></chord></chords><progression><block><length>4</length><chord>B</chord><notes>These are notes.</notes></block><block><length>4</length><chord>E</chord></block><block><length>12</length><chord>Cm9</chord></block><block><length>6</length><chord>D7&#9837;5&#9839;9</chord></block><block><length>6</length><notes>For quiet contemplation.</notes></block></progression></chordsheet>

232
gui.py

@ -24,6 +24,8 @@ from chordsheet.document import Document, Style, Chord, Block
from chordsheet.render import savePDF from chordsheet.render import savePDF
from chordsheet.parsers import parseFingering, parseName from chordsheet.parsers import parseFingering, parseName
from _version import version
# set the directory where our files are depending on whether we're running a pyinstaller binary or not # set the directory where our files are depending on whether we're running a pyinstaller binary or not
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
scriptDir = sys._MEIPASS scriptDir = sys._MEIPASS
@ -66,6 +68,7 @@ class DocumentWindow(QMainWindow):
self.UIFileLoader(str(os.path.join(scriptDir, 'ui','mainwindow.ui'))) self.UIFileLoader(str(os.path.join(scriptDir, 'ui','mainwindow.ui')))
self.UIInitStyle() self.UIInitStyle()
self.updateChordDict()
self.setCentralWidget(self.window.centralWidget) self.setCentralWidget(self.window.centralWidget)
self.setMenuBar(self.window.menuBar) self.setMenuBar(self.window.menuBar)
@ -73,7 +76,7 @@ class DocumentWindow(QMainWindow):
if filename: if filename:
try: try:
self.doc.loadXML(filename)
self.openFile(filename)
except: except:
UnreadableMessageBox().exec() UnreadableMessageBox().exec()
@ -103,6 +106,11 @@ class DocumentWindow(QMainWindow):
self.window.actionSave_PDF.triggered.connect(self.menuFileSavePDFAction) self.window.actionSave_PDF.triggered.connect(self.menuFileSavePDFAction)
self.window.actionPrint.triggered.connect(self.menuFilePrintAction) self.window.actionPrint.triggered.connect(self.menuFilePrintAction)
self.window.actionClose.triggered.connect(self.menuFileCloseAction) self.window.actionClose.triggered.connect(self.menuFileCloseAction)
self.window.actionUndo.triggered.connect(self.menuEditUndoAction)
self.window.actionRedo.triggered.connect(self.menuEditRedoAction)
self.window.actionCut.triggered.connect(self.menuEditCutAction)
self.window.actionCopy.triggered.connect(self.menuEditCopyAction)
self.window.actionPaste.triggered.connect(self.menuEditPasteAction)
self.window.actionNew.setShortcut(QKeySequence.New) self.window.actionNew.setShortcut(QKeySequence.New)
self.window.actionOpen.setShortcut(QKeySequence.Open) self.window.actionOpen.setShortcut(QKeySequence.Open)
@ -213,12 +221,15 @@ class DocumentWindow(QMainWindow):
def menuFileOpenAction(self): def menuFileOpenAction(self):
filePath = QFileDialog.getOpenFileName(self.window.tabWidget, 'Open file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)") filePath = QFileDialog.getOpenFileName(self.window.tabWidget, 'Open file', self.getPath("workingPath"), "Chordsheet ML files (*.xml *.cml)")
if filePath[0]: if filePath[0]:
self.currentFilePath = filePath[0]
self.doc.loadXML(filePath[0])
self.lastDoc = copy(self.doc)
self.setPath("workingPath", self.currentFilePath)
self.UIInitDocument()
self.updatePreview()
self.openFile(filePath[0])
def openFile(self, filePath):
self.currentFilePath = filePath
self.doc.loadXML(self.currentFilePath)
self.lastDoc = copy(self.doc)
self.setPath("workingPath", self.currentFilePath)
self.UIInitDocument()
self.updatePreview()
def menuFileSaveAction(self): def menuFileSaveAction(self):
self.updateDocument() self.updateDocument()
@ -259,7 +270,37 @@ class DocumentWindow(QMainWindow):
self.saveWarning() self.saveWarning()
def menuFileAboutAction(self): def menuFileAboutAction(self):
aboutDialog = QMessageBox.information(self, "About", "Chordsheet © Ivan Holmes, 2019", buttons = QMessageBox.Ok, defaultButton = QMessageBox.Ok)
aDialog = AboutDialog()
def menuEditUndoAction(self):
try:
QApplication.focusWidget().undo()
except:
pass
def menuEditRedoAction(self):
try:
QApplication.focusWidget().redo()
except:
pass
def menuEditCutAction(self):
try:
QApplication.focusWidget().cut()
except:
pass
def menuEditCopyAction(self):
try:
QApplication.focusWidget().copy()
except:
pass
def menuEditPasteAction(self):
try:
QApplication.focusWidget().paste()
except:
pass
def saveWarning(self): def saveWarning(self):
""" """
@ -297,7 +338,8 @@ class DocumentWindow(QMainWindow):
self.window.guitarVoicingLineEdit.clear() self.window.guitarVoicingLineEdit.clear()
def updateChordDict(self): def updateChordDict(self):
self.chordDict = {c.name:c for c in self.doc.chordList}
self.chordDict = {'None':None}
self.chordDict.update({c.name:c for c in self.doc.chordList})
self.window.blockChordComboBox.clear() self.window.blockChordComboBox.clear()
self.window.blockChordComboBox.addItems(list(self.chordDict.keys())) self.window.blockChordComboBox.addItems(list(self.chordDict.keys()))
@ -312,27 +354,51 @@ class DocumentWindow(QMainWindow):
self.updateChordDict() self.updateChordDict()
def addChordAction(self): def addChordAction(self):
success = False
self.updateChords() self.updateChords()
self.doc.chordList.append(Chord(parseName(self.window.chordNameLineEdit.text())))
if self.window.guitarVoicingLineEdit.text():
self.doc.chordList[-1].voicings['guitar'] = parseFingering(self.window.guitarVoicingLineEdit.text(), 'guitar')
cName = parseName(self.window.chordNameLineEdit.text())
if cName:
self.doc.chordList.append(Chord(cName))
if self.window.guitarVoicingLineEdit.text():
try:
self.doc.chordList[-1].voicings['guitar'] = parseFingering(self.window.guitarVoicingLineEdit.text(), 'guitar')
success = True
except:
VoicingWarningMessageBox().exec()
else:
success = True
else:
NameWarningMessageBox().exec()
self.window.chordTableView.populate(self.doc.chordList)
self.clearChordLineEdits()
self.updateChordDict()
if success == True:
self.window.chordTableView.populate(self.doc.chordList)
self.clearChordLineEdits()
self.updateChordDict()
def updateChordAction(self): def updateChordAction(self):
success = False
if self.window.chordTableView.selectionModel().hasSelection(): if self.window.chordTableView.selectionModel().hasSelection():
self.updateChords() self.updateChords()
row = self.window.chordTableView.selectionModel().currentIndex().row() row = self.window.chordTableView.selectionModel().currentIndex().row()
self.doc.chordList[row] = Chord(parseName(self.window.chordNameLineEdit.text()))
if self.window.guitarVoicingLineEdit.text():
self.doc.chordList[-1].voicings['guitar'] = parseFingering(self.window.guitarVoicingLineEdit.text(), 'guitar')
self.window.chordTableView.populate(self.doc.chordList)
self.clearChordLineEdits()
self.updateChordDict()
cName = parseName(self.window.chordNameLineEdit.text())
if cName:
self.doc.chordList[row] = Chord(cName)
if self.window.guitarVoicingLineEdit.text():
try:
self.doc.chordList[-1].voicings['guitar'] = parseFingering(self.window.guitarVoicingLineEdit.text(), 'guitar')
success = True
except:
VoicingWarningMessageBox().exec()
else:
success = True
else:
NameWarningMessageBox().exec()
if success == True:
self.window.chordTableView.populate(self.doc.chordList)
self.clearChordLineEdits()
self.updateChordDict()
def clearBlockLineEdits(self): def clearBlockLineEdits(self):
self.window.blockLengthLineEdit.clear() self.window.blockLengthLineEdit.clear()
@ -351,23 +417,38 @@ class DocumentWindow(QMainWindow):
def addBlockAction(self): def addBlockAction(self):
self.updateBlocks() self.updateBlocks()
self.doc.blockList.append(Block(self.window.blockLengthLineEdit.text(),
chord = self.chordDict[self.window.blockChordComboBox.currentText()],
notes = (self.window.blockNotesLineEdit.text() if not "" else None)))
try:
bLength = int(self.window.blockLengthLineEdit.text())
except:
bLength = False
self.window.blockTableView.populate(self.doc.blockList)
self.clearBlockLineEdits()
if bLength:
self.doc.blockList.append(Block(bLength,
chord = self.chordDict[self.window.blockChordComboBox.currentText()],
notes = (self.window.blockNotesLineEdit.text() if not "" else None)))
self.window.blockTableView.populate(self.doc.blockList)
self.clearBlockLineEdits()
else:
LengthWarningMessageBox().exec()
def updateBlockAction(self): def updateBlockAction(self):
if self.window.blockTableView.selectionModel().hasSelection(): if self.window.blockTableView.selectionModel().hasSelection():
self.updateBlocks() self.updateBlocks()
row = self.window.blockTableView.selectionModel().currentIndex().row()
self.doc.blockList[row] = (Block(self.window.blockLengthLineEdit.text(),
chord = self.chordDict[self.window.blockChordComboBox.currentText()],
notes = (self.window.blockNotesLineEdit.text() if not "" else None)))
self.window.blockTableView.populate(self.doc.blockList)
self.clearBlockLineEdits()
try:
bLength = int(self.window.blockLengthLineEdit.text())
except:
bLength = False
row = self.window.blockTableView.selectionModel().currentIndex().row()
if bLength:
self.doc.blockList[row] = (Block(bLength,
chord = self.chordDict[self.window.blockChordComboBox.currentText()],
notes = (self.window.blockNotesLineEdit.text() if not "" else None)))
self.window.blockTableView.populate(self.doc.blockList)
self.clearBlockLineEdits()
else:
LengthWarningMessageBox().exec()
def generateAction(self): def generateAction(self):
self.updateDocument() self.updateDocument()
@ -378,12 +459,13 @@ class DocumentWindow(QMainWindow):
savePDF(self.doc, self.style, self.currentPreview) savePDF(self.doc, self.style, self.currentPreview)
pdfView = fitz.Document(stream=self.currentPreview, filetype='pdf') pdfView = fitz.Document(stream=self.currentPreview, filetype='pdf')
pix = pdfView[0].getPixmap(alpha = False)
pix = pdfView[0].getPixmap(matrix = fitz.Matrix(4, 4), alpha = False) # render at 4x resolution and scale
fmt = QImage.Format_RGB888 fmt = QImage.Format_RGB888
qtimg = QImage(pix.samples, pix.width, pix.height, pix.stride, fmt) qtimg = QImage(pix.samples, pix.width, pix.height, pix.stride, fmt)
self.window.imageLabel.setPixmap(QPixmap.fromImage(qtimg))
self.window.imageLabel.setPixmap(QPixmap.fromImage(qtimg).scaled(self.window.scrollArea.width()-30, self.window.scrollArea.height()-30, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation))
# -30 because the scrollarea has a margin of 12 each side (extra for safety)
self.window.imageLabel.repaint() # necessary on Mojave with PyInstaller (or previous contents will be shown) self.window.imageLabel.repaint() # necessary on Mojave with PyInstaller (or previous contents will be shown)
def updateTitleBar(self): def updateTitleBar(self):
@ -406,17 +488,7 @@ class DocumentWindow(QMainWindow):
blockTableList = [] blockTableList = []
for i in range(self.window.blockTableView.model.rowCount()): for i in range(self.window.blockTableView.model.rowCount()):
blockLength = int(self.window.blockTableView.model.item(i, 1).text()) blockLength = int(self.window.blockTableView.model.item(i, 1).text())
blockChordName = parseName(self.window.blockTableView.model.item(i, 0).text())
if blockChordName:
blockChord = None
for c in self.doc.chordList:
if c.name == blockChordName:
blockChord = c
break
if blockChord is None:
exit("Chord {c} does not match any chord in {l}.".format(c=blockChordName, l=self.doc.chordList))
else:
blockChord = None
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 blockNotes = self.window.blockTableView.model.item(i, 2).text() if self.window.blockTableView.model.item(i, 2).text() else None
blockTableList.append(Block(blockLength, chord=blockChord, notes=blockNotes)) blockTableList.append(Block(blockLength, chord=blockChord, notes=blockNotes))
@ -469,6 +541,28 @@ class GuitarDialog(QDialog):
else: else:
return None 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)
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): class UnsavedMessageBox(QMessageBox):
""" """
Message box to alert the user of unsaved changes and allow them to choose how to act. Message box to alert the user of unsaved changes and allow them to choose how to act.
@ -476,6 +570,7 @@ class UnsavedMessageBox(QMessageBox):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setIcon(QMessageBox.Question)
self.setWindowTitle("Unsaved changes") self.setWindowTitle("Unsaved changes")
self.setText("The document has been modified.") self.setText("The document has been modified.")
self.setInformativeText("Do you want to save your changes?") self.setInformativeText("Do you want to save your changes?")
@ -484,17 +579,60 @@ class UnsavedMessageBox(QMessageBox):
class UnreadableMessageBox(QMessageBox): class UnreadableMessageBox(QMessageBox):
""" """
Message box to inform the user that the chosen file cannot be opened.
Message box to warn the user that the chosen file cannot be opened.
""" """
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.setIcon(QMessageBox.Warning)
self.setWindowTitle("File cannot be opened") self.setWindowTitle("File cannot be opened")
self.setText("The file you have selected 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.setInformativeText("Please make sure it is in the right format.")
self.setStandardButtons(QMessageBox.Ok) self.setStandardButtons(QMessageBox.Ok)
self.setDefaultButton(QMessageBox.Ok) self.setDefaultButton(QMessageBox.Ok)
class NameWarningMessageBox(QMessageBox):
"""
Message box to warn the user that a chord must have a name
"""
def __init__(self):
super().__init__()
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 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 whole number 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__': if __name__ == '__main__':
app = QApplication(sys.argv) app = QApplication(sys.argv)

2
mac.spec

@ -36,7 +36,7 @@ exe = EXE(pyz,
console=False ) console=False )
app = BUNDLE(exe, app = BUNDLE(exe,
name='Chordsheet.app', name='Chordsheet.app',
icon=None,
icon='ui/icon.icns',
bundle_identifier=None, bundle_identifier=None,
info_plist={ info_plist={
'NSPrincipalClass': 'NSApplication', 'NSPrincipalClass': 'NSApplication',

121
ui/aboutdialog.ui

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>200</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>About Chordsheet</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="leftPane">
<item>
<widget class="QLabel" name="iconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="rightPane">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<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>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="versionLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Version not set</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignRight|Qt::AlignTrailing</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

BIN
ui/icon.afdesign

BIN
ui/icon.icns

BIN
ui/icon.ico

BIN
ui/icon.png

After

Width: 1024  |  Height: 1024  |  Size: 236 KiB

1
win.spec

@ -27,6 +27,7 @@ exe = EXE(pyz,
a.datas, a.datas,
[], [],
name='Chordsheet', name='Chordsheet',
icon='ui\\icon.ico',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,

Loading…
Cancel
Save