Тема: AHK: Закрытие определённой вкладки Google Chrome
Доброго времени суток Форумчане. Существует ли возможность используя AHK закрыть вкладку браузера с определенным названием?
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Доброго времени суток Форумчане. Существует ли возможность используя AHK закрыть вкладку браузера с определенным названием?
Желательно указать браузер и приложить скриншот. И ещё: название вкладки известно заранее или формируется динамически?
Браузер Google Chrome, имя закладки заранее известно.
Можно при помощи команды ImageSearch сначала найти в окне саму вкладку, затем найти справа её кнопку-крестик "закрыть" и послать в эти координаты нажатие ЛКМ командой ControlSend.
Можно при помощи команды ImageSearch сначала найти в окне саму вкладку, затем найти справа её кнопку-крестик "закрыть" и послать в эти координаты нажатие ЛКМ командой ControlSend.
Зачем крестик-то искать? Клик и Ctrl+w.
В качестве альтернативы, более устроившей лично меня, - перебор вкладок посредством Ctrl+TAB с проверкой имени окна (оно меняется в соответствии с именем текущей вкладки), а при нахождении те же Ctrl+w.
А вообще давно и безуспешно пытаюсь найти решение без переборов и сравнения изображений. Одно время заголовки неактивных вкладок отображались как скрытый текст, что позволяло просто найти хотя бы вкладку с уникальным заголовком. Полагаю, курить надо гуглхромовые форумы, может есть плагин, выдающий сведения о вкладках в удобоваримой для внешнего софта форме. Но плагинов, связанных с вкладками - тьма, а интересующая нас функция не сказать, что сильно востребована...
У меня так получилось. Закрываем вкладку "Яндекс" в активном окне Хрома по F10:
TabName := "Яндекс"
F10::
AccChrome := AccObjectFromWindow(hWnd := WinExist("A"))
if oCoords := GetCloseButtonCoords(TabName, AccChrome)
{
WinGetPos, X, Y,,, ahk_id %hWnd%
X := oCoords.x - X + oCoords.w//4
Y := oCoords.y - Y + oCoords.h//2
ControlClick, x%X% y%Y%, ahk_id %hWnd%
}
return
GetCloseButtonCoords(TabName, AccObj)
{
if AccRole(AccObj) = "язычок страницы" && AccObj.accName(0) = TabName
Return AccLocation(AccChild(AccObj, 1))
for k, v in AccChildren(AccObj)
if location := GetCloseButtonCoords(TabName, v)
Return location
}
AccInit()
{
static h
!h ? h := DllCall("LoadLibrary","Str","oleacc","Ptr")
}
AccObjectFromWindow(hWnd, idObject = -4)
{
AccInit()
If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF
, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81
, NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
Return ComObjEnwrap(9,pacc,1)
}
AccRole(Acc, ChildId=0)
{
try return ComObjType(Acc,"Name")="IAccessible"?AccGetRoleText(Acc.accRole(ChildId)):"invalid object"
}
AccGetRoleText(nRole)
{
nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
Return sRole
}
AccLocation(Acc, ChildId=0)
{
try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
catch
return
return {x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")
, pos:"x" NumGet(x,0,"int")" y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")}
}
AccChildren(Acc)
{
cChildren:=Acc.accChildCount, Children:=[]
if DllCall("oleacc\AccessibleChildren", "Ptr", ComObjValue(Acc), "Int", 0, "Int", cChildren
, "Ptr", VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*", cChildren)=0 {
Loop %cChildren%
i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i)
, Children.Insert(NumGet(varChildren,i-8)=3?child:AccQuery(child)), ObjRelease(child)
return Children
}
error:=Exception("",-1)
MsgBox, 262420, AccChildren Failed, % "File: " error.file "`nLine: " error.line "`n`nContinue Script?"
IfMsgBox, No
ExitApp
}
AccChild(Acc, ChildId=0)
{
try child:=Acc.accChild(ChildId)
return child?AccQuery(child):
}
AccQuery(Acc)
{
try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Язык интерфейса хрома должен быть русским.
teadrinker, куда идти курить что бы это было без участия пользователя, и проверка была раз в 5 минут?и сложно будет это реализовать под другие браузеры?(opera, firefox)
что бы это было без участия пользователя
Как вы себе это представляете?
teadrinker, я себе это представляю, как бесконечный цикл или вызов по таймеру той процедуры, которая в вашем скрипте вызывается по нажатии F10. Возможно, kaipov представляет себе это несколько иначе.
teadrinker, Вы правы. Я это представляю как бесконечный цикл, но в виде службы, которая запускается с ОС, и например раз в 5 минут, проверяет наличие вкладки с определенным названием, и при нахождении закрывает ее.
Тогда так примерно:
#Persistent
TabName := "Яндекс"
SetTimer, TabWatch, 300000
return
TabWatch:
WinGet, List, List, ahk_class Chrome_WidgetWin_1
Loop % List
{
AccChrome := AccObjectFromWindow(hWnd := List%A_Index%)
if oCoords := GetCloseButtonCoords(TabName, AccChrome)
{
WinGetPos, X, Y,,, ahk_id %hWnd%
X := oCoords.x - X + oCoords.w//4
Y := oCoords.y - Y + oCoords.h//2
ControlClick, x%X% y%Y%, ahk_id %hWnd%
}
}
return
GetCloseButtonCoords(TabName, AccObj)
{
if AccRole(AccObj) = "язычок страницы" && AccObj.accName(0) = TabName
Return AccLocation(AccChild(AccObj, 1))
for k, v in AccChildren(AccObj)
if location := GetCloseButtonCoords(TabName, v)
Return location
}
AccInit()
{
static h
!h ? h := DllCall("LoadLibrary","Str","oleacc","Ptr")
}
AccObjectFromWindow(hWnd, idObject = -4)
{
AccInit()
If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF
, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81
, NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
Return ComObjEnwrap(9,pacc,1)
}
AccRole(Acc, ChildId=0)
{
try return ComObjType(Acc,"Name")="IAccessible"?AccGetRoleText(Acc.accRole(ChildId)):"invalid object"
}
AccGetRoleText(nRole)
{
nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
Return sRole
}
AccLocation(Acc, ChildId=0)
{
try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
catch
return
return {x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")
, pos:"x" NumGet(x,0,"int")" y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")}
}
AccChildren(Acc)
{
cChildren:=Acc.accChildCount, Children:=[]
if DllCall("oleacc\AccessibleChildren", "Ptr", ComObjValue(Acc), "Int", 0, "Int", cChildren
, "Ptr", VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*", cChildren)=0 {
Loop %cChildren%
i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i)
, Children.Insert(NumGet(varChildren,i-8)=3?child:AccQuery(child)), ObjRelease(child)
return Children
}
error:=Exception("",-1)
MsgBox, 262420, AccChildren Failed, % "File: " error.file "`nLine: " error.line "`n`nContinue Script?"
IfMsgBox, No
ExitApp
}
AccChild(Acc, ChildId=0)
{
try child:=Acc.accChild(ChildId)
return child?AccQuery(child):
}
AccQuery(Acc)
{
try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Окно может быть неактивным и перекрытым, но не должно быть свёрнутым.
Ярлык скрипта положить в папку автозагрузки.
С Оперй так же нельзя, с FF можно.
Тогда так примерно:
код
Окно может быть неактивным и перекрытым, но не должно быть свёрнутым.
Ярлык скрипта положить в папку автозагрузки.
С Оперй так же нельзя, с FF можно.
Чрезвычайно интересно, но много моментов, очень далеких от собственно АНК. Может кто-нибудь (не обязательно автор) взялся бы откомментировать код, чтобы, поняв его хоть отчасти, можно было бы переделать под свои нужды? Меня в частности более всего интересует механизм перебора вкладок и получения их заголовков.
Инструмент для исследования accessible-структуры окон, библиотека для работы с accessible-интерфейсом.
Accessible-структура имеет древовидную форму, объекты структуры имеют отношения parent-children. Функция GetCloseButtonCoords() в моём коде рекурсивно исследует структуру окна, двигаясь от родительских объектов к дочерним до тех пор, пока не найдёт вкладку ("язычок страницы") с нужным названием. После этого она получает и возвращает координаты кнопки закрытия вкладки. В случае с хромом это четырёхугольник вокруг реальной кнопки "с запасом", сама кнопка находится в центре его левой части. Всё это наглядно можно исследовать с помощью указанного выше инструмента.
Информация об интерфейсе Accessible в MSDN.
Инструмент для исследования accessible-структуры окон, библиотека для работы с accessible-интерфейсом.
Accessible-структура имеет древовидную форму, объекты структуры имеют отношения parent-children. Функция GetCloseButtonCoords() в моём коде рекурсивно исследует структуру окна, двигаясь от родительских объектов к дочерним до тех пор, пока не найдёт вкладку ("язычок страницы") с нужным названием. После этого она получает и возвращает координаты кнопки закрытия вкладки. В случае с хромом это четырёхугольник вокруг реальной кнопки "с запасом", сама кнопка находится в центре его левой части. Всё это наглядно можно исследовать с помощью указанного выше инструмента.
Информация об интерфейсе Accessible в MSDN.
Есть ли шансы найти информацию на русском? Изучение английского даже для решения давней проблемы не входит в мои планы.
Не знаю, я не искал.
Бывает, не обращайте внимания.
Дело в том, что там нет опции "разрешить" или "пропустить". А "бывает" всякий раз, как я пытаюсь скачать.
Какие сопроматы мне скурить для решения этой проблемы?
Нажми в хроме Ctrl+J, откроются загрузки и там ты сможешь скачать.
Нажми в хроме Ctrl+J, откроются загрузки и там ты сможешь скачать.
Нет, там тоже не пускает.
Но таким вот образом присутствие Opera в моей системе получило практическое обоснование.
Таки интересно, что это гугл пометил сию приладу как вредоносную?
Что ж, в целом концепция ясна, но освоение для меня чрезмерно трудоемко.
Правильно ли я понимаю, что из всех этих элементов собственно к содержимому вкладки имеет отношение только текст на язычке?
Тогда получается фактически то же самое, что и при переключении вкладок с проверкой названия окна, только без этого самого переключения?
Просто в наиболее меня интересующем на данный момент частном случае заголовки вкладок одинаковы, да и вообще в Accessible Info Viewer они различаются только номером язычка и координатой-х. В этом случае метод бесполезен? Или возможно извлечь еще какие-то сведения о вкладках, не отображаемые в окне Accessible Info Viewer?
Вопрос вдогонку.
Окно может быть неактивным и перекрытым, но не должно быть свёрнутым.
Это условие для работы скрипта в целом или и для работы с accessible-интерфейсом в частности?
Не удается запустить вышеприведенный скрипт.
Правильно ли я понимаю, что из всех этих элементов собственно к содержимому вкладки имеет отношение только текст на язычке?
К содержимому кнопки вкладки.
Тогда получается фактически то же самое, что и при переключении вкладок с проверкой названия окна, только без этого самого переключения?
Ну да, именно без переключения, просто определение координат и посыл клика.
Вопрос вдогонку.
teadrinker пишет:Окно может быть неактивным и перекрытым, но не должно быть свёрнутым.
Это условие для работы скрипта в целом или и для работы с accessible-интерфейсом в частности?
Имеет значение только для определения координат.
Не удается запустить вышеприведенный скрипт.
Виноват, отредактировал, у меня просто эти функции в библиотеке, а в скрипте название исправить забыл.
Функция GetCloseButtonCoords() в моём коде рекурсивно исследует структуру окна, двигаясь от родительских объектов к дочерним до тех пор, пока не найдёт вкладку ("язычок страницы") с нужным названием. После этого она получает и возвращает координаты кнопки закрытия вкладки.
Рекурсия и прочие изящные рациональности кода, несомненно понятные и приятные для специалистов, чрезвычайно затрудняют мне использование его качестве примера.
Был бы признателен за простой пример получения названия язычка в такой частности:
Попутно вопросы:
1. Перебор объектов по нисходящей обязателен? Нет ли возможности прямого обращения (типа пути или адреса, для моего скрина, например, 4/1/2/3/1/3 - нечто подобное отображается в Accessible Info Viewer, только через точку)? Просто как бы я не надругивался над хромом, все равно все вкладки (в смысле язычки) располагаются в одном и том же месте древа и в одной и той же последовательности (то есть 4/1/2/3/1/1 - кнопка создания новой вкладки, а начиная с 4/1/2/3/1/2 - вкладки).
2. Скачав библиотеку, я ожидал увидеть dll, но это оказалась библиотека в смысле АНК. Я так понимаю, необходимые выдержки из нее - уже в коде приведенного выше скрипта?
Был бы признателен за простой пример получения названия язычка
1. В последней версии библиотеки есть Acc_Get():
MsgBox % Acc_Get("Name", "4.1.2.3.1.2", 0, "ahk_class Chrome_WidgetWin_1") ; название первой вкладки
Но нет гарантии, что в будущих версиях Chrome этот путь останется прежним.
2. Да, чтобы можно было пользоваться, не имея библиотеки.
многобанофф пишет:Был бы признателен за простой пример получения названия язычка
1. В последней версии библиотеки есть Acc_Get():
MsgBox % Acc_Get("Name", "4.1.2.3.1.2", 0, "ahk_class Chrome_WidgetWin_1") ; название первой вкладки
Но нет гарантии, что в будущих версиях Chrome этот путь останется прежним.
Шикааарно. Главное, чтобы вообще остался (а я так понимаю, никуда не денется), а уж какой - глянуть недолго. Премного благодарен.
Пытаюсь все-таки в меру возможностей вдумчиво укуривать басурманское чтиво.
Поднакопились вопросы, представляющиеся неразрешимыми самостоятельно.
1. Заинтересовали методы IAccessible - accDoDefaultAction, put_accValue, get_accDescription, accSelect. Первые два - просто как полезная возможность что-то нажать или ввести данные получается без (?) активизации окна. Get_accDescription занадобился, когда стал изучать кнопку "обновить страницу" хрома, по внешнему виду которой до сих пор в моих скриптах определяется окончание загрузки страницы. Так вот Description тоже при этом меняется, его отслеживать куда удобнее, чем цвет пиксела. АccSelect тоже для хрома - как я уже писал когда-то фокус при отображении флеш-приложений становится неочевидным. Accessible Info Viewer позволил в этом разобраться. Оказалось, что фокус может быть отдельно на окне с флешем и на заголовке окна. В первом случае как обычно доступны все команды окну, во втором почти все они окно минуют и идут в приложение, образно выражаясь. До сего момента приходилось лишний раз клацать по заголовку. АccSelect - заманчивая альтернатива лишнему клацанию.
Собственно вопрос. В Асс.анк я их не нашел, в Acc_Get пихать пытался, но безрезультатно. Я так понял адаптированы для АНК посредством Асс.анк не все методы, а использовать неадаптированные можно посредством DllCall с указанием туевой хучи параметров? Если да, то нельзя ли дополнить хотя бы get_accDescription и accSelect, а то сам уж точно не вкурю?
2. Пока писал, забыл. Отдельно напишу.
Попутно.
В ходе изысканий обнаружилась пресловутая строка состояния хрома (4.2). Появляется в структуре древа на время визуального отображения. Содержит элемент "текст", для которого можно извлечь некоторые атрибуты, но Value не извлекается. Жаль.
нельзя ли дополнить хотя бы get_accDescription и accSelect
Как будет время.
Метод get_accDescription реализуется просто через
Description := AccObj.accDescription(varID)
где varID либо 0 (если нужно получить описание самого объекта), либо индекс дочернего объекта. Description есть не у всех объектов.
Метод accSelect так же:
AccObj.accSelect(flagsSelect, varID)
где flagsSelect — один или комбинация флагов отсюда.
Я так понимаю, это подразумевает необходимость спуститься по иерархии до нужного элемента.
А можно ли достичь того же опять используя Path из Accessible Info Viewer?
Если нет - нельзя ли объяснить как навигация происходит? Из приведенного в теме скрипта никак понять не могу - обилие функций мешает. Ясно, что можно, среди прочих свойств (? здесь и далее не уверен в терминологии), узнать количество дочерних объектов, но как начать оперировать с одним из них мне непонятно.
многобанофф, избегайте ненужного цитирования, я отредактировал Ваш пост.
Я так понимаю, это подразумевает необходимость спуститься по иерархии до нужного элемента.
А можно ли достичь того же опять используя Path из Accessible Info Viewer?
Без разницы, как получать ссылку на объект, можно и так, и так. Браузер Crome не слишком удачный пример для использования IAccessible, он поддерживает этот интерфейс пока в незначительной степени. Если остались вопросы, создайте соответствующую тему.
Хотелось бы задать автозакрытие определенной вкладки/сайта (или нескольких) в браузере Chrome спустя, например, минуту после ее открытия. Как это можно реализовать?
Доброго времени суток. Прошло 5 лет и способ уже не работает Если использовать скрип выше то вкладка просто выделяется, но не закрывается к сожалению. Помогите пожалуйста исправить этот не достаток, буду признателен!
Kvisk
Возможно, в Хроме какие-то использованные в скрипте названия поменялись.
Для анализа структуры iaccessible не используйте багнутый accviewer, а используйте либо:
AccExplorer32:
https://github.com/blackrosezy/gui-insp … orer32.exe
либо Inspect:
https://github.com/guolaok/Python-UIAut … er/inspect
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться