Можно ли нажимать специальные символы в AppleScript?

Баг:

Для некоторых специальных символов и символов AppleScript не сможет найти keystrokeфактический символ и вместо этого просто наберет букву a.


Пример:

Следующий код AppleScript:

tell application "System Events" to keystroke "² ³ é ½ ₭"

вводит следующую строку текста (независимо от того, в каком приложении я нахожусь):

a a a a a

Обходной путь:

Можно эффективно «напечатать» текст, поместив специальные символы в буфер обмена, а затем вставив буфер обмена, например:

set the clipboard to "² ³ é ½ ₭"
tell application "System Events" to keystroke "v" using command down

Я избегаю использования описанного выше метода для помещения текста в буфер обмена в AppleScript, потому что в процессе он обязательно преобразует буфер обмена в форматированный текст.

Я предпочитаю использовать описанный здесь метод , чтобы убедиться, что обычный текст помещается в буфер обмена. Но этот метод преобразует специальные символы в моджибаке.

Например, используя ту же строку, что и выше, этот метод помещает в буфер обмена следующий текст:

² ³ é ½ ₭

Однако вы можете вручную учесть эту проблему, добавив replace_charsоператор для каждого специального символа.


Вопрос:

Есть ли способ вводить специальные символы через AppleScript без использования буфера обмена?

Например, есть ли способ добавить нужные специальные символы в «банк» или системный файл где-нибудь, чтобы System Eventsприложение было знакомо с ними?


Ответы (5)

Это возможно с CoreGraphics ( CGEventPost), это C API (он не работает в Cocoa-AppleScript), но можно вызвать исполняемый файл из AppleScript.


Вот метод создания исполняемого файла с помощью Xcode (версия 8.3.3):

Вы можете сделать это самостоятельно (можно скачать Xcode из App Store ), или попросить доверенное лицо создать исполняемый файл.

1- Откройте Xcode, выберите меню " Файл " --> " Создать " --> " Проект ..."введите описание изображения здесь


2- Выберите macOS и инструмент командной строки , нажмите кнопку « Далее ».введите описание изображения здесь


3- Назовите его TypeCharacters, выберите Objective-C в качестве языка, нажмите кнопку « Далее ».введите описание изображения здесь


4- Сохраните проект в нужную папку.


5- В окне Xcode выберите main.mзначок, очистите текст в окне и вставьте один из этих двух кодов:

Код нуждается в цикле из-за ошибки, CGEventKeyboardSetUnicodeString()метод усекает строку, которая превышает двадцать символов, или это ограничение (не задокументировано).

Этот код использует цикл для ввода списка символов на каждой итерации, список может содержать от одного до двадцати символов. (Это быстро, 1 секунда, чтобы ввести 2400 символов в документе TextEdit на моем компьютере.)

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        if (argc > 1) {
            NSString *theString = [NSString stringWithUTF8String:argv[1]];
            NSUInteger len = [theString length];
            NSUInteger n, i = 0;
            CGEventRef keyEvent = CGEventCreateKeyboardEvent(nil, 0, true);
            unichar uChars[20];
            while (i < len) {
                n = i + 20;
                if (n>len){n=len;}
                [theString getCharacters:uChars range:NSMakeRange(i, n-i)];
                CGEventKeyboardSetUnicodeString(keyEvent, n-i, uChars);
                CGEventPost(kCGHIDEventTap, keyEvent); // key down
                CGEventSetType(keyEvent, kCGEventKeyUp);
                CGEventPost(kCGHIDEventTap, keyEvent); // key up (type 20 characters maximum)
                CGEventSetType(keyEvent, kCGEventKeyDown);
                i = n;
                [NSThread sleepForTimeInterval:0.004]; // wait 4/1000 of second, 0.002 it's OK on my computer, I use 0.004 to be safe, increase it If you still have issues
            }
            CFRelease(keyEvent);
        }
    }
    return 0;
}

Этот код использует цикл для ввода одного символа на каждой итерации (это медленнее, чем первый код).

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        if (argc > 1) {
            NSString *theString = [NSString stringWithUTF8String:argv[1]];
            UniChar uChar;
            CGEventRef keyEvent = CGEventCreateKeyboardEvent(nil, 0, true);
            for (int i = 0; i < [theString length]; i++)
            {
                uChar = [theString characterAtIndex:i];
                CGEventKeyboardSetUnicodeString(keyEvent, 1, &uChar);
                CGEventPost(kCGHIDEventTap, keyEvent); // key down
                CGEventSetType(keyEvent, kCGEventKeyUp);
                CGEventPost(kCGHIDEventTap, keyEvent); // key up (type the character)
                CGEventSetType(keyEvent, kCGEventKeyDown);
                [NSThread sleepForTimeInterval:0.001]; // wait 1/1000 of second, no need of this line on my computer, I use 0.001 to be safe, increase it If you still have issues
            }
            CFRelease(keyEvent);
        }
    }
    return 0;
}

Примечание . Этот код работает на macOS Sierra и должен работать на El Capitan , но не на более старых ОС .

введите описание изображения здесь


6- Выберите меню " Продукт " --> " Сборка ".


7- Выберите TypeCharactersзначок в папке « Продукт », щелкните правой кнопкой мыши и выберите в контекстном меню пункт « Показать в Finder ».

введите описание изображения здесь


8- Из Finder переместите файл « TypeCharacters » в нужную папку, выйдите из Xcode , вот и все.


Из AppleScript вызовите исполняемый файл, например

set myString to "ùéèà ² ³ é ½ ₭ "
do shell script "'/full path/of/TypeCharacters' " & quoted form of myString
Невероятный ответ. Это решение безупречно работает и в OS X 10.11.6. О лучшем решении я и мечтать не мог. Я также ценю подробные скриншоты. Большое большое спасибо!!!
Я обнаружил ошибку: если длина myStringпревышает 20 символов, то строка будет обрезана. То есть любой текст, следующий за двадцатым символом, не будет напечатан. Вы знаете, как это исправить?
Теперь я использую цикл для ввода каждого символа (проверено со строкой из 2000 или более символов), я обновил код в своем ответе.
Ваше новое решение решило проблему. Спасибо! Примечание: вы удалили эту #import <Foundation/Foundation.h>строку из своего кода при редактировании, но эта строка по-прежнему необходима для успешной сборки. Я знаю, что эта строка кода есть по умолчанию, но существует некоторая двусмысленность, потому что в ваших инструкциях говорится «очистить текст в окне».
Я думаю, что новое решение имеет новую ошибку. Время от времени в набранном тексте будет отсутствовать символ или измениться порядок текста. Это происходит независимо от длины строки. Вот реальный пример: В файле TypeCharacters была указана строка, = 278,932но была введена следующая строка: =2 78,93.
Здесь нет ошибок. Может быть (замена, преобразование или правильное написание автоматически) в приложении переднего плана. Проверьте замену текста в «Системных настройках». В противном случае используйте код отладки (в моем ответе).
Возможно ( во время большой загрузки процессора ) коду нужна небольшая задержка между каждым нажатием кнопки KeyDown . Попробуйте так: добавьте эту строку в конец цикла [NSThread sleepForTimeInterval:0.02]; // wait 2/100 of second, increase it If you still have issues.. Я обновил код в своем ответе. Я добавляю еще один код в свой ответ (этот код может набирать 20 символов на каждой итерации вместо одного символа, попробуйте.
Хм. Это определенно не проблема замены/преобразования текста. Кроме того, проблема возникает, даже если никакое другое приложение не открыто, поэтому она не зависит от загрузки ЦП (и возникает в любом приложении, от TextEdit до Google Chrome). Я сбросил свои NVRAM и SMC; проблема сохраняется. Может дело в Эль-Капитане? Кстати, я использую Retina MacBook Pro начала 2013 года, так что, возможно, мой компьютер просто стареет и тормозит.
Независимо от того, что вызвало проблему, любое из двух ваших новых решений, похоже, полностью остановило проблему. Вот GIF-файлы, которые показывают результаты на моем компьютере: Если я использую 1-й блок кода в вашем текущем ответе. Если я использую код отладки . Если я использую 3-й блок кода (который я изменил с более короткой задержкой).
Я рад, что ваша проблема решена. На моем компьютере: для ввода одного символа на каждой итерации метод не нужен sleepForTimeInterval:, но он необходим для ввода списка символов на каждой итерации (конечно, если строка содержит более двадцати символов). Я обновил свой ответ (очистка).
Я понял, что этот TypeCharactersскрипт не может вводить символы новой строки. Кроме того, если строка содержит новую строку, она вообще не будет вводить строку. Вы знаете, как сохранить символы новой строки в печатном тексте? Спасибо.
Поскольку это фактически имитирует нажатие клавиш, если вы нажмете клавишу-модификатор во время этого, могут ли произойти плохие вещи?

Обходной путь (с буфером обмена):

1 - Вы можете использовать метод какао из скорлупы:

set tString to "² ³ é ½ ₭"
my stringToClipboard(tString)
tell application "System Events" to keystroke "v" using command down

on stringToClipboard(t1)
    do shell script "/usr/bin/python -c 'import sys;from AppKit import NSPasteboard, NSPasteboardTypeString; cb=NSPasteboard.generalPasteboard();cb.declareTypes_owner_([NSPasteboardTypeString], None);cb.setString_forType_(sys.argv[1].decode(\"utf8\"), NSPasteboardTypeString)' " & quoted form of t1
end stringToClipboard

2 - Или апплет Cocoa-AppleScript:

use framework "AppKit"
use scripting additions
set tString to "² ³ é ½ ₭"
my stringToClipboard(tString)
tell application "System Events" to keystroke "v" using command down

on stringToClipboard(t1)
    set ca to current application
    set cb to ca's NSPasteboard's generalPasteboard()
    cb's declareTypes:{ca's NSPasteboardTypeString} owner:(missing value)
    cb's setString:t1 forType:(ca's NSPasteboardTypeString)
end stringToClipboard

Для keystrokeсимвола этот символ должен быть сопоставлен с фактической клавишей на выбранной вами клавиатуре.

Например, я отлично могу использовать keystroke é и ² , потому что использую французскую раскладку клавиатуры ПК. Однако стандартная американская QWERTY-клавиатура не имеет этих клавиш и не сможет keystrokeих использовать.

Вы можете определить все свои специальные наборы символов как переменные, например

on specialKeys(input)
    tell application "System Events"
        keystroke input
    end tell
end specialKeys


set characterSet1 to "²³é½₭ "
set characterSet2 to "³ "
set characterSet3 to "é "
set characterSet4 to "¬Ω "
set characterSet5 to "₭ "
set characterSet6 to characterSet1 & characterSet2 & characterSet3 & characterSet4 & characterSet5

specialKeys(characterSet1)
-- ² ³ é ½ ₭ is the result from calling specialKeys(characterSet1) After adding the text replacement for “a a a a a” in my keyboard preferences

Некоторые специальные символы нажимаются правильно, если вы устанавливаете их как переменные. Любой из специальных символов, которые вы установили в качестве переменных, которые не нажимаются должным образом, вы можете добавить «текстовые замены» в системных настройках вашей клавиатуры.

Обязательно выберите все элементы и перетащите их на рабочий стол или куда-либо еще. Это создаст файл .plist, который будет резервной копией ваших исходных настроек. Вы можете перетащить этот файл обратно в любое время.

введите описание изображения здесь

введите описание изображения здесь

Затем в ScriptEditor убедитесь, что у вас включены замены текста в подменю «замены» меню редактирования.

введите описание изображения здесь

введите описание изображения здесь

Обходной путь «Замена текста» работает в большинстве приложений OS X (например, TextEdit, Notes, Script Editor и т. д.), но я надеялся найти решение, которое будет работать в каждом текстовом поле в любом приложении. Я много прошу, я знаю! Я все еще думаю, что это очень умное решение, так что +1. Спасибо за ответ.

Мы можем вводить символы, отличные от ASCII, с помощью буфера обмена:

tell application "System Events"
    set textBuffer to "² ³ é ½ ₭"
    repeat with i from 1 to count characters of textBuffer
        set the clipboard to (character i of textBuffer)
        delay 0.05
        keystroke "v" using command down
    end repeat
end tell