Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a820a83

Browse files
Multiple Updates
1. App Close Save Prompt 2. Save As file dialog update 3. Application Icon 4. Page Setup dialog 5. Document print.
1 parent 2b696b5 commit a820a83

File tree

2 files changed

+129
-61
lines changed

2 files changed

+129
-61
lines changed

‎Sample GUI Implementation/notepad.ico

66.1 KB
Binary file not shown.

‎Sample GUI Implementation/notepad.py

Lines changed: 129 additions & 61 deletions
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://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows2.py
150-
# https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Design_Pattern_Multiple_Windows.py
151-
# https://github.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://github.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+
globaltext_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, '')andnotisinstance(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-
ifEVENTin (sg.WINDOW_CLOSED, 'Exit', '-EXITTHEME-'):
298-
# exit out of the application is close or exit clicked.
299-
WIN.close()
300-
ifWIN==window2: # if closing win 2, mark as closed
301-
window2=None
302-
else: # if closing win 1, exit program
303-
break
355+
ifuser_prompt_action=='Yes':
356+
save_file(FILE_NAME)
357+
elifuser_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)

AltStyle によって変換されたページ (->オリジナル) /