how to create a modeless dialog in tcl/tk - multithreading

I'm trying to make a modeless dialog, since whenever a dialog appears stops the real time process that is running in the main gui, after some reasearch i realize that the problem that is causing the real time part to stop is the "vwait" command in the dialog procedure. How could I make the my current dialog modeless so it doesn't affect the real time process in the back? Should I put the msgDialog in a different thread everytime i call the proc ? or what other way can I do it?
TCL CODE:
proc MsgDialog {w message type icon} \
{
if {![winfo exists $w]} {
set dialColor white
image create photo .alert -format PNG -file alertIcon.png -width 40
image create photo .question -format PNG -file questionicon.png
toplevel $w -borderwidth 2 -relief raised -background $dialColor
wm overrideredirect $w 1
set x [expr { ( [winfo vrootwidth $w] - 350 ) / 2 }]
set y [expr { ( [winfo vrootheight $w] - 190 ) / 2 }]
wm geometry $w 350x190+${x}+${y}
frame $w.msgPnl -relief flat -borderwidth 1 -background $dialColor -width 280 -height 140
place $w.msgPnl -x 0 -y 0
frame $w.imgPnl -relief flat -borderwidth 1 -background $dialColor -width 50 -height 140
place $w.imgPnl -x 285 -y 0
frame $w.btnPnl -relief flat -borderwidth 1 -background $dialColor -width 300 -height 50
place $w.btnPnl -x 0 -y 130
label $w.msgPnl.message -text $message -background $dialColor -justify center -wraplength 270 -font dialogFont
pack $w.msgPnl.message -anchor center -pady 20 -padx 10 -expand 1 -fill both
if {$type == "ok"} {
button $w.btnPnl.okbut -text "OK" -background black -foreground white -relief flat -command {set _res "ok"} -width 8 -height 2 -highlightthickness 2 -font boldFont
grid $w.btnPnl.okbut -row 1 -column 1 -padx 125
} elseif {$type == "yesno"} {
button $w.btnPnl.yes -text "Yes" -background black -foreground white -relief flat -command {set _res "yes"} -width 8 -height 2 -highlightthickness 2 -font boldFont
button $w.btnPnl.no -text "No" -background black -foreground white -relief flat -command {set _res "no"} -width 8 -height 2 -highlightthickness 2 -font boldFont
grid $w.btnPnl.yes -row 1 -column 1 -padx 50
grid $w.btnPnl.no -row 1 -column 2
} else {
button $w.btnPnl.okbut -text "OK" -background $btnColor -relief flat -command {set _res "ok"} -width 8 -height 2
pack $w.btnPnl.okbut -side top -anchor center
}
if {$icon == "alert"} {
label $w.imgPnl.alertI -image .alert -compound top -background $dialColor
pack $w.imgPnl.alertI -fill both -expand 1 -pady 20
} elseif {$icon == "question"} {
label $w.imgPnl.quest -image .question -compound top -background $dialColor
pack $w.imgPnl.quest -fill both -expand 1 -pady 20
} else {
label $w.imgPnl.alertI -image .alert -compound top -background $dialColor
pack $w.imgPnl.alertI -fill both -expand 1 -pady 20
}
raise $w
vwait _res
destroy $w
return $::_res
}
}
I was trying something like this, but when i get invalid command name MsgDialog
set tid [thread::create {thread::wait}]
::thread::send -async $tid {MsgDialog .dialog "Are you ready for measurement ?" yesno question} answer
vwait answer
if {$answer == yes} {
#do something
}

Your dialog proc is fundamentally modal, since it returns a value. It therefore blocks until the user responds, because it can't return its value until the user gives it one.
To make it modeless, build it to just create itself and return. The buttons all then need to call procs (either global or with some fully qualified name) that will set the user value in some place you're waiting for it, then destroy the dialog properly.
This means that your "return" value must be global, the window ID variable must be global, the handler proc/procs must be global, and you'll need to trigger whatever processing you want the value for in some way that's too application specific for me to guess. It's a fair bit of work, but it's easier than trying to incorporate the threading library.

Have you tried simply removing the vwait? The difference between a modal dialog and a non-modal one is really nothing more than the modal dialog calling vwait and then doing a grab on the keyboard and mouse.

Related

How supress "jpegtopnm: WRITING PPM FILE" etc within output of jpegtopnm[Solved]

I want to see the sizes of images within a directory. For this purpose I do
$ for file in *.jpg; do jpegtopnm $file | pnmfile; done
Then I can see
jpegtopnm: WRITING PPM FILE
stdin: PPM raw, 960 by 1280 maxval 255
jpegtopnm: WRITING PPM FILE
stdin: PPM raw, 960 by 1280 maxval 255
jpegtopnm: WRITING PPM FILE
stdin: PPM raw, 1200 by 1600 maxval 255
and so on.
I would like to see
960 by 1280
960 by 1280
1200 by 1600
.............
How one can do this?
Answer
The command jpegtopnm is a part of netpbm - package of graphics manipulation programs and libraries:
$ apt-file -l find pnmfile
netbpm
Then we must read "man netbpm":
-quiet Suppress all informational messages
Thus we solved our problem:
$ for file in *.jpg; do jpegtopnm $file -quiet | pnmfile | cut -c 16-28; done
4000 by 3000
2592 by 1944
4000 by 3000
............
About "cut -c 16-28".
This is a filter that selects characters from 16 to 28 in a string
"stdin: PPM raw, 960 by 1280 maxval 255".
If you have at your directory images with different sizes such as 4000x5000, 300x400, 2x3, 40x67 etc it won't work properly. For that reason you have to use more complicated way. It is a "cut" filter by fields(-f). The field separator will be a space character(-d ' ').
$ for file in *.jpg; do jpegtopnm $file -quiet | pnmfile | cut -d ' ' -f 3-5; done
700 by 900
65 by 40
2 by 3
7000 by 9000
4000 by 3000
............

How to convert raw YUV image to jpg

I have a raw image that was taken with v4l2-ctl after the camera had been setup like:
# media-ctl -d /dev/media0 -l "'rzg2l_csi2 10830400.csi2':1 -> 'CRU output':0 [1]"
# media-ctl -d /dev/media0 -V "'rzg2l_csi2 10830400.csi2':1 [fmt:UYVY8_2X8/1280x960 field:none]"
# media-ctl -d /dev/media0 -V "'ov5645 0-003c':0 [fmt:UYVY8_2X8/1280x960 field:none]"
and then the picture got snapped with:
# v4l2-ctl --device /dev/video0 --stream-mmap --stream-to=frame.raw --stream-count=1
now I've tried multiple methods to convert this into a jpeg but nothing seems to yield the expected output
the raw file can be downloaded here: https://drive.google.com/file/d/1VqXnrJDYbzdtSsWfTlm2mX9rl1-Rl_7F/view?usp=sharing
I tried out the following command:
convert -verbose -size 1280x960 UYVY:frame.raw frame.bmp
which I found on Converting from YUV(UYVY) to RGB using imagemagick
but it doesn't do the trick
Your frame is 2457600 bytes and your pixel dimensions are 1280x960, so you have:
bits per pixel = 2457600 * 8 / (1280 * 960) = 16
You can get a list of the pixel formats that ffmpeg supports using:
ffmpeg -pix_fmts 2> /dev/null
Sample Output
FLAGS NAME NB_COMPONENTS BITS_PER_PIXEL
-----
IO... yuv420p 3 12
IO... yuyv422 3 16
IO... rgb24 3 24
IO... bgr24 3 24
IO... yuv422p 3 16
IO... yuv444p 3 24
IO... yuv410p 3 9
...
...
That means you can get a list of pixel formats that contain Y, U and V with 16 bits per pixel like this:
ffmpeg -pix_fmts 2> /dev/null | awk '/y/ && /u/ && /16$/ {print}'
IO... yuyv422 3 16
IO... yuv422p 3 16
IO... yuvj422p 3 16
IO... uyvy422 3 16
IO... yuv440p 3 16
IO... yuvj440p 3 16
IO... yvyu422 3 16
Now you can run a loop, iterating over all the 16-bit per pixel YUV formats and see what ffmpeg makes of your image - naming each result after the format so you can identify which is which:
ffmpeg -pix_fmts 2> /dev/null |
awk '/y/ && /u/ && /16$/ {print $2}' |
while read f; do
ffmpeg -y -s:v 1280x960 -pix_fmt $f -i frame.raw $f.jpg
done
That gives you these files:
-rw-r--r-- 1 mark staff 304916 3 Feb 09:38 yuv440p.jpg
-rw-r--r-- 1 mark staff 227123 3 Feb 09:38 yuvj422p.jpg
-rw-r--r-- 1 mark staff 39543 3 Feb 09:38 yuyv422.jpg
-rw-r--r-- 1 mark staff 39545 3 Feb 09:38 yvyu422.jpg
And I guess that yuyv422.jpg is your image, so that means you can extract it with:
ffmpeg -y -s:v 1280x960 -pix_fmt yuyv422 -i frame.raw result.jpg
If you wanted to do that with ImageMagick, you could do something like this:
#!/bin/bash
python3 <<EOF
import numpy as np
h, w = 960, 1280
# Load raw file into Numpy array
raw = np.fromfile('frame.raw', np.uint8)
raw[0::2].tofile('Y') # Starting at the 1st byte, write every 2nd byte to file "Y"
raw[1::4].tofile('U') # Starting at the 2nd byte, write every 4th byte to file "U"
raw[3::4].tofile('V') # Starting at the 3rd byte, write every 4th byte to file "V"
EOF
# Load the Y channel, then the U and V channels forcibly resizing them, then combine and go to sRGB
magick -depth 8 -size 1280x960 gray:Y \
\( -size 640x960 gray:U gray:V -resize 1280x960\! \) \
-set colorspace YUV -combine -colorspace sRGB result.jpg
If yo don't like/have Python, that part can be replaced with some basic C as follows:
#include <stdint.h>
#include <stdio.h>
// Split YUYV file called "frame.raw" into separate channels with filenames "Y", "U" and "V"
// Compile with: clang -O3 splitter.c -o splitter
int main(){
FILE *in, *Y, *U, *V;
uint8_t buffer[4];
size_t bytesRead;
// Open input file and 1 output file per channel
in = fopen("frame.raw", "rb");
Y = fopen("Y", "wb");
U = fopen("U", "wb");
V = fopen("V", "wb");
// read up to sizeof(buffer) bytes
while ((bytesRead = fread(buffer, 1, sizeof(buffer), in)) > 0)
{
fputc(buffer[0], Y);
fputc(buffer[1], U);
fputc(buffer[2], Y);
fputc(buffer[3], V);
}
}
Having had so much fun doing ffmpeg, Python, and C versions, I thought I'd try just doing it in the shell - converting bytes to lines and so I could pick alternate lines instead of alternate bytes. This works the same as the above:
#!/bin/bash
# Build JPEG image from YUYV image with packed bytes in order YUYVYUYV...
# Use "xxd" to convert bytes into lines, then extract alternate lines - which is easier than extracting bytes
H=960
W=1280
INPUT="frame.raw"
# Take top byte of every uint16 and put into "Y.pgm"
xxd -c1 -p "$INPUT" | sed -n 'p;n' | xxd -r -p | magick -size ${W}x${H} -depth 8 gray:- Y.pgm
# Take bottom byte of every 2nd uint16, starting at the 1st, resize up to full width and put into "U.pgm"
xxd -c1 -p "$INPUT" | sed -n 'n;p' | sed -n 'p;n' | xxd -r -p | magick -size $((W/2))x${H} -depth 8 gray:- -resize ${W}x${H}\! U.pgm
# Take bottom byte of every 2nd uint16, starting at the 2nd, resize up to full width and put into "V.pgm"
xxd -c1 -p "$INPUT" | sed -n 'n;p' | sed -n 'n;p' | xxd -r -p | magick -size $((W/2))x${H} -depth 8 gray:- -resize ${W}x${H}\! V.pgm
# Load the 3 channels, combine and convert to JPEG
magick {Y,U,V}.pgm -set colorspace YUV -combine -colorspace sRGB result.jpg
# Remove litter
rm {Y,U,V}.pgm
As regards colour cast removal, as I said in the comments, the " normal" way, AFAIK, is to get the average colour of the image and invert its Hue then blend that "negated cast" back with the original image to offset the original colour cast. Here is a crude attempt - if anyone knows better please ping me!
Step 1: Get average colour cast
magick result.jpg -resize 1x1\! cast.png
Step 2: Invert the cast
magick cast.png -modulate 100,100,0 correction.png
Step 3: Blend the original with the correction and brighten maybe
magick result.jpg correction.png -define compose:args=50,50 -compose blend -composite -auto-level result.jpg
Here are the original and corrected versions:
Obviously you can change the percentages for different degrees of "correction".

Script is not working. What am I doing wrong?

First off I found this guide, which details exactly what I need.
https://imagemagick.org/script/connected-components.php
For the life of me I cannot get this to work. Anyone have any idea?
I've tried a bunch of variations of the scripts listed in the guide.
Also when I run convert /var/www/mailtovoice/audrey/sean_look_grey.png -define connected-components:verbose=true -connected-components 8 /var/www/mailtovoice/audrey/sean_look4.png
I get 1000s of objects. When I converted it to an image with just 3 objects I get 100s.
Mark has the right idea, but the solution is much simpler than he posted, since ImageMagick -connected-components can do the filtering and output directly.
input:
Unix line endings (for windows use ^ rather than \ )
convert image.png \
-define connected-components:area-threshold=100 \
-define connected-components:mean-color=true \
-connected-components 4 \
result.png
The method suggested by Fred (#fmw42) is far simpler and preferable to that shown in this answer, so all but die-hard enthusiasts should use Fred's answer. Rather than delete mine, I will leave it showing as it could form the basis for other more demanding/involved processing.
This is a rather funny way to do it... find all the blobs. i.e. connected components:
convert spotty.png -define connected-components:verbose=true -connected-components 4 null:
which gives you something like this but with 2,000+ lines:
Objects (id: bounding-box centroid area mean-color):
0: 860x482+0+0 431.5,239.7 405738 gray(0)
800: 43x263+252+219 265.9,350.5 2458 gray(255)
2: 21x226+276+0 288.9,111.2 1540 gray(255)
2216: 5x16+107+445 109.3,452.9 65 gray(255)
910: 7x15+276+228 279.0,234.5 63 gray(255)
491: 7x14+651+150 654.1,156.6 54 gray(255)
1207: 7x9+735+282 737.9,285.8 53 gray(255)
2313: 6x9+147+457 149.6,460.9 48 gray(255)
985: 8x9+754+238 757.3,242.0 48 gray(255)
...
...
Now look for all the ones with a size (second-to-last field) less than 1000 using awk and print the region:
convert spotty.png \
-define connected-components:verbose=true \
-connected-components 4 null: |
awk -v thresh=1000 'NR>1 && $(NF-1)<thresh{print " -region " $2 " -colorize 100%"}'
Output
-region 5x16+107+445 -colorize 100%
-region 7x15+276+228 -colorize 100%
-region 7x14+651+150 -colorize 100%
-region 7x9+735+282 -colorize 100%
...
...
Now reload the original image, set the fill colour for colorised regions to red and regenerate the list of regions to be filled exactly as above:
convert spotty.png -fill red $(convert spotty.png -define connected-components:verbose=true -connected-components 4 null: | awk -v thresh=1000 'NR>1 && $(NF-1)<thresh{print " -region " $2 " -colorize 100%"}' ) result.png
The command generated boils down to:
convert spotty.png -threshold 50% -fill red \
-region 56x16+107+445 -colorize 100% \
-region 70x15+276+228 -colorize 100% \
-region ... -colorize 100% \
...
...
result.png

How to -draw text and -shadow it in ImageMagick?

I'm printing some text on an image with convert and I would like to decorate the text with a black shadow, I tried -blur or -gaussian but I cannot apply to the text, it is applied to the background image only.
I need to use -draw command and not -annotate.
And this is the code I need to update for shadowing
-font "geometricslab703bt-bold-webfont.ttf" -fill White -pointsize 18 -draw "rotate -4 text 350,250 '---- mijn ideale ----'"
thanks in advance
A better, more flexible, way to work with text shadow is to render the shadow on a new layer. This method will allow you to manipulate the shadow-text, as needed, without affecting the background. Finally draw the actual text on top of the shadow after adjusting any geometric offsetting. Here's an example:
convert -size 280x100 pattern:SMALLFISHSCALES \
\( xc:transparent -font "Menlo" -pointsize 32 -fill black -draw "rotate -4 text 20,60 'ImageMagick'" -blur 0x1 \) \
-geometry +2+2 -composite \
-font "Menlo" -fill white -pointsize 32 -draw "rotate -4 text 20,60 'ImageMagick'" \
example.png
The escaped braces "\( \)" will create a new sub-image; which, will be applied to the background with -composite flag.
This solution is a little bit more labor intensive, but keeps all your effects isolated.
You can use caption to draw text, and make a shadow layer with clone then merge the two layer.
convert logo: -resize 40%x40 \
\( -size "80x40" -background none -gravity west -fill green caption:"Caption text" \
\( +clone -background navy -shadow 80x3+5+5 \) +swap -background none -layers merge +repage \) -composite \
\( -size "80x40" -background none -gravity east -fill green caption:"Caption text" \
\( +clone -background red -shadow 80x3+5+5 \) +swap -background none -layers merge +repage \) -composite \
out.png
#emcconville's answer have helped me with this transparency implementation:
if [ ! -f ../watermark_template.png ]; then convert -size 511x81 xc:transparent ../watermark_template.png; fi
convert ../watermark_template.png -size 511x81 \
\( xc:transparent -font "Arial" -pointsize 20 -fill black -draw "text 40,35 '$osname $osversion Insider Preview'" -blur 0x1 \) \
-geometry +2+2 -composite \
-font "Arial" -fill white -pointsize 20 -draw "text 40,35 '$osname $osversion Insider Preview'" \
../watermark.png
#- from https://stackoverflow.com/a/20861391/5623661
convert ../watermark.png -size 511x81 \
\( xc:transparent -font "Arial" -pointsize 20 -fill black -draw "text 40,60 'Evaluation compilation. Build $osbuild $osbuildcodename'" -blur 0x1 \) \
-geometry +2+2 -composite \
-font "Arial" -fill white -pointsize 20 -draw "text 40,60 'Evaluation compilation. Build $osbuild $osbuildcodename'" \
../watermark.png
convert ../watermark.png -size 511x81 \
\( xc:transparent -font "Arial" -pointsize 16 -fill black -draw "text 180,76 'Build attempt $specialbuildattempt'" -blur 0x1 \) \
-geometry +2+2 -composite \
-font "Arial" -fill white -pointsize 16 -draw "text 180,76 'Build attempt $specialbuildattempt'" \
../watermark.png
Which (locally) results in:
PS: this will vary depending on how variables are declared.
You can put your own custom variables so the image generation become dynamic and can be used programmatically.
Also, this implementation doesn't require, necessarilly, having an existing image to work from.

Wait for a GUI application to fully startup before running script

If I have something like this in a bash script:
audacity &
pid=$!
wmctrl -r "Audacity" -e 0,0,0,800,600
... it will usually fail, because the process startup (audacity &) will finish/return much earlier than the point where the Audacity window is fully shown (and can be controlled by wmctrl), which otherwise may take a couple of seconds.
Is there an easy way to "sync" or "wait" for a GUI application to be fully started up (that is, its window being fully rendered), before proceeding with a script? (there is a way I've found, which I'm posting as an answer - but was wandering if there is an easier, more compact way)
EDIT: this does detect when the window is shown; but does not detect when all the menus/widgets inside it are finished with placement/layout
Ok, so first I run this script:
audacity &
pid=$!
while [ "1" ] ; do
xwininfo -name 'Audacity'
sleep 0.1
done
... which should be run like this, to obtain a full log:
bash testscript.sh 2>&1 | tee testscript.log
... and can see a point, where the dump from xwininfo "transitions", so to speak:
xwininfo: Window id: 0x3a000b5 (has no name)
Absolute upper-left X: 0
Absolute upper-left Y: 0
Relative upper-left X: 0
Relative upper-left Y: 0
Width: 200
Height: 200
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x20 (installed)
Bit Gravity State: NorthWestGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsUnMapped
Override Redirect State: no
Corners: +0+0 -824+0 -824-400 +0-400
-geometry 200x200+0+0
xwininfo: Window id: 0x4c00587 "Audacity"
Absolute upper-left X: 50
Absolute upper-left Y: 59
Relative upper-left X: 0
Relative upper-left Y: 18
Width: 830
Height: 540
Depth: 24
Visual: 0x21
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x20 (installed)
Bit Gravity State: NorthWestGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable
Override Redirect State: no
Corners: +50+59 -144+59 -144-1 +50-1
-geometry 830x540+50-1
So, I could basically grep for xwininfo output not containing "has no name", or containing "Map State: IsViewable" ...
So, I finally try this - and it seems to work:
audacity &
pid=$!
WINREP=""
while [[ ! "`echo $WINREP | grep -l 'Map State: IsViewable'`" ]] ; do
WINREP=$(xwininfo -name 'Audacity')
#echo $WINREP
sleep 0.1
done
echo Exited
# must use -F here for case-insensitive, to ensure proper window targetting
wmctrl -v -F -r "Audacity" -e 0,0,0,800,600

Resources