Skip to content

Commit d38fea7

Browse files
committed
Command line support added
1 parent 32dfaeb commit d38fea7

File tree

1 file changed

+63
-44
lines changed

1 file changed

+63
-44
lines changed

viewer.py

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
__copyright__ = '(c) 2025 Ilya Razmanov'
1616
__credits__ = 'Ilya Razmanov'
1717
__license__ = 'unlicense'
18-
__version__ = '2.19.1.7'
18+
__version__ = '2.20.4.20'
1919
__maintainer__ = 'Ilya Razmanov'
2020
__email__ = 'ilyarazmanov@gmail.com'
2121
__status__ = 'Production'
2222

23+
from pathlib import Path
2324
from platform import python_version
24-
from tkinter import Button, Frame, Label, Menu, PhotoImage, Tk, filedialog
25+
from sys import argv
26+
from time import ctime # Used to show file info only
27+
from tkinter import Button, Frame, Label, Menu, PhotoImage, Tk
28+
from tkinter.filedialog import askopenfilename, asksaveasfilename
2529
from tkinter.messagebox import showinfo
2630

2731
from pypnm import pnmlpnm
@@ -32,6 +36,14 @@ def DisMiss(event=None) -> None:
3236
sortir.destroy()
3337

3438

39+
def BindAll() -> None:
40+
"""Binding events needed even with no image open"""
41+
sortir.bind_all('<Button-3>', ShowMenu)
42+
sortir.bind_all('<Alt-f>', ShowMenu)
43+
sortir.bind_all('<Control-o>', GetSource)
44+
sortir.bind_all('<Control-q>', DisMiss)
45+
46+
3547
def UINormal() -> None:
3648
"""Normal UI state"""
3749
zanyato.config(state='normal')
@@ -54,41 +66,41 @@ def ShowInfo(event=None) -> None:
5466
showinfo(
5567
title='General information',
5668
message=f'PNMViewer ver. {__version__}\nPython: {python_version()}\nModules:\n{pnmlpnm.__name__} ver. {pnmlpnm.__version__}',
57-
detail=f'Image: {sourcefilename}\nX={X}, Y={Y}, Z={Z}, maxcolors={maxcolors}',
69+
detail=f'File:\n{sourcefilename}\nSize: {(Path(sourcefilename).stat().st_size / 1024):.2f} kb\nCreated: {ctime(Path(sourcefilename).stat().st_birthtime)}\nModified: {ctime(Path(sourcefilename).stat().st_mtime)}\n\nImage: X={X}, Y={Y}, Z={Z}, maxcolors={maxcolors}',
5870
)
5971

6072

6173
def GetSource(event=None) -> None:
6274
"""Opening source image and redefining other controls state"""
6375
global zoom_factor, zoom_do, zoom_show, preview, preview_data
64-
global X, Y, Z, maxcolors, image3D, sourcefilename
76+
global X, Y, Z, maxcolors, image3D, sourcefilename, filename_from_command
6577
zoom_factor = 0
6678

67-
sourcefilename = filedialog.askopenfilename(title='Open PPM/PGM file to view', filetypes=[('Portable map formats', '.ppm .pgm .pbm')])
68-
if sourcefilename == '':
69-
return
79+
# ↓ Trying to receive file name from command line, if None, opening GUI
80+
if filename_from_command is None:
81+
sourcefilename = askopenfilename(title='Open PPM/PGM file to view', filetypes=[('Portable map formats', '.ppm .pgm .pbm')], initialfile=filename_from_command)
82+
if sourcefilename == '':
83+
return
84+
else:
85+
sourcefilename = filename_from_command
86+
filename_from_command = None # Removing file name after first open
7087

7188
UIBusy()
7289

73-
""" ┌────────────────────────────────────────┐
74-
│ Loading file, converting data to list. │
75-
│ NOTE: maxcolors, image3D are GLOBALS! │
76-
│ They are used during save! │
77-
└────────────────────────────────────────┘ """
78-
90+
# ↓ Loading file, converting data to list.
91+
# NOTE: maxcolors, image3D are GLOBALS!
92+
# They are used during save!
7993
X, Y, Z, maxcolors, image3D = pnmlpnm.pnm2list(sourcefilename)
8094

81-
""" ┌─────────────────────────────────────────────────────────────────────────┐
82-
│ Converting list to bytes of PPM-like structure "preview_data" in memory │
83-
└────────────────────────────────────────────────────────────────────────-┘ """
95+
# ↓ Converting list to bytes of PPM-like structure "preview_data" in memory
8496
preview_data = pnmlpnm.list2bin(image3D, maxcolors)
8597

86-
""" ┌────────────────────────────────────────────────┐
87-
│ Now showing "preview_data" bytes using Tkinter │
88-
└────────────────────────────────────────────────┘ """
98+
# ↓ Now showing "preview_data" bytes using Tkinter
8999
preview = PhotoImage(data=preview_data)
90-
91-
zoom_show = { # Text to show below preview
100+
# ↓ Adding filename to window title a-la Photoshop
101+
sortir.title(f'PNMViewer: {Path(sourcefilename).name}')
102+
# ↓ Dictionary of zoom label texts
103+
zoom_show = {
92104
-4: 'Zoom 1:5',
93105
-3: 'Zoom 1:4',
94106
-2: 'Zoom 1:3',
@@ -99,7 +111,8 @@ def GetSource(event=None) -> None:
99111
3: 'Zoom 4:1',
100112
4: 'Zoom 5:1',
101113
}
102-
zoom_do = { # Actions to zoom preview; "zoom" zooms in, "subsample" zooms out
114+
# ↓ Dictionary of zoom functions, corresponding to "zoom_show" above
115+
zoom_do = {
103116
-4: preview.subsample(5, 5),
104117
-3: preview.subsample(4, 4),
105118
-2: preview.subsample(3, 3),
@@ -113,21 +126,22 @@ def GetSource(event=None) -> None:
113126

114127
preview = zoom_do[zoom_factor]
115128
zanyato.config(image=preview, compound='none', borderwidth=1, background=zanyato.master['background'])
116-
# binding zoom on preview click
129+
# binding zoom on preview click
117130
zanyato.bind('<Control-Button-1>', zoomIn) # Ctrl + left click
118131
zanyato.bind('<Double-Control-Button-1>', zoomIn) # Ctrl + left click too fast
119132
zanyato.bind('<Alt-Button-1>', zoomOut) # Alt + left click
120133
zanyato.bind('<Double-Alt-Button-1>', zoomOut) # Alt + left click too fast
121134
sortir.bind_all('<MouseWheel>', zoomWheel) # Wheel
122135
sortir.bind_all('<Control-i>', ShowInfo)
123-
# enabling zoom buttons
136+
# enabling zoom buttons
124137
butt_plus.config(state='normal', cursor='hand2')
125138
butt_minus.config(state='normal', cursor='hand2')
126-
# updating zoom label display
139+
# updating zoom label display
127140
label_zoom.config(text=zoom_show[zoom_factor])
128-
# enabling "Save as..."
141+
# enabling "Save as..."
129142
menu01.entryconfig('Save binary PNM...', state='normal') # Instead of name numbers from 0 may be used
130143
menu01.entryconfig('Save ascii PNM...', state='normal')
144+
menu01.entryconfig('Info', state='normal')
131145

132146
UINormal()
133147

@@ -143,18 +157,16 @@ def SaveAsPNM(bin: bool) -> None:
143157
extension = ('Portable pixel map', '.ppm')
144158
filetype = 'PPM'
145159

146-
# Open "Save as..." file
147-
savefilename = filedialog.asksaveasfilename(
160+
# Open "Save as..." file
161+
savefilename = asksaveasfilename(
148162
title=f'Save {filetype} file',
149163
filetypes=format,
150164
defaultextension=extension,
151165
)
152166
if savefilename == '':
153167
return
154168

155-
""" ┌────────────────────────────────────────────────────┐
156-
│ Saving "savefilename" in format depending on "bin" │
157-
└───────────────────────────────────────────────────-┘ """
169+
# ↓ Saving "savefilename" in PNM format depending on "bin"
158170
UIBusy()
159171
pnmlpnm.list2pnm(savefilename, image3D, maxcolors, bin)
160172
UINormal()
@@ -167,9 +179,9 @@ def zoomIn(event=None) -> None:
167179
preview = PhotoImage(data=preview_data)
168180
preview = zoom_do[zoom_factor]
169181
zanyato.config(image=preview, compound='none')
170-
# updating zoom factor display
182+
# updating zoom factor display
171183
label_zoom.config(text=zoom_show[zoom_factor])
172-
# reenabling +/- buttons
184+
# reenabling +/- buttons
173185
butt_minus.config(state='normal', cursor='hand2')
174186
if zoom_factor == 4: # max zoom 5
175187
butt_plus.config(state='disabled', cursor='arrow')
@@ -184,9 +196,9 @@ def zoomOut(event=None) -> None:
184196
preview = PhotoImage(data=preview_data)
185197
preview = zoom_do[zoom_factor]
186198
zanyato.config(image=preview, compound='none')
187-
# updating zoom factor display
199+
# updating zoom factor display
188200
label_zoom.config(text=zoom_show[zoom_factor])
189-
# reenabling +/- buttons
201+
# reenabling +/- buttons
190202
butt_plus.config(state='normal', cursor='hand2')
191203
if zoom_factor == -4: # min zoom 1/5
192204
butt_minus.config(state='disabled', cursor='arrow')
@@ -210,12 +222,9 @@ def zoomWheel(event) -> None:
210222
sourcefilename = X = Y = Z = maxcolors = None
211223

212224
sortir = Tk()
213-
214225
sortir.title('PNMViewer')
215226
sortir.geometry('+200+100')
216227
sortir.minsize(128, 128)
217-
218-
# Main dialog icon is PPM as well!
219228
sortir.iconphoto(True, PhotoImage(data=b'P6\n2 2\n255\n\xff\x00\x00\xff\xff\x00\x00\x00\xff\x00\xff\x00'))
220229

221230
menu01 = Menu(sortir, tearoff=False) # Main menu, currently one "File" entry
@@ -225,15 +234,10 @@ def zoomWheel(event) -> None:
225234
menu01.add_command(label='Save binary PNM...', state='disabled', command=lambda: SaveAsPNM(bin=True))
226235
menu01.add_command(label='Save ascii PNM...', state='disabled', command=lambda: SaveAsPNM(bin=False))
227236
menu01.add_separator()
228-
menu01.add_command(label='Info', accelerator='Ctrl+I', command=ShowInfo)
237+
menu01.add_command(label='Info', accelerator='Ctrl+I', state='disabled', command=ShowInfo)
229238
menu01.add_separator()
230239
menu01.add_command(label='Exit', state='normal', accelerator='Ctrl+Q', command=DisMiss)
231240

232-
sortir.bind('<Button-3>', ShowMenu)
233-
sortir.bind_all('<Alt-f>', ShowMenu)
234-
sortir.bind_all('<Control-o>', GetSource)
235-
sortir.bind_all('<Control-q>', DisMiss)
236-
237241
frame_img = Frame(sortir, borderwidth=2, relief='groove')
238242
frame_img.pack(side='top')
239243

@@ -265,4 +269,19 @@ def zoomWheel(event) -> None:
265269
label_zoom = Label(frame_zoom, text='Zoom 1:1', font=('courier', 8), state='disabled')
266270
label_zoom.pack(side='left', anchor='n', padx=2, pady=0, fill='both')
267271

272+
BindAll()
273+
274+
# ↓ Command line part
275+
if len(argv) == 2:
276+
try_to_open = argv[1]
277+
if Path(try_to_open).exists() and Path(try_to_open).is_file() and (Path(try_to_open).suffix in ('.ppm', '.pgm', '.pbm')):
278+
filename_from_command = str(Path(try_to_open).resolve())
279+
GetSource()
280+
else:
281+
filename_from_command = None
282+
sortir.focus_force() # Otherwise loses focus when run from command line
283+
else:
284+
filename_from_command = None
285+
sortir.focus_force() # Otherwise loses focus when run from command line
286+
268287
sortir.mainloop()

0 commit comments

Comments
 (0)