I want to define a shortcut using autohotkey that automates navigation to the Cell Width text box in Word's ribbon menu (i.e. sends the keys Alt,j,l,w while in Word).
My initial attempt at a script introduces an issue where the Up actions of the physical key presses haven't completed before the keys are sent by the script, causing it to fail.
Although I have a workaround (using sleep 250, shown further below), I would like to understand whether there is a more elegant solution - e.g. this value was chosen by trial and error, and this workaround may fail on a different machine, or if my laptop is having a bad day.
Essentially, I would like to find a solution that avoids hardcoding a wait duration.
Any suggestions?
The issue
In my initial script I tried to define the hotkey Alt+Shift+p in Word, to send Alt,j,l,w in sequence. This fails and enters the text "jlw" into the active table cell instead.
This is because for keys physically pressed (marked with # in comments below), the Up actions are yet to run when the script has already begun sending keys (indicated with $). This disrupts the send sequence Alt,j,l,w so it fails.
Also note events marked with ? may be a byproduct of the failed actions.
Script
; Jump to table cell width entry when Alt+Shift+p pressed in Word only
#IfWinActive, ahk_exe WINWORD.EXE
!+p::
Send, {RAlt down}{RAlt up}jlw
Return
Key history
VK SC Type Up/Dn Elapsed Key Comment
-------------------------------------------------------------------------------------------------------------
A4 038 d 1.03 LAlt #
A0 02A d 0.14 LShift #
50 019 h d 0.13 p #, h=Hook Hotkey
A5 138 i d 0.02 RAlt $
A5 138 i u 0.00 RAlt $
11 01D i d 0.00 Control ?
11 01D i u 0.00 Control ?
A4 038 i u 0.00 LAlt #
A0 02A i u 0.00 LShift #
4A 024 i d 0.00 j $
4A 024 i u 0.00 j $
4C 026 i d 0.00 l $
4C 026 i u 0.00 l $
57 011 i d 0.00 w $
57 011 i u 0.00 w $
11 01D i d 0.00 Control ?
A4 038 i d 0.00 LAlt ?
11 01D i u 0.00 Control ?
A0 02A i d 0.00 LShift ?
50 019 s u 0.08 p #
A0 02A u 0.25 LShift ?
A4 038 u 0.00 LAlt ?
Workaround
By adding a sleep 250 statement, the hotkey and sent keys run in the intended sequence. Note no other keys are triggered (nothing indicated with ?).
Script
; Jump to table cell width entry when Alt+Shift+p pressed in Word only
#IfWinActive, ahk_exe WINWORD.EXE
!+p::
Sleep 250
Send, {RAlt down}{RAlt up}jlw
Return
Key history
VK SC Type Up/Dn Elapsed Key Comment
-------------------------------------------------------------------------------------------------------------
A4 038 d 0.39 LAlt #
A0 02A d 0.20 LShift #
50 019 h d 0.20 p #, h=Hook Hotkey
50 019 s u 0.11 p #
A0 02A u 0.11 LShift #
A4 038 u 0.00 LAlt #
A5 138 i d 0.03 RAlt $
A5 138 i u 0.00 RAlt $
4A 024 i d 0.00 j $
4A 024 i u 0.00 j $
4C 026 i d 0.00 l $
4C 026 i u 0.00 l $
57 011 i d 0.00 w $
57 011 i u 0.00 w $, SUCCESS!
If the target window does not receive the keystrokes reliably, you can try increasing the press duration in the send command via the second parameter of SetKeyDelay, eg.
!+p::
SetKeyDelay, 10, 10
Send, {RAlt down}{RAlt up}jlw
Return
Not sure why you're using right alt, don't think that's supposed to work even. Just use "Alt". And I think your hotkey is not very good for this. Word gets confused with the input.
But I think maybe doing this, could work
!+p up:: ;up means it'll run once u release p, might help a bit
SetKeyDelay, 500, 10 ;good amount of delay, can probably do with less
Send, {Shift Up}{Alt Up}{Alt}jlw ;send shift and alt up first
Return
And now for a better alternative option, automating it with the Word ComObject. Usage of VBA for this is the normal and intended way, but you can AHK (or any other language) just as well.
Here's a quick AHK example of changing the first table's first cell's width:
Word := ComObjActive("Word.Application")
Word.ActiveDocument.Tables(1).Cell(1, 1).Width := 500
Of course that's not a very good way to go about this, I'm sure you can reference the active table and active cell somehow, I just quickly read the docs and wrote that. Haven't actually ever used any of this stuff in practice myself.
I found a more elegant solution using KeyWait. Compared to my earlier workaround script, I consider this more elegant since it doesn't require hardcoding a wait duration. This simply waits for the pressed keys to come up before the script continues, preventing any conflict:
Script
#IfWinActive, ahk_exe WINWORD.EXE
!+p::
KeyWait Alt
KeyWait Shift
KeyWait p
Send, {Alt Down}{Alt Up}jlw
Return
Key history
VK SC Type Up/Dn Elapsed Key Comment using previous notation
-------------------------------------------------------------------------------------------------------------
A4 038 d 0.63 LAlt #
A0 02A d 0.11 LShift #
50 019 h d 0.11 p #
50 019 s u 0.06 p #
A0 02A u 0.19 LShift #
A4 038 u 0.00 LAlt #
12 038 i d 0.01 Alt $
12 038 i u 0.00 Alt $
4A 024 i d 0.00 j $
4A 024 i u 0.00 j $
4C 026 i d 0.00 l $
4C 026 i u 0.00 l $
57 011 i d 0.00 w $
57 011 i u 0.00 w $
Interestingly, the above script modified to use Send, !jlw works (i.e. navigates to the Cell Width box) but the sent keys come Up out of sequence as follows. I won't use this, just to avoid any unintended consequences of this occurring out of sequence.
VK SC Type Up/Dn Elapsed Key Comment
-------------------------------------------------------------------------------------------------------------
A4 038 d 1.17 LAlt
A0 02A d 0.11 LShift
50 019 h d 0.14 p
50 019 s u 0.09 p
A0 02A u 0.16 LShift
A4 038 u 0.00 LAlt
A4 038 i d 0.02 LAlt <--
4A 024 i d 0.00 j
4A 024 i u 0.00 j
A4 038 i u 0.00 LAlt <--
4C 026 i d 0.00 l
4C 026 i u 0.00 l
57 011 i d 0.00 w
57 011 i u 0.00 w
Related
I was writing a Powershell script using a pipeline with a Process block and it started doing something unexpected: listing all the running processes and then dumping the script contents. I kept minimizing the script to try to figure out what was going on and ended up with this:
[CmdletBinding()]
Param()
$varname = "huh"
Process
{
# nothing here
}
So it looks like this:
PS /Volumes/folder> cat ./test.ps1
[CmdletBinding()]
Param()
$varname = "huh"
Process
{
# nothing here
}
PS /Volumes/folder> pwsh ./test.ps1
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
0 0.00 0.00 0.00 0 639
0 0.00 0.00 0.00 1 1
0 0.00 0.00 0.00 60 60
0 0.00 0.00 0.00 61 61
0 0.00 0.00 0.00 65 65
0 0.00 0.00 0.00 67 67
0 0.00 0.00 0.00 68 68
0 0.00 0.00 0.00 69 69
0 0.00 0.00 0.00 71 71
0 0.00 0.00 0.00 73 73
0 0.00 0.00 0.00 75 75
0 0.00 25.60 75.82 68475 1 Activity Monito
0 0.00 11.74 97.63 1053 1 Adobe Crash Han
0 0.00 11.76 97.62 1084 1 Adobe Crash Han
0 0.00 11.69 97.64 1392 1 Adobe Crash Han
0 0.00 112.50 83.59 973 1 Adobe Desktop S
0 0.00 11.94 97.31 986 1 AdobeCRDaemon
0 0.00 16.95 105.99 966 1 AdobeIPCBroker
0 0.00 61.52 168.92 721 1 Adobe_CCXProces
0 0.00 18.57 3.01 454 1 adprivacyd
0 0.00 16.46 23.16 700 1 AGMService
0 0.00 13.65 4.43 701 1 AirPlayUIAgent
--snip--
0 0.00 9.11 12.72 89003 …03 VTDecoderXPCSer
0 0.00 13.32 4.69 418 1 WiFiAgent
0 0.00 12.21 1.58 543 543 WiFiProxy
# nothing here
I haven't done much in Powershell for a long time so if this is something stupid simple I'm going to laugh but I couldn't find anything searching the net.
Can someone tell me what's happening?
In order to use a process block (possibly alongside a begin, end, and, in v7.3+, the clean block), there must not be any code OUTSIDE these blocks - see the conceptual about_Functions help topic.
Therefore, remove $varname = "huh" from the top-level scope of your function body (possibly move it into one of the aforementioned blocks).
As for what you tried:
By having $varname = "huh" in the top-level scope of your function body, you've effectively made the function in one whose code runs in an implicit end block only.
process - because it is on its own line - was then interpreted as a command, which - due to the best-avoided default-verb logic - was interpreted as an argument-less call to the Get-Process cmdlet.
The output therefore included the list of all processes on your system.
The { ... } on the subsequent lines was then interpreted as a script block literal. Since that script block wasn't invoked, it was implicitly output, which results in its stringification, which is its _verbatim content, excluding { and }, resulting in output of the following string:
# nothing here
Most of the keys work in combination with AltGr+Alt+[key]. The not working exceptions I found are y and . (period). I am following this instruction (https://www.autohotkey.com/docs/KeyList.htm#SpecialKeys Step 1 to 5) in order to analyze the problem.
Log output for AltGr+Alt+x (my AHK script is working with that shortcut)
A2 01D d 11.34 LControl
A5 138 d 0.00 RAlt
A4 038 d 0.13 LAlt
58 02D d 0.20 x
58 02D u 0.13 x
A2 01D u 0.17 LControl
A5 138 u 0.00 RAlt
A4 038 u 0.00 LAlt
Log output for AltGr+Alt+y (my AHK script is NOT working with that short cut)
A2 01D d 1.89 LControl
A5 138 d 0.00 RAlt
A4 038 d 0.05 LAlt
A2 01D u 0.36 LControl
A5 138 u 0.00 RAlt
A4 038 u 0.06 LAlt
This log output leads me to believe, that the problem is not my script, but something more global. It seems that AHK is not even receiving the key stroke for "y".
Log output for AltGr+y to prove that my keyboard isn't defective :)
A2 01D d 10.27 LControl
A5 138 d 0.00 RAlt
59 02C d 0.23 y
59 02C u 0.13 y
A2 01D u 0.17 LControl
A5 138 u 0.00 RAlt
Can any of you reproduce this? How can I use AltGr+Alt+Y as a shortcut?
I am using Win10 x64 and AHK Version 1.1.30.01.
Edit
Obviously, the issue is only on my computer. So I would like to change the question slightly into: How can I identify and eliminate the reason that blocks some hotkeys? Remember that AltGr+Alt+Y is not the only hotkey that does not work, also AltGr+Alt+. and AltGr+Alt+O and possibly more that I have not tested yet.
Instead of AltGr+LAlt+Key or AltGr+RControl+Key which may cause issues in some programs or key jamming, you can use any other key in place of LAlt or RControl this way:
LControl & RAlt:: AltGr := true ; assign the boolean value "true" to the variable 'AltGr''
LControl & RAlt Up:: AltGr := false
; The #If directive creates context-sensitive hotkeys and hotstrings
#If (AltGr) ; If this variable has the value "true"
a:: MsgBox AltGr+a
a & b:: MsgBox AltGr+a+b
b & a:: MsgBox AltGr+b+a
h & l:: Send Hello
i & e:: Run iexplore.exe
n & p:: Run notepad
w & p:: Run wordpad
#If ; turn off context sensitivity
https://autohotkey.com/docs/commands/_If.htm
I have some text files and I need to remove the first character from the fourth column only if the column has four characters
file1 as follows
ATOM 5181 N AMET K 406 12.440 6.552 25.691 0.50 7.37 N
ATOM 5182 CA AMET K 406 13.685 5.798 25.578 0.50 5.87 C
ATOM 5183 C AMET K 406 14.045 5.179 26.909 0.50 5.07 C
ATOM 5184 O MET K 406 14.595 4.083 27.003 0.50 7.07 O
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5202 N AARG K 408 12.186 3.982 29.147 0.50 6.55 N
file2 as follows
ATOM 41 CA ATRP A 6 -18.975 -29.894 -7.425 0.50 19.50 C
ATOM 42 CA BTRP A 6 -18.979 -29.890 -7.428 0.50 19.16 C
ATOM 43 C HIS A 6 -18.091 -29.845 -8.669 1.00 19.84 C
ATOM 44 O HIS A 6 -17.015 -30.452 -8.696 1.00 20.10 O
ATOM 45 CB ASER A 9 -18.499 -28.879 -6.370 0.50 19.73 C
ATOM 46 CB BSER A 9 -18.565 -28.837 -6.367 0.50 19.13 C
ATOM 47 CG CHIS A 12 -19.421 -27.711 -6.216 0.50 21.30 C
Desired output
file1
ATOM 5181 N MET K 406 12.440 6.552 25.691 0.50 7.37 N
ATOM 5182 CA MET K 406 13.685 5.798 25.578 0.50 5.87 C
ATOM 5183 C MET K 406 14.045 5.179 26.909 0.50 5.07 C
ATOM 5184 O MET K 406 14.595 4.083 27.003 0.50 7.07 O
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5202 N ARG K 408 12.186 3.982 29.147 0.50 6.55 N
file2
ATOM 41 CA TRP A 6 -18.975 -29.894 -7.425 0.50 19.50 C
ATOM 42 CA TRP A 6 -18.979 -29.890 -7.428 0.50 19.16 C
ATOM 43 C HIS A 6 -18.091 -29.845 -8.669 1.00 19.84 C
ATOM 44 O HIS A 6 -17.015 -30.452 -8.696 1.00 20.10 O
ATOM 45 CB SER A 9 -18.499 -28.879 -6.370 0.50 19.73 C
ATOM 46 CB SER A 9 -18.565 -28.837 -6.367 0.50 19.13 C
ATOM 47 CG HIS A 12 -19.421 -27.711 -6.216 0.50 21.30 C
This might work for you (GNU sed):
sed -r 's/^((\S+\s+){3})\S(\S{3}\s)/\1 \3/' file
This replaces the first character of the fourth column with a space if that column has four non-space characters.
Use the length() function to find the length of the column and the substr() function to print the substring you need:
$ awk 'length($4)==4{$4=substr($4,2)}1' file | column -t
ATOM 5181 N MET K 406 12.440 6.552 25.691 0.50 7.37 N
ATOM 5182 CA MET K 406 13.685 5.798 25.578 0.50 5.87 C
ATOM 5183 C MET K 406 14.045 5.179 26.909 0.50 5.07 C
ATOM 5184 O MET K 406 14.595 4.083 27.003 0.50 7.07 O
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5202 N ARG K 408 12.186 3.982 29.147 0.50 6.55 N
Piping to column -t rebuilds a nice table format. To store the changes back to a file uses the redirection operator:
$ awk 'length($4)==4{$4=substr($4,2)}1' file | column -t > new_file
With sed you could do:
$ sed -r 's/^((\S+\s+){3})\S(\S{3}\s)/\1\3/' file
ATOM 5181 N MET K 406 12.440 6.552 25.691 0.50 7.37 N
ATOM 5182 CA MET K 406 13.685 5.798 25.578 0.50 5.87 C
ATOM 5183 C MET K 406 14.045 5.179 26.909 0.50 5.07 C
ATOM 5184 O MET K 406 14.595 4.083 27.003 0.50 7.07 O
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5185 CB MET K 406 14.812 6.674 25.044 0.50 6.80 C
ATOM 5202 N ARG K 408 12.186 3.982 29.147 0.50 6.55 N
To store the changes back to the original file you can use the -i option:
$ sed -ri 's/^((\S+\s+){3})\S(\S{3}\s)/\1\3/' file
I have pdb (protein data base) file which has thousands of lines.
REMARK 1 PDB file generated by ptraj (set 1000)
ATOM 1 O22 DDM 1 2.800 4.419 20.868 0.00 0.00
ATOM 2 H22 DDM 1 3.427 4.096 20.216 0.00 0.00
ATOM 3 C22 DDM 1 3.351 5.588 21.698 0.00 0.00
ATOM 4 H42 DDM 1 3.456 5.274 22.736 0.00 0.00
ATOM 5 C23 DDM 1 2.530 6.846 21.639 0.00 0.00
ATOM 6 H43 DDM 1 2.347 7.159 20.611 0.00 0.00
ATOM 7 O23 DDM 1 1.313 6.498 22.334 0.00 0.00
ATOM 8 H23 DDM 1 0.903 5.837 21.771 0.00 0.00
ATOM 9 C24 DDM 1 3.073 8.109 22.266 0.00 0.00
ATOM 10 H44 DDM 1 3.139 7.837 23.319 0.00 0.00
ATOM 11 O24 DDM 1 2.218 9.278 22.007 0.00 0.00
ATOM 12 H24 DDM 1 1.278 9.184 22.179 0.00 0.00
ATOM 13 C25 DDM 1 4.494 8.317 21.764 0.00 0.00
ATOM 14 H45 DDM 1 4.391 8.452 20.687 0.00 0.00
'
I want to insert word "TER" every 81 lines in that file whcih contains more than 20,000 lines but ignoring the first line since it is a comment.
I browse through internet, seems SED can do it. But i am lost.
Can anyone guide?
Thanks in advance.
Try this:
sed -i -e '1~81 i\TER' file
I'm partial to awk myself:
awk '{if(FNR%81==0)print "TER"; print}' file
I find this is a lot easier to understand and debug than the sed equivalent. The only magic is that FNR is the line number
You might have to fiddle with the numbers in the if to get it exactly the way you want it.
The more verbose shell commands would be
{
read header
echo "$header"
i=0
while read line; do
echo "$line"
if (( ++i == 81 )); then
echo TER
i=0
fi
done
} < infile > outfile &&
mv outfile infile
there are 200 files named File1_0.pdb,File1_60.pdb etc....it looks like:
ATOM 1 N VAL 1 8.897 -21.545 -7.276 1.00 0.00
ATOM 2 H1 VAL 1 9.692 -22.015 -6.868 1.00 0.00
ATOM 3 H2 VAL 1 9.228 -20.766 -7.827 1.00 0.00
ATOM 4 H3 VAL 1 8.289 -22.236 -7.693 1.00 0.00
TER
ATOM 5 CA VAL 1 8.124 -20.953 -6.203 1.00 0.00
ATOM 6 HA VAL 1 8.072 -19.874 -6.345 1.00 0.00
ATOM 7 CB VAL 1 6.693 -21.515 -6.176 1.00 0.00
ATOM 8 HB VAL 1 6.522 -22.024 -5.227 1.00 0.00
ATOM 9 CG1 VAL 1 5.684 -20.370 -6.330 1.00 0.00
ATOM 10 1HG1 VAL 1 5.854 -19.861 -7.279 1.00 0.00
i have to extract the part after TER and put in a different file...this has to be done on all 200 files. I did something like sed '1,/TER/d' File1_0.pdb > 1_0.pdb. But this will work for one file at a time...can there be a solution for all 200 files in one go... output file is named same only "File" is removed from the name...
for i in *.pdb; do sed '1,/TER/d' $i > ${i/File/}; done
This might work:
seq 0 200| xargs -i -n1 cp File1_{}.pdb 1_{}.pbd # backup files
sed -si '1,/TER/d' 1_{0..200}.pdb # edit files separately inline