Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to tkinter canvas2pdf?
#1
After a long time I desided to post a question because my previous posts where a little dumb...

I have a situation where I created a tkinter program which uses the canvas widget. On that widget my program is drawing music in a special format called PianoScript. It's a mini typesetting program and now I need to create a function that exports a beautiful pdf file containing all pages. I already created a function which is exporting separate postscript files for each page:
def exportPDF():
	print('exportPDF')

	f = filedialog.asksaveasfile(mode='w', parent=root, filetypes=[("Postscript","*.ps")])

	if f:
		name = f.name[:-3]
		counter = 0

		for export in range(render('q')):
			counter += 1
			export = export
			canvas.postscript(file=f"{name}{counter}.ps", colormode='gray', x=50, y=50+(export*(paperheigth+50)), width=paperwidth, height=paperheigth, rotate=False)

	else:

		pass

	return
But of course I want a clean and nice pdf export. On different forums I found that I could use Imagemagick for postscript to pdf conversion. Or am I wrong? Can Imagemagick convert PS2PDF? And PS is designed to contain multiple pages right? So how can I create a multiple-page postscript file from the tkinter canvas? Or do you guys know maybe a simple tool that does the job of converting postscript to pdf?

To give an idea of this hobby project made with love:
### IMPORTS ###
from tkinter import Tk, Text, PanedWindow, Canvas, Scrollbar, Menu, filedialog, END, messagebox, simpledialog
import platform, subprocess, os

### GUI ###
# Root
root = Tk()
root.title('PianoScript')
scrwidth = root.winfo_screenwidth()
scrheight = root.winfo_screenheight()
root.geometry(f"{int(scrwidth / 1.5)}x{int(scrheight / 1.25)}+{int(scrwidth / 6)}+{int(scrheight / 12)}")
# PanedWindow
paned = PanedWindow(root, relief='sunken', sashwidth=15, sashcursor='arrow')
paned.pack(fill='both', expand=1)
# Left Panel
leftpanel = PanedWindow(paned, relief='raised', width=900)
paned.add(leftpanel)
# Right Panel
rightpanel = PanedWindow(paned,
                            orient='vertical',
                            sashwidth=15,
                            sashcursor='arrow')
paned.add(rightpanel)
# Canvas
canvas = Canvas(leftpanel, bg='grey')
canvas.place(relwidth=1, relheight=1)
vbar = Scrollbar(canvas, orient='vertical', width=20)
vbar.pack(side='right', fill='y')
vbar.config(command=canvas.yview)
canvas.configure(yscrollcommand=vbar.set)
hbar = Scrollbar(canvas, orient='horizontal', width=20)
hbar.pack(side='bottom', fill='x')
hbar.config(command=canvas.xview)
canvas.configure(xscrollcommand=hbar.set)
# if platform.system() == 'Darwin':
# 	root.bind('<Control-,>', lambda event: canvas.yview('scroll', -1, 'units'))
# 	root.bind('<Control-.>', lambda event: canvas.yview('scroll', 1, 'units'))
# if platform.system() == 'Linux':
#     root.bind('<4>', lambda event: canvas.yview('scroll', -1, 'units'))
#     root.bind('<5>', lambda event: canvas.yview('scroll', 1, 'units'))
#linux zoom
def bbox_offset(bbox):
		x1, y1, x2, y2 = bbox
		return (x1-40, y1-40, x2+40, y2+40)
def zoomerP(event):
	canvas.scale("all", event.x, event.y, 1.1, 1.1)
	canvas.configure(scrollregion=bbox_offset(canvas.bbox("all")))
def zoomerM(event):
	canvas.scale("all", event.x, event.y, 0.9, 0.9)
	canvas.configure(scrollregion=bbox_offset(canvas.bbox("all")))
canvas.bind("<1>", zoomerP)
canvas.bind("<3>", zoomerM)
# # Text
textw = Text(rightpanel, foreground='white', background='black', insertbackground='white')
textw.place(relwidth=1, relheight=1)
textw.focus_set()
fontsize = 16
textw.configure(font=f'courier {fontsize} ')
# Rightclick menu
def do_popup(event): 
    try: 
        menu.tk_popup(event.x_root, event.y_root)
    finally: 
        menu.grab_release()
menu = Menu(root, tearoff = 0, background='black', foreground='white',
               activebackground='white', activeforeground='black') 
menu.add_command(label ="[x] close menu")
if platform.system() == 'Linux':
    textw.bind("<Button-3>", do_popup)
if platform.system() == 'Darwin':
    textw.bind("<Button-2>", do_popup)







### MAIN CODE ###

##########################################################################
## File management 														##
##########################################################################















file = textw.get('1.0', END + '-1c')
filechange = False
filepath = ''

def fileChange(v):
	global filechange, filepath, file

	if filepath == 'New':
		
		if file == '':

			filechange = False
			root.title(f'PianoScript - {filepath}')
			return

		else:

			filechange = True
			root.title(f'PianoScript - {filepath}*')
			return

	else:# if file is opened file

		sourcefile = textw.get('1.0', END + '-1c')

		if sourcefile != file:

			filechange = True
			root.title(f'PianoScript - {filepath}*')
			return

		else:

			filechange = False
			root.title(f'PianoScript - {filepath}')
			return


	return


def saveQuest():
	
	if messagebox.askyesno('Wish to save?', 'Do you wish to save the current file?'):
		
		saveFile()

	else:
		
		pass

	return


def newFile():
	print('newFile')

	global filechange, filepath, file

	if filechange == True:

		saveQuest()

	else:

		pass

	filechange = False
	filepath = 'New'
	textw.delete('1.0', END)
	textw.insert('1.0', '''//titles:
title=Moonlight sonata
composer=L. Beethoven
copyright=copyright reserved 2021

//grid:
grid.<W>.4.69

//settings:
mpline=4
scale=105





//music:

// M1-20 //
{


R
_1 <Q/3>G3-C4-e4 G3-C4-e4 G3-C4-e4 G3-C4-e4
_2 G3-C4-e4 G3-C4-e4 G3-C4-e4 G3-C4-e4
_3 a3-C4-e4 a3-C4-e4 a3-d4-F4 a3-d4-F4
_4 G3-c4-F4 G3-C4-e4 G3-C4-D4 F3-c4-D4

_5 e3-G3-C4 G3-C4-e4 G3-C4-e4 G3-C4-e4 <Q>_ <E+S>G4- <S>G4
_6 <Q/3>G3-D4-F4 G3-D4-F4 G3-D4-F4 G3-D4-F4 _6 <H+Q>G4 <E+S>G4- <S>G4
_7 <Q/3>G3-C4-e4 G3-C4-e4 a3-C4-F4 a3-C4-F4 _7 <H>G4 a4
_8 <Q/3>G3-b3-e4 G3-b3-e4 a3-b3-D4 a3-b3-D4 _8 <H>G4 <Q>F4 b4

_9 <W>e4 _ <Q/3>G3-b3-e4 G3-b3-e4 G3-b3-e4 G3-b3-e4
_10 g3-b3-e4 g3-b3-e4 g3-b3-e4 g3-b3-e4 <Q>_ <E+S>g4- <S>g4
_11 <Q/3>g3-b3-f4 g3-b3-f4 g3-b3-f4 g3-b3-f4 _11 <H+Q>g4 <E+S>g4- <S>g4
_12 <Q/3>g3-c4-e4 g3-b3-e4 g3-C4-e4 F3-C4-e4 _12 <H+Q>g4 <Q>F4

_13 <Q/3>F3-b3-d4 F3-b3-d4 g3-b3-C4 e3-b3-C4 _13 <H>F4 <Q>g4 e4
_14 <Q/3>F3-b3-d4 F3-b3-d4 F3-A3-C4 F3-A3-C4 _14 <H>F4 F4
_15 <Q/3>b3-d4-F4 b3-d4-F4 b3-D4-F4 b3-D4-F4 _15 <Q>b3 r r b4
_16 <Q/3>b3-e4-g4 b3-e4-g4 b3-e4-g4 b3-e4-g4 _16 <H+Q>c5 <Q>A4

_17 <Q/3>b3-D4-F4 b3-D4-F4 b3-D4-F4 b3-D4-F4 _17 <H+Q>b4 <Q>b4
_18 <Q/3>b3-e4-g4 b3-e4-g4 b3-e4-g4 b3-e4-g4 _18 <H+Q>c5 <Q>A4
_19 <Q/3>b3-D4-F4 b3-D4-F4 b3-d4-f4 b3-d4-f4 _19 <H>b4 b4
_20 <Q/3>b3-C4-G4 b3-C4-G4 a3-C4-F4 a3-C4-F4 _20 <H>b4 a4


L
_1 <W>C2_C3
_2 b1_b2
_3 <H>a1_a2 F1_F2
_4 G1_G2 G1_G2

_5 <W>C2_G2_C3
_6 c2_G2_c3 //6
_7 <H>C2_C3 F1_F2
_8 b1_b2 b1_b2

_9 <W>e2_e3
_10 e2_e3
_11 d2_d3
_12 <Q>c2_c3 b1_b2 <H>A1_A2

_13 <H>b1_b2 <Q>e2 g2
_14 <H>F2 F2_F1
_15 <W>b1 <Q>= _15 <W>b2 <Q>=
_16 <Q>r e2_e3 g2_g3 e2_e3

_17 <W>b1 <Q>= _17 <W>b2 <Q>=
_18 <Q>r e2_e3 g2_g3 e2_e3
_19 <H>b1_b2 G1_G2
_20 f1_f2 F1_F2


// M21-40 //
R
_21 <Q/3>g3-b3-d4 g3-b3-d4 F3-a3-D4 F3-a3-D4 _21 <H>g4 F4
_22 <Q/3>C3-F3-a3 C3-F3-a3 C3-F3-G3 C3-f3-G3 _22 <H>C4 <Q>C4 C4
_23 <Q/3>F3-a3-C4 a3-C4-F4 C4-F4-a4 C4-F4-a4 _23 <H+Q>r <E+S>C5- <S>C5
_24 <Q/3>C4-G4-b4 C4-G4-b4 C4-G4-b4 C4-G4-b4 _24 <H+Q>r <E+S>C5- <S>C5

_25 <Q/3>C4-F4-a4 C4-F4-a4 c4-F4-a4 C4-F4-a4 _25 <H>C5 <Q>c5 C5
_26 <Q/3>D4-F4-G4 D4-F4-G4 D4-F4-G4 D4-F4-G4 _26 <H+Q>D5 <Q>D5
_27 <Q/3>e4-G4-C5 e4-G4-C5 D4-F4-a4 C4-e4-A4 _27 <H>e5 <Q>D5 C5
_28 <Q/3>c5 c4-D4 G4 c4-D4 a4 c4-D4 F4 c4-D4 _28 <Q>c5 G4 a4 F4

_29 <Q/3>r c4-D4 G3 c4-D4 a3 c4-D4 F3 c4-D4 _29 <Q>r G3 a3 F3
_30 <Q/3>e3 e4-G4 C5 e4-G4 e5 e4-G4 C5 e4-G4 _30 <Q>r C5 e5 C5
_31 <Q/3>e2 e3-G3 C4 e3-G3 e3 e3-G3 C4 e3-G3 _31 <Q>r C4 e4 C4
_32 <Q/3>D3-a3-F3-c4-a3-D4-c4-F4-D4-a4-F4-c5

_33 e3-C4-G3-e4-C4-G4-e4-C5-G4-e5-C5-G4
_34 C4-g4-e4-A4-g4-C5-A4-e5-C5-g5-e5-A5
_35 F4-c5-a4-D5-c5-F5-D5-a5-F5-c6-a5-D6
_36 c6-F5-a5 D5-F5-c5 D5-a4-c5 F4-a4-D4

_37 F4-c4-D4 a3-c4-F3 a3-D3-F3 C3-F3-a3 _37 <H>r <Q/3>r <Q/3*2>D3 <Q>C3
_38 <Q/3>c3-F3-G3 a3-G3-F3 D3-F3-a3 C3-F3-a3 _38 <H>c3 <Q>D3 C3
_39 <Q/3>c3-F3-G3 a3-G3-F3 d3-F3-a3 C3-F3-a3 _39 <H>c3 <Q>d3 C3
_40 <Q/3>c3-F3-G3 a3-G3-F3 C3-e3-C4 C3-e3-C4 _40 <H>c3


L
_21 <H>b1_b2 c2_c3
_22 C2 C1_C2
_23 <W>F1_F2
_24 f2_C3_f3

_25 <H>F2_F3 <Q>D2_D3 C2_C3
_26 <H+Q>c2_G2_c3 <Q>c2_G2_c3
_27 <H>C2_C3 <Q>F1_F2 g1_g2
_28 <W>G1_G2

_29 G1_G2
_30 G1_G2
_31 G1_G2
_32 G1_G2

_33 G1_G2
_34 G1_G2
_35 G1 == _35 G2
_36 =

_37 =
_38 G1_G2
_39 G1_G2
_40 <H>G1_G2 a1_a2


// M41-60 //
R
_41 <Q/3>D3-a3-C4 D3-a3-C4 D3-G3-c4 D3-F3-c4
_42 <Q/3>e3-G3-C4 G3-C4-e4 G3-C4-e4 G3-C4-e4 _42 <Q>rrr <E+S>G4- <S>G4
_43 <Q/3>G3-D4-F4 G3-D4-F4 G3-D4-F4 G3-D4-F4 _43 <H+Q>G4 <E+S>G4- <S>G4
_44 <Q/3>G3-C4-e4 G3-C4-e4 <Q/3>a3-C4-F4 a3-C4-F4 _44 <H>G4 a4

_45 <Q/3>G3-b3-e4 G3-b3-e4 a3-b3-D4 a3-b3-D4<Q/3> _45 <H>G4 <Q>F4 b4
_46 <Q/3>G3-b3-e4 b3-e4-G4 b3-e4-G4 b3-e4-G4 _46 <Q>e4 r r <E+S>b4- <S>b4
_47 <Q/3>b3-F4-a4 b3-F4-a4 b3-F4-a4 b3-F4-a4 _47 <H+Q>b4 <E+S>b4- <S>b4
_48 <Q/3>b3-e4-G4 b3-e4-G4 c4-F4-G4 C4-e4-G4 _48 <H>b4 <Q>c5 C5

_49 <Q/3>D4-F4-G4 D4-F4-G4 e4-G4-C5 e4-G4-C5 _49 <H>D5 e5
_50 <Q/3>d4-F4-a4 d4-F4-a4 c4-F4-G4 c4-F4-G4 _50 <H>d5 c5
_51 <Q/3>C4-e4-G4 C4-e4-G4 C4-f4-G4 C4-f4-G4 _51 <H+Q>C5 <Q>C5
_52 <Q/3>C4-F4-a4 C4-F4-a4 C4-F4-a4 C4-F4-a4 _52 <H+Q>d5 <Q>c5

_53 <Q/3>C4-f4-G4 C4-f4-G4 C4-f4-G4 C4-f4-G4 _53 <H+Q>C5 <Q>C5
_54 <Q/3>C4-F4-a4 C4-F4-a4 C4-F4-a4 C4-F4-a4 _54 <H+Q>d5 <Q>c5
_55 <Q/3>C4-f4-G4 C4-f4-G4 C4-F4-a4 C4-F4-a4 _55 <H>C5 C5
_56 <Q/3>b3-F4-a4 b3-F4-a4 b3-F4-a4 b3-e4-G4 _56 <H+Q>b4 <Q>b4

_57 <Q/3>a3-e4-G4 a3-D4-F4 G3-D4-F4 G3-C4-e4 _57 <Q>a4 a4 G4 G4
_58 <Q/3>F3-C4-D4 F3-C4-D4 G3-C4-D4 a3-C4-D4 _58 <H>F4 <Q>G4 a4
_59 <Q/3>G3-C4-e4 G3-C4-e4 G3-c4-D4 G3-c4-D4 _59 <H>G4 G4
_60 <Q/3>e3-G3-C4 G3-C4-e4 G3-C4-e4 G3-C4-e4 _60 <Q>C4


L
_41 <H>F1_F2 G1_G2
_42 <W>C2_G2_C3
_43 c2_G2_c3
_44 <H>C2_C3 F1_F2

_45 b1_b2 b1_b2
_46 <W>e2_e3
_47 D2_D3
_48 <H>e2_e3 <Q>D2_D3 C2_C3

_49 <H>c2_G2_c3 C2_G2_C3
_50 F1_F2 G1_G2
_51 <W+Q>C2_C3 
_52 <Q>r F2_F3 a2_a3 F2_F3

_53 <W+Q>C2_C3
_54 <Q>r F2_F3 a2_a3 F2_F3
_55 <H>C2_C3 F1_F2
_56 <H+Q>D2_D3 <Q>e2_e3

_57 C2_C3 D2_D3 c2_c3 C2_C3
_58 <H>a1_a2 <Q>G1_G2 F1_F2
_59 <H>G1_G2 G1_G2
_60 <W>C2 _60 <H+Q>G2 <E+S>G2 <S>G2


// M61-69
R
_61 <Q/3>G3-D4-F4 G3-D4-F4 G3-D4-F4 G3-D4-F4
_62 G3-e4-C4 G4-e4-C5 G4-e5-C5 G5-e5-C5
_63 c5-D5-a4 c5-F4-a4 D4-F4-a3 c4-G3-F3 ___ <Q>c4
_64 <Q/3>e3_C4-e4-C4 G4-e4-C5 G4-e5C4 G5-e5-C5

_65 c5-D5-a4 c5-F4-a4 D4-F4-a3 c4-G3-F3 ___ <Q>c4
_66 <Q/3>e3_C4-G3-C4 e4-C4-G3 r e3-G3 C4-G3-e3 _66 <H>e3
_67 <Q/3>r C3-e3 G3-e3-C3 G2-C3-G2 e2-G2-e2 
_68 <H>r e3_G3_C4

_69 <W>e3_G3_C4


L
_61 <W>c2 _61 <H+Q>G2 <E+S>G2 <S>G2
_62 <W>C2 _62 <H+Q>G2 <E+S>G2 <S>G2
_63 <W>G1 _63 <H+Q>G2 <E+S>G2 <S>G2
_64 <W>C2 _64 <H+Q>G2 <E+S>G2 <S>G2

_65 <W>G1 _65 <H+Q>G2 <E+S>G2 <S>G2
_66 <W>G1 _66 <H>G2 C3
_67 <W>G1 _67 <H>G2
_68 C2 C2_G2_C3

_69 <W>C2_G2_C3
}''') # For test purposes
	render('q')
	root.title(f'PianoScript - {filepath}')
	
	return


def openFile():
	print('openFile')

	global filechange, filepath, file

	if filechange == True:

		saveQuest()

	else:

		pass

	f = filedialog.askopenfile(parent=root, mode='rb', title='Open')
	
	if f:

		textw.delete('1.0', END)
		textw.insert('1.0', f.read())
		render('q')

	else:

		return
	
	filechange = False
	filepath = f.name
	root.title(f'PianoScript - {filepath}')
	
	return


def saveFile():
	print('saveFile')

	global filechange
	
	if filepath == 'New':

		saveAs()
		filechange = False
		fileChange('x')# ?
		return

	else:

		f = open(filepath, 'w')
		d = textw.get('1.0', END + '-1c')
		f.write(d)
		f.close()

	filechange = False
	fileChange('x')
	root.title(f'PianoScript - {filepath}')

	return


def saveAs():
	print('saveAs')

	global filepath, filechange

	f = filedialog.asksaveasfile(mode='w', parent=root)

	if f:

		d = textw.get('1.0', END + '-1c')
		f.write(d)
		f.close()
		
		filechange = False
		filepath = f.name
		root.title(f'PianoScript - {filepath}')

	else:

		pass

	return


def quitEditor():
	print('quitEditor')
	
	global filechange

	if filechange == True:

		saveQuest()

	else:

		pass

	root.destroy()


def getFile():
	global file
	file = textw.get('1.0', END + '-1c')
	return file


def fileChecker():# checks if save file is valid and raises error if not.
	pass

menu.add_separator()
menu.add_command(label ="New", command=newFile)
menu.add_command(label ="Open", command=openFile)
menu.add_command(label ="Save", command=saveFile)
menu.add_command(label ="Save as", command=saveAs)
menu.add_separator()
menu.add_command(label ="Quit", command=quitEditor)

















##########################################################################
## Tools			 													##
##########################################################################

def bracketExtractor(text, openbracket, closebracket):


	def search_symbol_return_index(search, symbol):

		cntr = -1
		index = []
		for i in search:
			cntr += 1
			if i.find(symbol) == 0:
				index.append(cntr)
			else:
				pass
		return index


	start = search_symbol_return_index(text, openbracket)
	end = search_symbol_return_index(text, closebracket)
	startend = list(zip(start, end))
	voicelist = []

	for i in startend:
		voicelist.append(text[i[0]+1:i[1]])

	return voicelist


def strip_file(f):
	fl = ''
	for i in f.split('\n'):
		find = i.find('//')
		if find >= 0:
			i = i[:find]
			fl += i+'\n'
		else:
			fl += i+'\n'

	f = ''
	for i in fl.split('\n'):
		if i == '':
			pass
		else:
			f += i+'\n'
	return f


def durationConverter(string): # converts duration string to length in 'pianotick' format.
			
	calc = ''
	
	for i in string:
		if i == 'W':
			calc += '1024'
		if i == 'H':
			calc += '512'
		if i == 'Q':
			calc += '256'
		if i == 'E':
			calc += '128'
		if i == 'S':
			calc += '64'
		if i == 'T':
			calc += '32'
		if i == '+':
			calc += '+'
		if i == '-':
			calc += '-'
		if i == '*':
			calc += '*'
		if i == '/':
			calc += '/'
		if i == '(':
			calc += '('
		if i == ')':
			calc += ')'
		if i == '.':
			calc += '.'
		if i in ['0','1','2','3','4','5','6','7','8','9']:
			calc += i
	
	dur = None
	
	try: 
		dur = eval(calc)
	except SyntaxError: 
		print(f'wrong duration: {string}')
		return

	return dur


def string2pitch(string):
	pitchdict = {
	# Oct 0
	'a0':1, 'A0':2, 'b0':3,
	# Oct 1
	'c1':4, 'C1':5, 'd1':6, 'D1':7, 'e1':8, 'f1':9, 'F1':10, 'g1':11, 'G1':12, 'a1':13, 'A1':14, 'b1':15, 
	# Oct 2
	'c2':16, 'C2':17, 'd2':18, 'D2':19, 'e2':20, 'f2':21, 'F2':22, 'g2':23, 'G2':24, 'a2':25, 'A2':26, 'b2':27,
	# Oct 3
	'c3':28, 'C3':29, 'd3':30, 'D3':31, 'e3':32, 'f3':33, 'F3':34, 'g3':35, 'G3':36, 'a3':37, 'A3':38, 'b3':39,
	# Oct 4
	'c4':40, 'C4':41, 'd4':42, 'D4':43, 'e4':44, 'f4':45, 'F4':46, 'g4':47, 'G4':48, 'a4':49, 'A4':50, 'b4':51,
	# Oct 5
	'c5':52, 'C5':53, 'd5':54, 'D5':55, 'e5':56, 'f5':57, 'F5':58, 'g5':59, 'G5':60, 'a5':61, 'A5':62, 'b5':63,
	# Oct 6
	'c6':64, 'C6':65, 'd6':66, 'D6':67, 'e6':68, 'f6':69, 'F6':70, 'g6':71, 'G6':72, 'a6':73, 'A6':74, 'b6':75,
	# Oct 7
	'c7':76, 'C7':77, 'd7':78, 'D7':79, 'e7':80, 'f7':81, 'F7':82, 'g7':83, 'G7':84, 'a7':85, 'A7':86, 'b7':87,
	# Oct 8
	'c8':88
	}
	ret = pitchdict[string]
	return ret


def barline_pos_list(gridlist):
	barlinepos = [0]
	for grid in gridlist:
		cntr = 0
		for i in range(grid[3]):
			nxtbarln = barlinepos[-1] + grid[1]
			barlinepos.append(nxtbarln)
	return barlinepos


def newline_pos_list(gridlist, mpline):
	gridlist = barline_pos_list(gridlist)
	linelist = [0]
	cntr = 0
	for barline in gridlist:
		cntr += mpline
		try: linelist.append(gridlist[cntr])
		except IndexError: 
			linelist.append(gridlist[-1])
			break
	if linelist[-1] == linelist[-2]:
		linelist.remove(linelist[-1])

	linelist.pop(0)

	return linelist


def staff_height(mn, mx):
	'''
	This function returns the height of a staff based on the
	lowest and highest note.
	'''	
	staffheight = 0

	if mx >= 81:
		staffheight = 225
	if mx >= 76 and mx <= 80:
		staffheight = 190
	if mx >= 69 and mx <= 75:
		staffheight = 165
	if mx >= 64 and mx <= 68:
		staffheight = 130
	if mx >= 57 and mx <= 63:
		staffheight = 105
	if mx >= 52 and mx <= 56:
		staffheight = 70
	if mx >= 45 and mx <= 51:
		staffheight = 45
	if mx >= 40 and mx <= 44:
		staffheight = 10
	if mx < 40:
		staffheight = 10
	if mn >= 33 and mn <= 39:
		staffheight += 35
	if mn >= 28 and mn <= 32:
		staffheight += 60
	if mn >= 21 and mn <= 27:
		staffheight += 95
	if mn >= 16 and mn <= 20:
		staffheight += 120
	if mn >= 9 and mn <= 15:
		staffheight += 155
	if mn >= 4 and mn <= 8:
		staffheight += 180
	if mn >= 1 and mn <= 3:
		staffheight += 195
	return staffheight


def draw_staff_lines(y, mn, mx):
	'''
	'y' takes the y-position of the uppper line of the staff.
	'mn' and 'mx' take the lowest and highest note in the staff
	so the function can draw the needed lines.
	'''

	def draw3Line(y):
		x = 70
		canvas.create_line(x, y, x+printareawidth, y, width=2)
		canvas.create_line(x, y+10, x+printareawidth, y+10, width=2)
		canvas.create_line(x, y+20, x+printareawidth, y+20, width=2)


	def draw2Line(y):
		x = 70
		canvas.create_line(x, y, x+printareawidth, y, width=1)
		canvas.create_line(x, y+10, x+printareawidth, y+10, width=1)


	def drawDash2Line(y):
		x = 70
		canvas.create_line(x, y, x+printareawidth, y, width=1, dash=(6,6))
		canvas.create_line(x, y+10, x+printareawidth, y+10, width=1, dash=(6,6))

	keyline = 0
	staffheight = 0

	if mx >= 81:
		draw3Line(0+y)
		draw2Line(35+y)
		draw3Line(60+y)
		draw2Line(95+y)
		draw3Line(120+y)
		draw2Line(155+y)
		draw3Line(180+y)
		keyline = 215
	if mx >= 76 and mx <= 80:
		draw2Line(0+y)
		draw3Line(25+y)
		draw2Line(60+y)
		draw3Line(85+y)
		draw2Line(120+y)
		draw3Line(145+y)
		keyline = 180
	if mx >= 69 and mx <= 75:
		draw3Line(0+y)
		draw2Line(35+y)
		draw3Line(60+y)
		draw2Line(95+y)
		draw3Line(120+y)
		keyline = 155
	if mx >= 64 and mx <= 68:
		draw2Line(0+y)
		draw3Line(25+y)
		draw2Line(60+y)
		draw3Line(85+y)
		keyline = 120
	if mx >= 57 and mx <= 63:
		draw3Line(0+y)
		draw2Line(35+y)
		draw3Line(60+y)
		keyline = 95
	if mx >= 52 and mx <= 56:
		draw2Line(0+y)
		draw3Line(25+y)
		keyline = 60
	if mx >= 45 and mx <= 51:
		draw3Line(0+y)
		keyline = 35
	
	drawDash2Line(keyline+y)
	
	if mn >= 33 and mn <= 39:
		draw3Line(keyline+25+y)
	if mn >= 28 and mn <= 32:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
	if mn >= 21 and mn <= 27:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
		draw3Line(keyline+85+y)
	if mn >= 16 and mn <= 20:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
		draw3Line(keyline+85+y)
		draw2Line(keyline+120+y)
	if mn >= 9 and mn <= 15:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
		draw3Line(keyline+85+y)
		draw2Line(keyline+120+y)
		draw3Line(keyline+145+y)
	if mn >= 4 and mn <= 8:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
		draw3Line(keyline+85+y)
		draw2Line(keyline+120+y)
		draw3Line(keyline+145+y)
		draw2Line(keyline+180+y)
	if mn >= 1 and mn <= 3:
		draw3Line(keyline+25+y)
		draw2Line(keyline+60+y)
		draw3Line(keyline+85+y)
		draw2Line(keyline+120+y)
		draw3Line(keyline+145+y)
		draw2Line(keyline+180+y)
		canvas.create_line(70, keyline+205+y, 70+printareawidth, keyline+205+y, width=2)


def draw_paper(y):
			
			#canvas.create_rectangle(55, 55+y, 55+paperwidth, 55+paperheigth+y, fill='black', outline='')
			canvas.create_rectangle(50, 50+y, 50+paperwidth, 50+paperheigth+y, fill='white', outline='')
			#canvas.create_rectangle(70, 70+y, 70+printareawidth, 70+printareaheight+y, fill='', outline='blue')


### noteheads ###
def black_key_right(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 6
    y1 = y + 5
    canvas.create_line(x0,y-20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline='black', fill='black')
    

def white_key_right_dga(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 10
    y1 = y + 5
    canvas.create_line(x0,y-20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline="black", width=2, fill='white')


def white_key_right_cefb(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 10
    y1 = y + 2.5
    canvas.create_line(x0,y-20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline="black", width=2, fill='white')


def black_key_left(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 6
    y1 = y + 5
    canvas.create_line(x0,y+20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline='black', fill='black')
    canvas.create_oval(x0+4, y0+4, x1-4, y1-4, outline='white', fill='white')
    

def white_key_left_dga(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 10
    y1 = y + 5
    canvas.create_line(x0,y+20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline="black", width=2, fill='white')
    canvas.create_oval(x0+4, y0+4, x1-4, y1-4, outline="black")


def white_key_left_cefb(x, y):  # center coordinates, radius
    x0 = x
    y0 = y - 5
    x1 = x + 10
    y1 = y + 2.5
    canvas.create_line(x0,y+20, x0,y, width=2)
    canvas.create_oval(x0, y0, x1, y1, outline="black", width=2, fill='white')
    canvas.create_oval(x0+4, y0+4, x1-4, y1-4, outline="black")
    

def noteStop(x, y):
    #canvas.create_line(x-5,y-5, x, y, x,y, x-5,y+5, width=2) # orginal klavarscribo design
    #canvas.create_line(x,y, x,y+5, x,y+5, x,y-5, x,y-5, x,y, x,y, x-5,y+5, x-5,y+5, x,y, x,y, x-5,y-5, fill='black', width=1.5) # maybe the pianoscript design
    canvas.create_line(x, y-10, x, y+10, fill='black', width=1.5, dash=3)


def note_y_pos(note, mn, mx, cursy):
	'''
	This function returns the position of c4 relative to 'cursy'(the y axis staff cursor)
	'''	

	if mx >= 81:
		c4 = 230
	if mx >= 76 and mx <= 80:
		c4 = 195
	if mx >= 69 and mx <= 75:
		c4 = 170
	if mx >= 64 and mx <= 68:
		c4 = 135
	if mx >= 57 and mx <= 63:
		c4 = 110
	if mx >= 52 and mx <= 56:
		c4 = 75
	if mx >= 45 and mx <= 51:
		c4 = 50
	if mx >= 40 and mx <= 44:
		c4 = 15
	if mx < 40:
		c4 = 15

	return (cursy + c4) + (40 - note) * 5


def note_on(x1, x2, y, linenr):
	x1 = event_x_pos(x1, linenr)
	x2 = event_x_pos(x2, linenr)
	canvas.create_rectangle(x1, y-5, x2, y+5, fill='#bbbbbb', outline='')#e3e3e3
	canvas.create_line(x2, y-5, x2, y+5, width=2)


def event_x_pos(pos, linenr):
	newlinepos = newline_pos_list(grid, mpline)
	newlinepos.insert(0, 0)
	linelength = newlinepos[linenr] - newlinepos[linenr-1]
	factor = printareawidth / linelength
	pos = pos - newlinepos[linenr-1]
	xpos = pos * factor + 70
	return xpos

##########################################################################
## Main 			 													##
##########################################################################
















## score variables ##
# titles:
title = ''
subtitle = ''
composer = ''
copyright = ''
# settings:
mpline = 4
systemspacing = 50
scale = 100
titlespace = 60
# music:
grid = []
msg = []

## constants ##
paperheigth = root.winfo_fpixels('1m') * 297 * (scale/100)  # a4 210x297 mm
paperwidth = root.winfo_fpixels('1m') * 210 * (scale/100)
marginsx = 40 * (scale/100)
marginsy = 60 * (scale/100)
printareawidth = paperwidth - marginsx
printareaheight = paperheigth - marginsy


def render(q):
	global title, subtitle, composer, copyright, mpline, systemspacing, scale, grid, msg, paperheigth, paperwidth, marginsy, marginsx, printareaheight, printareawidth
	grid = []
	msg = []
	

	def reading():
		global title, subtitle, composer, copyright, mpline, systemspacing, scale, grid, msg, paperheigth, paperwidth, marginsy, marginsx, printareaheight, printareawidth
		file = strip_file(getFile())

		# set titles if in file
		for i in file.split('\n'):
			if 'title' in i:
				index = i.find('=')+1
				i = i[index:]
				title = i
			else:
				pass

			if 'subtitle' in i:
				index = i.find('=')+1
				i = i[index:]
				subtitle = i
			else:
				pass

			if 'composer' in i:
				index = i.find('=')+1
				i = i[index:]
				composer = i
			else:
				pass

			if 'copyright' in i:
				index = i.find('=')+1
				i = i[index:]
				copyright = i
			else:
				pass

			if 'mpline' in i:
				index = i.find('=')+1
				i = i[index:]
				mpline = int(i)
			else:
				pass

			if 'mgrid' in i:
				index = i.find('=')+1
				i = i[index:]
				mgrid = int(i)
			else:
				pass

			if 'systemspace' in i:
				index = i.find('=')+1
				i = i[index:]
				systemspacing = int(i)
			else:
				pass

			if 'scale' in i:
				index = i.find('=')+1
				i = i[index:]
				scale = int(i)
				paperheigth = root.winfo_fpixels('1m') * 297 * (scale/100)  # a4 210x297 mm
				paperwidth = root.winfo_fpixels('1m') * 210 * (scale/100)
				printareawidth = paperwidth - marginsx
				printareaheight = paperheigth - marginsy
			else:
				pass

			if 'grid' in i:
				i = i.split('.')
				i[1] = durationConverter(i[1])
				i[2] = eval(i[2])
				i[3] = eval(i[3])
				grid.append(i)
			else:
				pass










		### reading voices ##
		msgs = bracketExtractor(file, '{', '}')
		V = []
		for v in msgs:
			V.append("".join(v.split()))
		msgs = V
		

		## create messages in list from file ##
		voicelist = []
		ncounter = 0
		prevnote = 0
		for mess in msgs:
			voicelist.insert(0, [])
			splitnote = []
			
			
			# UP
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('R')
				if fnd == 0:
					voicelist[0].append([index, 'hand', 'R'])
				else:
					pass

			
			# DOWN
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('L')
				if fnd == 0:
					voicelist[0].append([index, 'hand', 'L'])
				else:
					pass


			# rest
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('r')
				if fnd == 0:
					voicelist[0].append([index, 'rest'])
				else:
					pass


			# duration
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('<')
				if fnd == 0:
					d = mess[index+1:mess[index:].find('>') + index] # reads duration in string format.
					duration = durationConverter(d)
					voicelist[0].append([index, 'duration', duration])
				else:
					pass


			# note
			index = -1
			for symbol in mess:
				index += 1
				ncounter += 1
				if symbol in ['c', 'C', 'd', 'D', 'e', 'f', 'F', 'g', 'G', 'a', 'A', 'b']:
					if mess[index+1] in ['1', '2', '3', '4', '5', '6', '7', '8', '0']:
						note = string2pitch(mess[index]+mess[index+1])
						prevnote = note
						try:	
							if mess[index+2] == '-':#if '-' after note
								voicelist[0].append([index, 'note', note, 1, ncounter])
							else:
								voicelist[0].append([index, 'note', note, 0, ncounter])
						except IndexError:
							voicelist[0].append([index, 'note', note, 0, ncounter])
					else:
						pass
				else:
					pass


			# cursor
			index = -1
			for symbol in mess:
				index += 1
				ret = '0'
				dig = ['0','1','2','3','4','5','6','7','8','9']
				if symbol.find('_') >= 0:
					for i in mess[index+1:]:
						if i in dig:
							if ret == '0':
								ret = i
							else:
								ret += i
						elif not i in dig:
							voicelist[0].append([index, 'cursor', eval(ret)])
							break
						else:
							voicelist[0].append([index, 'cursor', 0])
					else:
						pass
				else:
					pass


			# split note (add split note)
			index = -1
			for symbol in mess:
				index += 1
				if symbol.find('=') >= 0:
					voicelist[0].append([index, 'split', prevnote])
				else:
					pass


			# repeats
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('[')
				if fnd == 0:
					voicelist[0].append([index, 'bgn_rpt'])
				else:
					pass
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find(']')
				if fnd == 0:
					voicelist[0].append([index, 'end_rpt'])
				else:
					pass

			# dashed barline
			index = -1
			for symbol in mess:
				index += 1
				fnd = symbol.find('|')
				if fnd == 0:
					voicelist[0].append([index, 'dashline'])
				else:
					pass

		
		# sort events by index
		for mess in voicelist:
			mess.sort()












		## creating messages for note, barlines, and split with correct begin times ##
		for voice in voicelist:
			
			#default values for every new voice
			hand = 'UP'
			duration = 256
			cursor = 0


			for event in voice:
				if event[1] == 'hand':
					hand = event[2]
				else:
					pass

				if event[1] == 'duration':
					duration = event[2]
				else:
					pass

				if event[1] == 'cursor':
					if event[2] == 0:
						cursor -= duration
					else:
						cursor = barline_pos_list(grid)[event[2]-1]
				else:
					pass

				if event[1] == 'rest':
					cursor += duration
				else:
					pass

				if event[1] == 'note':
					if event[3] == 1:
						msg.append([event[0], 'note', cursor, cursor+duration, event[2], hand, 'bound'])
					else:
						msg.append([event[0], 'note', cursor, cursor+duration, event[2], hand, 'loose'])
					cursor += duration
				else:
					pass

				if event[1] == 'split':
					note = msg[-1][4]
					msg.append([event[0], 'split', cursor, cursor+duration, note])
					cursor += duration
				else:
					pass

				if event[1] == 'dashline':
					msg.append([event[0], 'dashline', cursor])
				else:
					pass

				if event[1] == 'bgn_rpt':
					msg.append([event[0], 'bgn_rpt', cursor])
				else:
					pass

				if event[1] == 'end_rpt':
					msg.append([event[0], 'end_rpt', cursor])
				else:
					pass


		#adding barline messages with correct begin time
		for barline in barline_pos_list(grid):
			msg.insert(0, ['index', 'barline', barline])


		# adding grid messages
		icount = -1
		cursor = 0
		grdpart = []
		for i in grid:
			oldpos = 0
			for add in range(i[3]):
				length = i[1]
				divide = i[2]
				if divide == 0:
					divide = 1
				amount = i[2]
				for line in range(amount):
					gridpart = length / divide
					time = cursor + (gridpart * (line+1))
					grdpart.append(['dashline', time])
				cursor += length

		for barline in grdpart:
			msg.insert(0, ['index', 'dash', barline[1]])


		# sort on starttime of event to get the barlines in the right order
		msg.sort(key=lambda x: x[2])

		
		##  placing messages in lists of 'lines' ##
		newlinepos = newline_pos_list(grid, mpline)
		mem = 0
		msgs = msg
		msg = []
		bottpos = 0
		for newln in newlinepos:
			hlplst = []
			for note in msgs:
				if note[2] >= bottpos and note[2] < newln:
					hlplst.append(note)
			msg.append(hlplst)
			bottpos = newln
		

		## fitting the 'lines' into pages ##
		lineheight = []
		for line in msg:

			notelst = []
			for note in line:
				if note[1] == 'note':
					notelst.append(note[4])
				else:
					pass
			try: lineheight.append(staff_height(min(notelst), max(notelst)))
			except ValueError: lineheight.append(10)

		msgs = msg
		msg = []
		cursy = 40 * (scale/100)
		pagelist = []
		icount = 0
		for line, height in zip(msgs, lineheight):
			icount += 1
			cursy += height + systemspacing
			if icount == len(lineheight):#if this is the last iteration
				if cursy <= printareaheight:
					pagelist.append(line)
					msg.append(pagelist)
					break
				elif cursy > printareaheight:
					msg.append(pagelist)
					pagelist = []
					pagelist.append(line)
					msg.append(pagelist)
					break
				else:
					pass
			else:
				if cursy <= printareaheight:
					pagelist.append(line)
				elif cursy > printareaheight:
					msg.append(pagelist)
					pagelist = []
					pagelist.append(line)
					cursy = 0
					cursy += height + systemspacing
				else:
					pass
	reading()









	def drawing():
		canvas.delete('all')

		def paper():

			counter = 0
			page_y = 0
			
			for page in msg:
				counter += 1
				draw_paper(page_y)
				canvas.create_text(80, page_y+20+paperheigth, text=f'page {counter} of {len(msg)} | {title} | {copyright} - PianoScript sheet', anchor='w')
				#canvas.create_rectangle(70, page_y+5+paperheigth, 70+printareawidth, page_y+35+paperheigth)

				page_y += paperheigth + 50

			canvas.create_text(70, 90, text=title, anchor='w', font=("Terminal", 16, "normal"))
			canvas.create_text(70+printareawidth, 90, text=composer, anchor='e', font=("Courier", 12, "normal"))
		
		def note_active():
			cursy = 90 + titlespace
			lcounter = 0
			pcounter = 0
			for page in msg:
				pcounter += 1
				
				for line in page:
					lcounter += 1
					#create linenotelist
					linenotelist = []
					for note in line:
						if note[1] == 'note':
							linenotelist.append(note[4])
					if linenotelist:
						minnote = min(linenotelist)
						maxnote = max(linenotelist)
					else:
						minnote = 40
						maxnote = 44

					for note in line:
						if note[1] == 'note':
							note_on(note[2], note[3], note_y_pos(note[4], minnote, maxnote, cursy), lcounter)
							prevnote = note[3]
						if note[1] == 'split':
							note_on(note[2], note[3], note_y_pos(note[4], minnote, maxnote, cursy), lcounter)

					cursy += staff_height(minnote, maxnote) + systemspacing

				cursy = (paperheigth+50) * pcounter + 90


		def barline():
			cursy = 90 + titlespace
			pcounter = 0
			lcounter = 0
			bcounter = 0
			
			for page in msg:
				pcounter += 1
				
				
				for line in page:
					lcounter += 1
					
					#create linenotelist
					linenotelist = []
					for note in line:
						if note[1] == 'note'  or note[1] == 'split':
							linenotelist.append(note[4])
					if linenotelist:
						maxnote = max(linenotelist)
						minnote = min(linenotelist)
					else:
						maxnote = 44
						minnote = 40

					staffheight = staff_height(minnote, maxnote)

					for note in line:
		
						if note[1] == 'barline':
							bcounter += 1
							canvas.create_line(event_x_pos(note[2], lcounter), cursy, event_x_pos(note[2], lcounter), cursy+staffheight, width=2)
							canvas.create_text(event_x_pos(note[2]+7.5, lcounter), cursy-20, text=bcounter, anchor='w')
					canvas.create_line(70+printareawidth, cursy, 70+printareawidth, cursy+staffheight, width=2)
					
					if lcounter == len(newline_pos_list(grid, mpline)):
						canvas.create_line(70+printareawidth, cursy, 70+printareawidth, cursy+staffheight, width=5)
					
					
					cursy += staffheight + systemspacing

				cursy = (paperheigth+50) * pcounter + 90


		def staff():
			cursy = 90 + titlespace
			pcounter = 0
			lcounter = 0
			for page in msg:
				pcounter += 1
				
				
				for line in page:
					lcounter += 1
					#create linenotelist
					linenotelist = []
					for note in line:
						if note[1] == 'note' or note[1] == 'split':
							linenotelist.append(note[4])
					if linenotelist:
						maxnote = max(linenotelist)
						minnote = min(linenotelist)
					else:
						maxnote = 44
						minnote = 40

					draw_staff_lines(cursy, minnote, maxnote)
					#canvas.create_text(25, cursy+5, text=lcounter)
					staffheight = staff_height(minnote, maxnote)
					
					cursy += staffheight + systemspacing

				cursy = (paperheigth+50) * pcounter + 90


		def note_start():
			black = [2, 5, 6, 10, 12, 14, 17, 19, 22, 24, 26, 29, 31, 34, 36, 38, 41, 43, 46, 48, 50, 53, 55, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 82, 84, 86]
			white_dga = [6,11,13,18,23,25,30,35,37,42,47,49,54,59,61,66,71,73,78,83,85,88]
			white_be = [3,8,15,20,27,32,39,44,51,56,63,68,75,80,87] # possible typos
			white_cf = [1,4,9,16,21,28,33,40,45,52,57,64,69,76,81] # possible typos
			
			cursy = 90 + titlespace
			pcounter = 0
			lcounter = 0
			for page in msg:
				pcounter += 1

				for line in page:
					lcounter += 1


					# create max/min note variables for line
					linenotelist = []
					for note in line:
						if note[1] == 'note':
							linenotelist.append(note[4])
					if linenotelist:
						minnote = min(linenotelist)
						maxnote = max(linenotelist)
					else:
						minnote = 40
						maxnote = 44
					
					staffheight = staff_height(minnote, maxnote)



					notelst = []
					for note in line:
						if note[1] == 'note':
							notelst.append(note)

					notelst.sort(key=lambda x: x[0])


					old_x = 0
					old_y = 0
					boundloose = 0

					for note in notelst:

						if note[1] == 'note':

							if note[4] in white_dga:
							
								if note[5] == 'R':
									white_key_right_dga(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								elif note[5] == 'L':
									white_key_left_dga(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								# elif note[0] == 'split':
								# 	print('!!!')
								else:
									pass

							if note[4] in white_cf:
								
								if note[5] == 'R':
									white_key_right_cefb(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								elif note[5] == 'L':
									white_key_left_cefb(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								# elif note[0] == 'split':
								# 	print('!!!')
								else:
									pass

							if note[4] in white_be:
								
								if note[5] == 'R':
									white_key_right_cefb(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy)+1.5)
								elif note[5] == 'L':
									white_key_left_cefb(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy)+1.5)
								# elif note[0] == 'split':
								# 	print('!!!')
								else:
									pass

							if note[4] in black:
								
								if note[5] == 'R':
									black_key_right(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								elif note[5] == 'L':
									black_key_left(event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy))
								else:
									pass

							
							if boundloose == 1:
								if note[5] == 'R':
									canvas.create_line(old_x, old_y, event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy)-20, width=3)
								elif note[5] == 'L':
									canvas.create_line(old_x, old_y, event_x_pos(note[2], lcounter), note_y_pos(note[4], minnote, maxnote, cursy)+20, width=3)

							if note[6] == 'bound':
								boundloose = 1
								old_x = event_x_pos(note[2], lcounter)
								if note[5] == 'R':
									old_y = note_y_pos(note[4], minnote, maxnote, cursy)-20
								elif note[5] == 'L':
									old_y = note_y_pos(note[4], minnote, maxnote, cursy)+20
								else:
									pass
							elif note[6] == 'loose':
								boundloose = 0
							else:
								pass



					cursy += staffheight + systemspacing

				cursy = (paperheigth+50) * pcounter + 90


		def grid_lines():
			cursy = 90 + titlespace
			pcounter = 0
			lcounter = 0
			for page in msg:
				pcounter += 1

				for line in page:
					lcounter += 1

					# create max/min note variables for line
					linenotelist = []
					for note in line:
						if note[1] == 'note':
							linenotelist.append(note[4])
					if linenotelist:
						minnote = min(linenotelist)
						maxnote = max(linenotelist)
					else:
						minnote = 40
						maxnote = 44
					
					staffheight = staff_height(minnote, maxnote)

					for gridline in line:
						if gridline[1] == 'dash':
							canvas.create_line(event_x_pos(gridline[2], 
												lcounter), 
												cursy+(staffheight*0.25), 
												event_x_pos(gridline[2], 
												lcounter), 
												cursy+staffheight-(staffheight*0.25), 
												dash=(6, 6))

					cursy += staffheight + systemspacing

				cursy = (paperheigth+50) * pcounter + 90


		# function order
		paper()
		note_active()
		barline()
		staff()
		grid_lines()
		note_start()

	drawing()
	canvas.configure(scrollregion=bbox_offset(canvas.bbox("all")))
	return len(msg)









def exportPDF():
	print('exportPDF')

	f = filedialog.asksaveasfile(mode='w', parent=root, filetypes=[("Postscript","*.ps")])

	if f:
		name = f.name[:-3]
		counter = 0

		for export in range(render('q')):
			counter += 1
			export = export
			canvas.postscript(file=f"{name}{counter}.ps", colormode='gray', x=50, y=50+(export*(paperheigth+50)), width=paperwidth, height=paperheigth, rotate=False)

	else:

		pass

	return




def generate_pdf():
	canvas.update()
	canvas.postscript(file="tmp.ps", colormode='gray', x=50, y=50, width=paperwidth, height=paperheigth, rotate=False)
	process = subprocess.Popen(["ps2pdf", "tmp.ps", "result.pdf"], shell=True)
	process.wait()
	#os.remove("tmp.ps")

menu.add_command(label ="PDF to desktop", command=exportPDF)

def autosave(q):
	global filepath
	if filepath == 'New':
		render('q')
	else:
		saveFile()
		render('q')









newFile()
#exportPDF()
root.bind('<Escape>', render)
root.bind('<Escape>', autosave)
root.mainloop()
(hit escape to render the score)
At the bottom you can see I was experimenting a bit. the subprocess 'ps2pdf' behaves weird. Sometimes it outputs a pdf, sometimes not. I am talking about:
def generate_pdf():
	canvas.update()
	canvas.postscript(file="tmp.ps", colormode='gray', x=50, y=50, width=paperwidth, height=paperheigth, rotate=False)
	process = subprocess.Popen(["ps2pdf", "tmp.ps", "result.pdf"], shell=True)
	process.wait()
	#os.remove("tmp.ps")
Any tips and tricks about my programming are welcome too :)
Reply
#2
If ps2pdf doesn't work well and you are in Linux, you could try to use the CUPS-PDF printer for the conversion. Once it is installed you can print to a pdf file with the command
Output:
lpr -PPDF myfile.ps
The drawback is that it will store your file in the ${HOME}/PDF directory, and you'll have to move the file from there to the destination path. The destination directory can be changed only by editing the pdf printer's configuration file and I don't know any way to specify the name of the created file on the command line.

However, it could help if you are experimenting troubles in converting a specific postscript file.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020