Skip to content

Commit 3d3a4f7

Browse files
committed
General usability improvements.
1 parent 44573bf commit 3d3a4f7

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

Averager.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""Adaptive color average image filtering.
44
-------------------------------------------
55
6-
Average image colors in a pixel row until difference between averaged and next pixel in row reach threshold. Then repeat the same in column. Thus filter changes smooth image areas to completely flat colored, with detailed edges between them.
6+
Average image colors in a pixel row until difference between averaged and next pixel in a row reach threshold. Then repeat the same in column. Thus filter changes smooth image areas to completely flat colored, with detailed edges between them.
77
88
Input: PNG, PPM, PGM, PBM.
99
@@ -38,7 +38,7 @@
3838
__copyright__ = '(c) 2024-2025 Ilya Razmanov'
3939
__credits__ = 'Ilya Razmanov'
4040
__license__ = 'unlicense'
41-
__version__ = '3.22.11.11'
41+
__version__ = '3.22.18.8'
4242
__maintainer__ = 'Ilya Razmanov'
4343
__email__ = 'ilyarazmanov@gmail.com'
4444
__status__ = 'Production'
@@ -81,7 +81,7 @@ def ShowInfo(event=None) -> None:
8181
showinfo(
8282
title='Image information',
8383
message=f'File properties:\nLocation: {sourcefilename}\nSize: {file_size_str}\nLast modified: {ctime(Path(sourcefilename).stat().st_mtime)}',
84-
detail=f'Image properties, as represented internally:\nWidth: {X} px\nHeight: {Y} px\nChannels: {Z} channel{"s" if Z > 1 else ""}\nColor depth: {maxcolors + 1} gradations/channel',
84+
detail=f'Image properties, as represented internally:\nStatus: {is_filtered=}, {is_saved=}\nWidth: {X} px\nHeight: {Y} px\nChannels: {Z} channel{"s" if Z > 1 else ""}\nColor depth: {maxcolors + 1} gradations/channel',
8585
)
8686

8787

@@ -142,7 +142,8 @@ def GetSource(event=None) -> None:
142142

143143
zoom_factor = 0
144144
view_src = True
145-
is_filtered = is_saved = False
145+
is_filtered = False
146+
is_saved = True
146147

147148
sourcefilename = askopenfilename(title='Open image file', filetypes=[('Supported formats', '.png .ppm .pgm .pbm .pnm'), ('Portable network graphics', '.png'), ('Portable any map', '.ppm .pgm .pbm .pnm')])
148149
if sourcefilename == '':
@@ -158,11 +159,11 @@ def GetSource(event=None) -> None:
158159

159160
if Path(sourcefilename).suffix == '.png':
160161
# ↓ Reading PNG image as list
161-
X, Y, Z, maxcolors, image3D, info = png2list(sourcefilename)
162+
X, Y, Z, maxcolors, source_image3D, info = png2list(sourcefilename)
162163

163164
elif Path(sourcefilename).suffix in ('.ppm', '.pgm', '.pbm', '.pnm'):
164165
# ↓ Reading PNM image as list
165-
X, Y, Z, maxcolors, image3D = pnm2list(sourcefilename)
166+
X, Y, Z, maxcolors, source_image3D = pnm2list(sourcefilename)
166167
# ↓ Creating dummy info required to correctly Save As PNG later.
167168
# Fixing color mode, the rest is fixed with pnglpng v. 25.01.07.
168169
info = {'bitdepth': 16} if maxcolors > 255 else {'bitdepth': 8}
@@ -174,7 +175,7 @@ def GetSource(event=None) -> None:
174175
│ Creating deep copy of source 3D list │
175176
│ to avoid accumulating repetitive filtering │
176177
└────────────────────────────────────────────┘ """
177-
source_image3D = deepcopy(image3D)
178+
image3D = deepcopy(source_image3D)
178179

179180
""" ┌───────────────┐
180181
│ Viewing image │
@@ -200,8 +201,13 @@ def GetSource(event=None) -> None:
200201
# ↓ binding on preview click
201202
zanyato.bind('<Control-Button-1>', zoomIn) # Ctrl + left click
202203
zanyato.bind('<Double-Control-Button-1>', zoomIn) # Ctrl + left click too fast
204+
zanyato.bind('<Control-+>', zoomIn)
205+
zanyato.bind('<Control-=>', zoomIn)
203206
zanyato.bind('<Alt-Button-1>', zoomOut) # Alt + left click
204207
zanyato.bind('<Double-Alt-Button-1>', zoomOut) # Alt + left click too fast
208+
zanyato.bind('<Control-minus>', zoomOut)
209+
zanyato.bind('<Control-Key-1>', zoomOne)
210+
zanyato.bind('<Control-Alt-Key-0>', zoomOne)
205211
sortir.bind_all('<MouseWheel>', zoomWheel) # Wheel scroll
206212
sortir.bind_all('<Control-i>', ShowInfo)
207213
menu02.entryconfig('Image Info...', state='normal')
@@ -231,7 +237,7 @@ def GetSource(event=None) -> None:
231237
butt_filter.bind('<Leave>', lambda event=None: butt_filter.config(foreground=butt['foreground'], background=butt['background']))
232238
UINormal()
233239
sortir.geometry(f'+{(sortir.winfo_screenwidth() - sortir.winfo_width()) // 2}+{(sortir.winfo_screenheight() - sortir.winfo_height()) // 2 - 32}')
234-
butt_filter.focus_set() # moving focus to "Filter"
240+
zanyato.focus_set()
235241

236242

237243
def RunFilter(event=None) -> None:
@@ -317,6 +323,22 @@ def zoomOut(event=None) -> None:
317323
butt_minus.config(state='normal', cursor='hand2')
318324

319325

326+
def zoomOne(event=None) -> None:
327+
"""Zoom 1:1."""
328+
329+
global zoom_factor, view_src, preview
330+
zoom_factor = 0
331+
332+
if view_src:
333+
ShowPreview(preview_src, 'Source')
334+
else:
335+
ShowPreview(preview_filtered, 'Result')
336+
337+
# ↓ reenabling +/- buttons
338+
butt_plus.config(state='normal', cursor='hand2')
339+
butt_minus.config(state='normal', cursor='hand2')
340+
341+
320342
def zoomWheel(event) -> None:
321343
"""zoomIn or zoomOut by mouse wheel."""
322344

@@ -535,15 +557,15 @@ def SaveAs(event=None) -> None:
535557
info01.grid(row=0, column=2)
536558

537559
ini_threshold_x = IntVar(value=16)
538-
spin01 = Spinbox(frame_top, from_=0, to=256, increment=1, textvariable=ini_threshold_x, state='disabled', width=3, font=('helvetica', 11))
560+
spin01 = Spinbox(frame_top, from_=0, to=255, increment=1, textvariable=ini_threshold_x, state='disabled', width=3, font=('helvetica', 11))
539561
spin01.grid(row=0, column=3)
540562

541563
# ↓ Y-pass threshold control
542564
info02 = Label(frame_top, text='Y:', font=('helvetica', 11), state='disabled')
543565
info02.grid(row=0, column=4)
544566

545567
ini_threshold_y = IntVar(value=8)
546-
spin02 = Spinbox(frame_top, from_=0, to=256, increment=1, textvariable=ini_threshold_y, state='disabled', width=3, font=('helvetica', 11))
568+
spin02 = Spinbox(frame_top, from_=0, to=255, increment=1, textvariable=ini_threshold_y, state='disabled', width=3, font=('helvetica', 11))
547569
spin02.grid(row=0, column=5)
548570

549571
# ↓ Wrap around control

POVRayThread.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
__copyright__ = '(c) 2024-2025 Ilya Razmanov'
3838
__credits__ = 'Ilya Razmanov'
3939
__license__ = 'unlicense'
40-
__version__ = '1.22.08.20'
40+
__version__ = '1.22.18.8'
4141
__maintainer__ = 'Ilya Razmanov'
4242
__email__ = 'ilyarazmanov@gmail.com'
4343
__status__ = 'Production'
@@ -193,9 +193,14 @@ def GetSource(event=None) -> None:
193193
# ↓ binding on preview click
194194
zanyato.bind('<Control-Button-1>', zoomIn) # Ctrl + left click
195195
zanyato.bind('<Double-Control-Button-1>', zoomIn) # Ctrl + left click too fast
196+
zanyato.bind('<Control-+>', zoomIn)
197+
zanyato.bind('<Control-=>', zoomIn)
196198
zanyato.bind('<Alt-Button-1>', zoomOut) # Alt + left click
197199
zanyato.bind('<Double-Alt-Button-1>', zoomOut) # Alt + left click too fast
198-
sortir.bind_all('<MouseWheel>', zoomWheel) # Wheel
200+
zanyato.bind('<Control-minus>', zoomOut)
201+
zanyato.bind('<Control-Key-1>', zoomOne)
202+
zanyato.bind('<Control-Alt-Key-0>', zoomOne)
203+
sortir.bind_all('<MouseWheel>', zoomWheel) # Wheel scroll
199204
sortir.bind_all('<Control-i>', ShowInfo)
200205
menu02.entryconfig('Image Info...', state='normal')
201206
# ↓ binding global
@@ -217,7 +222,7 @@ def GetSource(event=None) -> None:
217222
butt_filter.bind('<Leave>', lambda event=None: butt_filter.config(foreground=butt['foreground'], background=butt['background']))
218223
UINormal()
219224
sortir.geometry(f'+{(sortir.winfo_screenwidth() - sortir.winfo_width()) // 2}+{(sortir.winfo_screenheight() - sortir.winfo_height()) // 2 - 32}')
220-
butt_filter.focus_set() # moving focus to "Filter"
225+
zanyato.focus_set()
221226

222227

223228
def RunFilter(event=None) -> None:
@@ -293,6 +298,22 @@ def zoomOut(event=None) -> None:
293298
butt_minus.config(state='normal', cursor='hand2')
294299

295300

301+
def zoomOne(event=None) -> None:
302+
"""Zoom 1:1."""
303+
304+
global zoom_factor, view_src, preview
305+
zoom_factor = 0
306+
307+
if view_src:
308+
ShowPreview(preview_src, 'Source')
309+
else:
310+
ShowPreview(preview_filtered, 'Result')
311+
312+
# ↓ reenabling +/- buttons
313+
butt_plus.config(state='normal', cursor='hand2')
314+
butt_minus.config(state='normal', cursor='hand2')
315+
316+
296317
def zoomWheel(event) -> None:
297318
"""zoomIn or zoomOut by mouse wheel."""
298319

@@ -452,15 +473,15 @@ def SaveAsStitch() -> None:
452473
info01.pack(side='left', padx=0, pady=0, fill='x')
453474

454475
ini_threshold_x = IntVar(value=16)
455-
spin01 = Spinbox(frame_top, from_=0, to=256, increment=1, textvariable=ini_threshold_x, state='disabled', width=3, font=('helvetica', 11))
476+
spin01 = Spinbox(frame_top, from_=0, to=255, increment=1, textvariable=ini_threshold_x, state='disabled', width=3, font=('helvetica', 11))
456477
spin01.pack(side='left', padx=(0, 4), pady=0, fill='x')
457478

458479
# ↓ Y-pass threshold control
459480
info02 = Label(frame_top, text='Y:', font=('helvetica', 10), state='disabled')
460481
info02.pack(side='left', padx=0, pady=0, fill='both')
461482

462483
ini_threshold_y = IntVar(value=8)
463-
spin02 = Spinbox(frame_top, from_=0, to=256, increment=1, textvariable=ini_threshold_y, state='disabled', width=3, font=('helvetica', 11))
484+
spin02 = Spinbox(frame_top, from_=0, to=255, increment=1, textvariable=ini_threshold_y, state='disabled', width=3, font=('helvetica', 11))
464485
spin02.pack(side='left', padx=(0, 4), pady=0, fill='x')
465486

466487
# ↓ Filter start

filter/avgrow.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def _criterion_x(channel: int, channel_sum: int) -> bool:
112112
for y in range(0, Y, 1):
113113
x_start = 0 # Defining start of inner loop of averaging until threshold.
114114
number = 1 # Number of pixels being read during averaging loop.
115-
pixel = source_image[0][0] # Current pixel.
115+
pixel = source_image[cy(y)][0] # Current pixel.
116116
pixels_sum = pixel # Sum of pixels being read during averaging loop.
117117
for x in range(0, X + x_overhead, 1):
118118
number += 1
@@ -139,7 +139,7 @@ def _criterion_y(channel: int, channel_sum: int) -> bool:
139139
for x in range(0, X, 1):
140140
y_start = 0
141141
number = 1
142-
pixel = intermediate_image[0][0]
142+
pixel = intermediate_image[0][cx(x)]
143143
pixels_sum = pixel
144144
for y in range(0, Y + y_overhead, 1):
145145
number += 1

0 commit comments

Comments
 (0)