File tree

2 files changed

+129
-61
lines changed

2 files changed

+129
-61
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# pylint: disable=no-member
33
# pylint: disable=invalid-name
44

5+
import os
56
import shlex
67
from tkinter import Tk
78
import wx
@@ -15,16 +16,31 @@
1516
wx_app = [] # pylint: disable=unused-variable
1617
wx_app = wx.App(None)
1718

19+
# get the current working directory.
20+
CURRENT_WORKING_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
21+
APPLICATION_ICON = CURRENT_WORKING_DIRECTORY + '\\notepad.ico'
1822

1923
# change the default theme.
20-
# sg.theme('dark grey 9')
24+
sg.theme('dark grey 9')
2125

2226
WINDOW_WIDTH: int = 90
2327
WINDOW_HEIGHT: int = 25
2428
FILE_NAME: str = None
2529
DEFAULT_FONT_NAME: str = 'Times New Roman'
2630
APP_NAME: str = 'NotepadPy+'
2731
SELECTED_THEME: str = ''
32+
text_to_save: str = ''
33+
# this is needed to control the displaying of the user prompt while closing.
34+
# If the user closes the document just before closing the document,
35+
# we do not want to display the prompt for save changes.
36+
text_last_saved_manually: str = ''
37+
38+
39+
# initialize the print data and set some default values
40+
pdata = wx.PrintData()
41+
pdata.SetPaperId(wx.PAPER_A3)
42+
pdata.SetOrientation(wx.PORTRAIT)
43+
margins = (wx.Point(15, 15), wx.Point(15, 15))
2844

2945
def ShowFontDialog():
3046
'''Get a font dialog to display and return all the
@@ -84,32 +100,49 @@ def ShowPrintDialog():
84100
data.SetMinPage(1)
85101
data.SetMaxPage(10)
86102

87-
dialog = wx.PrintDialog(None, data)
88-
89103
text_to_print = VALUES['-BODY-']
104+
# lines_to_print = text_to_print.split('\n')
105+
106+
dialog = wx.PrintDialog(None, data)
90107
if dialog.ShowModal() == wx.ID_OK:
91108
data = dialog.GetPrintDialogData()
92-
dc = dialog.GetPrintDC()
109+
data.GetPrintData().SetPaperId(wx.PAPER_A3)
93110

111+
dc = dialog.GetPrintDC()
94112
dc.StartDoc("MyDoc")
95113
dc.StartPage()
96114
dc.SetMapMode(wx.MM_POINTS)
97115

98116
dc.SetTextForeground("black")
99-
dc.DrawText(text_to_print, 50, 100)
117+
dc.DrawText(text_to_print, margins[0][0], margins[1][0])
100118

101119
dc.EndPage()
102120
dc.EndDoc()
103121
del dc
104122

105-
printer = wx.Printer(data)
106-
printer_config = wx.PrintData(printer.GetPrintDialogData().GetPrintData())
107-
108-
# print('GetAllPages: %d\n' % data.GetAllPages())
109-
110-
123+
# printer = wx.Printer(data)
111124
dialog.Destroy()
112125

126+
def ShowPageSetupDialog():
127+
'''display the page setup dialog.'''
128+
global pdata
129+
global margins
130+
data = wx.PageSetupDialogData()
131+
data.SetPrintData(pdata)
132+
133+
data.SetDefaultMinMargins(True)
134+
data.SetMarginTopLeft(margins[0])
135+
data.SetMarginBottomRight(margins[1])
136+
137+
dlg = wx.PageSetupDialog(None, data)
138+
if dlg.ShowModal() == wx.ID_OK:
139+
data = dlg.GetPageSetupData()
140+
pdata = wx.PrintData(data.GetPrintData()) # force a copy
141+
pdata.SetPaperId(data.GetPaperId())
142+
margins = (data.GetMarginTopLeft(), data.GetMarginBottomRight())
143+
144+
dlg.Destroy()
145+
113146
def rgb2hex(r, g, b):
114147
'''Convert RGB to hex values.'''
115148
return "#{:02x}{:02x}{:02x}".format(r, g, b)
@@ -127,7 +160,7 @@ def rgb2hex(r, g, b):
127160
edit_delete: str = 'Delete Del'
128161

129162

130-
menu_layout: list = [['&File', [file_new, file_open, file_save, 'Save As', '______________________', file_print, '______________________', 'Exit']],
163+
menu_layout: list = [['&File', [file_new, file_open, file_save, 'Save As', '______________________', 'Page Setup', file_print, '______________________', 'Exit']],
131164
['&Edit', [edit_cut, edit_copy, edit_paste, edit_delete]],
132165
['&Statistics', ['Word Count', 'Line Count', 'Character With Spaces', 'Character Without Spaces', ]],
133166
['F&ormat', ['Font', ]],
@@ -140,31 +173,18 @@ def rgb2hex(r, g, b):
140173
size=(WINDOW_WIDTH, WINDOW_HEIGHT), key='-BODY-', reroute_cprint=True)]]
141174

142175
WINDOW = sg.Window('untitled - ' + APP_NAME, layout=layout, margins=(0, 0),
143-
resizable=True, return_keyboard_events=True, finalize=True)
144-
# WINDOW.read(timeout=1)
145-
WINDOW.maximize()
146-
WINDOW['-BODY-'].expand(expand_x=True, expand_y=True)
176+
resizable=True, return_keyboard_events=True, icon=APPLICATION_ICON, finalize=True)
147177

148-
# APPLICATION THEME CHANGING DIALOG - A good place to refer are the following resources -
149-
# https://.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows2.py
150-
# https://.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows.py
151-
# https://.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows1.py
178+
# redefine the callback for window close button by using tkinter code.
179+
# this is required to delay the event of closing the main window incase
180+
# the text is not saved before closing.
181+
# more details @ https://.com/PySimpleGUI/PySimpleGUI/issues/3650
182+
WINDOW.TKroot.protocol("WM_DESTROY_WINDOW", lambda:WINDOW.write_event_value("WIN_CLOSE", ()))
183+
WINDOW.TKroot.protocol("WM_DELETE_WINDOW", lambda:WINDOW.write_event_value("WIN_CLOSE", ()))
152184

153-
def create_theme_browser():
154-
'''Creates a GUI Theme browser dialog to select
155-
and apply the application theme.'''
156-
157-
theme_window_layout = [[sg.Text('Select a theme from the list below and\nclick on Apply for changes to take effect.')],
158-
[sg.Listbox(values=sg.theme_list(), size=(20, 12), key='-THEMELIST-', enable_events=True)],
159-
[sg.Button('Apply', tooltip="Applies the selected theme.", key='-APPLYTHEME-'),
160-
sg.Button('Exit', key='-EXITTHEME-')]]
161-
162-
# Define the second window
163-
# Link it to the first window (master=window)
164-
# Assign a key to the window so that it can be easily referenced
165-
theme_window = sg.Window(title='Theme Browser', layout=theme_window_layout, finalize=True, modal=True)
166-
167-
return theme_window
185+
WINDOW.read(timeout=1)
186+
WINDOW.maximize()
187+
WINDOW['-BODY-'].expand(expand_x=True, expand_y=True)
168188

169189
def new_file() -> str:
170190
''' Reset body and info bar, and clear FILE_NAME variable '''
@@ -185,35 +205,58 @@ def open_file() -> str:
185205

186206
def save_file(file_name: str):
187207
''' Save file instantly if already open; otherwise display `save-as` popup '''
188-
208+
global text_last_saved_manually
189209
# Get the filename if already saved in the same session.
190210
file_name = WINDOW['-FILE_INFO-'].DisplayText
191211
if file_name not in (None, '', 'New File:'):
192212
with open(file_name, 'w') as f:
193-
f.write(VALUES.get('-BODY-'))
194-
WINDOW['-FILE_INFO-'].update(value=file_name)
213+
if VALUES is not None:
214+
f.write(VALUES.get('-BODY-'))
215+
WINDOW['-FILE_INFO-'].update(value=file_name)
216+
else:
217+
f.write(text_to_save)
218+
219+
# this is needed to control the displaying of the user prompt while closing.
220+
# If the user closes the document just before closing the document,
221+
# we do not want to display the prompt for save changes.
222+
text_last_saved_manually = text_to_save
195223
else:
196224
file_name = save_as()
197-
WINDOW.set_title(file_name + ' - ' + APP_NAME)
225+
226+
227+
# We will skip this line while closing the dialog.
228+
if VALUES is not None:
229+
WINDOW.set_title(file_name + ' - ' + APP_NAME)
198230

199231
def save_as() -> str:
200232
''' Save new file or save existing file with another name '''
233+
global text_last_saved_manually
201234
try:
202-
file_name: str = sg.popup_get_file('Save As', save_as=True, no_window=True)
235+
file_name: str = sg.popup_get_file('Save As', save_as=True, no_window=True,
236+
file_types=(('Text Documents', '*.txt'), ('ALL Files', '*.*'),),
237+
modal=True, default_path="*.txt",
238+
icon=APPLICATION_ICON)
203239
except: # pylint: disable=bare-except
204240
return ''
205-
if file_name not in (None, '') and not isinstance(FILE_NAME, tuple):
241+
if file_name not in (None, ''):
206242
with open(file_name, 'w') as f:
207-
f.write(VALUES.get('-BODY-'))
208-
WINDOW['-FILE_INFO-'].update(value=file_name)
243+
if VALUES is not None:
244+
f.write(VALUES.get('-BODY-'))
245+
WINDOW['-FILE_INFO-'].update(value=file_name)
246+
else:
247+
f.write(text_to_save)
248+
# this is needed to control the displaying of the user prompt while closing.
249+
# If the user closes the document just before closing the document,
250+
# we do not want to display the prompt for save changes.
251+
text_last_saved_manually = text_to_save
209252
return file_name
210253

211254
def get_word_count():
212255
''' Get the estimated word count '''
213256
total_words: int = 0
214257
if not validate_text():
215258
sg.PopupQuick('Enter some text to calculate the number of words.',
216-
title='Text Not Found', auto_close=False)
259+
title='Text Not Found', auto_close=False, modal=True)
217260
return 0
218261

219262
lines: list = VALUES['-BODY-'].splitlines()
@@ -238,7 +281,7 @@ def character_count():
238281

239282
if not validate_text():
240283
sg.PopupQuick('Enter some text to calculate the number of characters.',
241-
title='Text Not Found', auto_close=False)
284+
title='Text Not Found', auto_close=False, modal=True)
242285
return 0
243286

244287
chars = len(VALUES['-BODY-']) - 1
@@ -249,7 +292,7 @@ def characters_without_spaces():
249292

250293
if not validate_text():
251294
sg.PopupQuick('Enter some text to calculate the number of characters\nwithout spaces.',
252-
title='Text Not Found', auto_close=False)
295+
title='Text Not Found', auto_close=False, modal=True)
253296
return 0
254297

255298
chars_without_spaces: int = 0
@@ -268,7 +311,7 @@ def get_line_count():
268311

269312
if not validate_text():
270313
sg.PopupQuick('Enter some text to calculate the number of lines.',
271-
title='Text Not Found', auto_close=False)
314+
title='Text Not Found', auto_close=False, modal=True)
272315
return 0
273316

274317
text: str = VALUES['-BODY-']
@@ -286,21 +329,36 @@ def about():
286329
'''About the application'''
287330

288331
sg.PopupQuick('A simple Notepad like application created using\
289-
PySimpleGUI framework.', auto_close=False)
332+
PySimpleGUI framework.', auto_close=False, modal=True)
290333

291-
window1, window2 = WINDOW(), None
292334
# read the events and take appropriate actions.
293335
while True:
294336

295-
WIN, EVENT, VALUES = sg.read_all_windows() #WINDOW.read()
337+
EVENT, VALUES = WINDOW.read()
338+
339+
if EVENT in (sg.WINDOW_CLOSED, 'Exit', "WIN_CLOSE"):
340+
# Get the filename if already saved in the same session.
341+
file_name = WINDOW['-FILE_INFO-'].DisplayText
342+
343+
if file_name not in (None, '') and text_to_save.rstrip() != '' and text_last_saved_manually != text_to_save:
344+
# display a user prompt incase the note is not yet saved asking the
345+
# user 'Do you want to save changes to Untitled?'
346+
user_prompt_msg: str = ''
347+
if file_name == 'New File:':
348+
user_prompt_msg = 'Untitled'
349+
else:
350+
user_prompt_msg = file_name
351+
user_prompt_action = sg.popup_yes_no('Do you want to save changes to ' + user_prompt_msg + "?",
352+
title='NotepayPy+', modal=True,
353+
icon=APPLICATION_ICON)
296354

297-
if EVENT in (sg.WINDOW_CLOSED, 'Exit', '-EXITTHEME-'):
298-
# exit out of the application is close or exit clicked.
299-
WIN.close()
300-
if WIN == window2: # if closing win 2, mark as closed
301-
window2 = None
302-
else: # if closing win 1, exit program
303-
break
355+
if user_prompt_action == 'Yes':
356+
save_file(FILE_NAME)
357+
elif user_prompt_action == 'No':
358+
break
359+
360+
# finally breakout of the event loop and end the application.
361+
break
304362

305363
# file menu events.
306364
if EVENT in (file_new, 'n:78'):
@@ -313,6 +371,8 @@ def about():
313371
FILE_NAME = save_as()
314372
if EVENT in (file_print, 'p:80'):
315373
ShowPrintDialog()
374+
if EVENT == 'Page Setup':
375+
ShowPageSetupDialog()
316376

317377
# edit menu events.
318378
if EVENT == edit_cut:
@@ -338,23 +398,31 @@ def about():
338398
if EVENT in ('Word Count',):
339399
WORDS = get_word_count()
340400
if WORDS != 0:
341-
sg.PopupQuick('Word Count: {:,d}'.format(WORDS), auto_close=False)
401+
sg.PopupQuick('Word Count: {:,d}'.format(WORDS), auto_close=False, modal=True)
342402
if EVENT in ('Line Count',):
343403
LINES = get_line_count()
344404
if LINES != 0:
345-
sg.PopupQuick('Line Count: {:,d}'.format(LINES), auto_close=False)
405+
sg.PopupQuick('Line Count: {:,d}'.format(LINES), auto_close=False, modal=True)
346406
if EVENT in ('Character With Spaces',):
347407
CHARS = character_count()
348408
if CHARS != 0:
349-
sg.PopupQuick('Characters With Spaces: {:,d}'.format(CHARS), auto_close=False)
409+
sg.PopupQuick('Characters With Spaces: {:,d}'.format(CHARS),
410+
auto_close=False, modal=True)
350411
if EVENT in ('Character Without Spaces',):
351412
CHAR_WITHOUT_SPACES = characters_without_spaces()
352413
if CHAR_WITHOUT_SPACES != 0:
353414
sg.PopupQuick('Characters Without Spaces: {:,d}'.format(CHAR_WITHOUT_SPACES),
354-
auto_close=False)
415+
auto_close=False, modal=True)
355416
if EVENT in ('About',):
356417
about()
357418

358419
# Format Menu
359420
if EVENT in ('Font',):
360421
ShowFontDialog()
422+
423+
# record the text after each event to ensure the
424+
# file/text is saved.
425+
try:
426+
text_to_save = VALUES['-BODY-']
427+
except:
428+
pass

0 commit comments

Comments
 (0)