Отправляет email-рассылки с помощью сервиса Sendsay

[prg] Вопросы по разработке плагинов и модулей приложений для nvda

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

1. Как отлаживать написанный плагин или модуль,т.е как понять,к
примеру,почему у меня не скомпилировался плагин или модуль,или почему он
скомпилировался,но не работает,т.е где и какой возник exception.

2. Правильно ли я понял,что при написании плагина или модуля
классы,которые наследуются от globalplugin.handler и appmodule.handler
должны называться именно globalplugin или appmodule(Названия всех
классов написаны приблезительно),а остальные классы,т.е пользовательские
классы,которые наследуются от других классов nvda api могут называться
как угодно?

3. Можно ли,и если да,то как,управлять работой плагина или модуля при
загрузке nvda и при завершении её работы?

4. Учитывая,что для написания плагинов и модулей для nvda
используется,как я понял,python 2.7,можно писать плагины,которые могут
служить играми,бизнес приложениями и т.д. Исходя из этого возникает
вопрос,куда загружать дополнительные библиотеки для python 2.7,которые
могут мне понадобиться при необходимости?

5. Как можно подписать элементы меню приложения,поскольку они не
подписаны,т.е какой метод основного класса для модуля приложения лучше
переопределить для этих целей? Как это можно сделать,учитывая,что когда
я,к примеру,нажимаю стрелку вниз от первого столбца меню,который
называется файл,все элементы меню читаются только с использованием
просмотрового курсора nvda,т.е когда я нажимаю клавиши 7 и 9 на
дополнительном блоке с выключенным numlock,т.е как получить список всех
объектов просмотрового курсора nvda?

6. в первом примере руководства nvda,показан пример,позволяющий
генерировать звук при переходе по элементам управления в блокноте. Мне в
нём всё понятно,кроме последней строки переопределённого метода,т.е
зачем мы в конце вызываем функцию nextHandler()?

7. Каким ещё образом,кроме словаря __gestures можно назначать жесты
функциям. Просто очень часто хочется использовать одну и ту же функцию с
разными жестами,при этом,истественно,меняя переменные в зависимости от
жеста,поэтому словарь __gestures тут не подойдёт,поскольку в словаре не
допускаются эдентичные ключи. К примеру,я хочу написать функцию,которая
будет мне проговаривать синус угла 2*pi/functionkey,где functionkey -
Идентификатор функциональной клавиши. Например,если пользователь нажмёт
nvda+shift+f1,то nvda сообщит ему синус угла 2*pi,если вместо f1 будет
f2 - pi,f3 - 2*pi/3,f4 - pi/2,f5 - 2*pi/5: f12 - pi/6. Алгоритм разбора
жеста я уже,думаю,реализовал,вопрос только в том,как теперь назначить
жесты на эту функцию,не используя словаря __gestures?

9. Мне непонятно,почему иногда в кодах плагинов и дополнений nvda,иногда
заключают строки в скобки,при этом ставя перед открывающей скобкой
_,ведь если я в обычном python 2.7 или 3.6 перед скобкой поставлю _,то у
меня будет синтаксическая ошибка. Также у меня не получается корректно
скопировать русскоязычную строку в буфер обмена,хоть я и перевожу строку
в utf-8,используя метод decode строкового объекта. У меня почему-то
копируется в буфер обмена только один странный символ. Я использую
функцию,которая примерно называется copyToClip и которая находится в
модуле api (Эта функция использовалась в дополнении clipcopy). Дайте
пожалуйста ссылку на подробную документацию всех модулей,классов и их
методов,позволяющих разрабатывать плагины и модули под nvda (даже пусть
она будет на английском),просто даже в англоязычном руководстве,по
материалом которого была написана статья на тифлокомпе,нету ссылки на
документацию по api nvda. Заранее благодарю всех за помощь.

Ответить   Thu, 24 Aug 2017 00:10:34 +0300 (#3514720)

 

Ответы:

Здравствуйте, Саша Козловский.

Информация об ошибках плагинов ссыпается в общий лог NVDA, который пишется
в директории временных файлов Windows.

Да.

Почитайте раздел Core в описании архитектуры NVDA. Это даст понимание
порядка загрузки, работы и выгрузки блоков NVDA.
Но вообще я не очень понимаю, что значит "управлять работой при запуске" -
глобальный плагин итак начнёт работать при запуске NVDA, а модуль приложения
при запуске целевого приложения и не раньше.

Я бы не советовал этим увлекаться.

Ну извращаться можно по-разному, однако самое простое в директорию плагина.
Так же можно и модулизировать код самого плагина, чтобы не писать монолитом.

Я не понял, при чём здесь модуль основного класса для модуля всего
приложения. Вам нужно изменить свойства (вероятно name) для конкретного
объекта внутри интерфейса приложения.
Проще всего делать это через событие NVDAObject_init.
Смотрите последний пример в руководстве, он как раз про это.

Мне тут трудно сказать, не видя интерфейс. Надо более чётко изучить его
структуру, после чего понять, чем нужные вам объекты уникальны и отлавливать
их по этому свойству. Иногда объекты для скриптинга вообще приходится
идентифицировать по косвенным признакам, типа значения разницы уровней
объекта и его прародителя.

Если бы вы не поленились просто попробовать этот пример, удалив непонятный
вам вызов функции, то таких вопросов бы не было.
nextHandler() используется в переопределяемых методах событий, чтобы
воспроизвести ряд последующих стандартных обработок. В частности, тут мы
модифицировали событие, вставив туда звук, но надо же и сохранить чтение
информации о контроле. За это и отвечает nextHandler(). Без него у вас бы
просто перестал читаться интерфейс, а только бы пищал.

Если у вас не получается переиспользовать код какой-то функции в разных
ситуациях, то, как мне кажется, проблема просто в дизайне кода и
адекватности его разбиения на функции. Так-то вы можете хоть на уровне
inputCore.InputGesture отлавливать пользовательский ввод.

Извините, но использование скан-кодов клавиш как данных в выражениях - это
какое-то безумие... Вы серьёзно собираетесь это так использовать?

Дальше вы кажется пропустили пункт 8, а последующие вопросы я не очень
понял, возможно и из-за пропуска, так что прокомментировать вопросы по коду,
который не видел, не могу.

Документация по модулям и классам прописана стандартными средствами Python в
самом коде NVDA. Можете выкачать исходники NVDA и читать там, либо же читать
через консоль Python внутри NVDA командами help и dir.
Ещё советовал бы почитать описание архитектуры NVDA, если у вас,
действительно, такие планы на низкоуровневые плагины, -
https://github.com/nvaccess/nvda/wiki/DesignOverview
Также можете поискать статьи Джозефа Ли (Joseph Lee) по разработке плагинов
для NVDA. У него есть ряд публикаций, где он разбирает конкретные плагины и
на их примерах рассказывает о различных приёмах и особенностях разработки.
Материал не очень строгий и структурированный, но возможно будет чем-то
полезен для формирования представлений. Ссылки нет, ищите по его имени и
названию NVDA Add-on Internals.
Ну и вообще больше пишите и экспериментируйте. Ответы на половину ваших
вопросов находятся просто написанием тестового примера.
Успехов. Никита.

Ответить   Sat, 26 Aug 2017 20:17:37 +0300 (#3515253)

 

Здравствуйте Никита. Вы писали

Я имею ввиду,к примеру,какие методы основного класса нужно
переопределить,чтобы при загрузке или выгрузке nvda происходило
какое-либо действие,к примеру,как написать плагин,чтобы при загрузке
nvda программа говорила текущую дату и время,а при завершении работы,к
примеру,сколько я использовал nvda. Соответствующие алгоритмы я могу
написать сам,меня лишь интересует какие методы основного класса нужно
переопределить для достижения поставленной цели. Ещё Вы пишете

Обоснуйте пожалуйста почему. Дело в том,что в python некоторые вещи
гораздо легче делать чем,к примеру,в c# или java,но python не у всех
незрячих стоит,а вот nvda стоит у многих незрячих,поэтому если я буду
писать приложения для nvda в виде плагинов или дополнений,то я не должен
буду думать о компиляции приложений. Ещё Вы пишете

А куда можно закинуть библиотеки,чтобы они работали для всех плагинов?
Вообще,когда я импортирую модули,где nvda ищет их,т.е в каких
директориях. Ещё Вы пишете

Я хочу озвучить меню screamer версия 044 (это программа для
прослушивания интернет радио). Ещё Вы пишете

Просто иногда бывает лучше написать одну функцию,выполняющее какое-то
действие,и менять её параметры в зависимости от жеста,чем писать,к
примеру,12 эдентичных функций с разными параметрами. Ещё Вы пишете

Я не совсем понял,как это делать,но судя по всему нужно,наверное,создать
пользовательский класс,наследующийся от inputCore.InputGesture и
переопределить там какие-то методы,или нужно это делать как-то
по-другому? Ещё Вы пишете

Это используется в тренеровочных целях,чтобы не писать в словарь
gestures 12 эдентичных функций,а сделать всё с помощью одной. Конечно,на
практике это очень мало кому пригодится,но в целях тренеровки написания
плагинов и модулей nvda это,мне кажется,не помешает. Ещё Вы пишете

Я просто пропустил цифру,а по поводу кода,символ подчёркивания перед
скобками со строковыми аргументами встречался в коде дополнения
captchaSolver. Также у меня не получается скопировать в буфер обмена
строку на Русском,хоть я её и декодирую,т.е вызываю её метод
decode('utf-8'),кстате можно ли как-то сразу проговаривать строку на
русском,или её обязательно нужно декодировать в utf-8,как это делаю я?
Копирую в буфер обмена я с помощью соответствующего метода модуля api,но
почему-то копируется только один символ. Ещё Вы пишете

функция dir,конечно,выдаёт мне кое-какую информацию,а вот при вызове
функции help ничего не происходит. А структурированной и очень подробной
документации,как я понял,нет. Просто даже если я использую функцию dir
без аргументов,она мне показывает не все модули,к примеру,она не
показала,что у меня есть модуль inputCore,о котором Вы мне написали и
который у меня действительно есть. Конечно,когда я потом вставил его в
функцию dir,то у меня,конечно,появилась кое-какая информация об этом
модуле,но этого,на мой взгляд,мало. Надеюсь,что-то полезное будет в
документации исходников nvda,которые,как я понял,лежат на githube.

Ответить   Sun, 27 Aug 2017 21:42:47 +0300 (#3515383)

 

Здравствуйте, Саша Козловский.

Как я уже сказал, смотрите модуль Core и решайте, куда именно вам удобнее
вклиниваться для ваших задач. Тут главное не влезть раньше инициализации тех
модулей, на которые вы будите опираться, ну типа не пытаться сказать фразу
раньше инициализации модуля вывода речи.

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

А как по-вашему справляется сама NVDA?
Вам ничего не мешает писать на Python нативные приложения. Существует py2exe
и прочее.

Куда угодно, например, в общую директорию пользовательских настроек NVDA.
Только стандартный установщик NVDA строго распаковывает каждое дополнение в
собственную директорию, поэтому либо вручную, либо в дополнении можно
прописать процедуру, отрабатывающую при установке, которая и будет
разбрасывать общие библиотеки. Однако вам придётся хорошо поработать над
архитектурой, чтобы управлять версионированием общих библиотек и прочим.

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

Мне это ни о чём не говорит, а ставить не готов, извините.

Если вы о чём-то типа
printf(_("Hello World!"), hello);
то это gettext - такая специальная система локализации строковых ресурсов.

Вообще с Unicode-строками надо работать как с u'Привет!', но пока займитесь
другим.
Почитайте сперва про тот самый gettext. По-хорошему все строки ваших
сообщений должны быть вынесены именно в po-файлы системы gettext.
Без этого вы толком ни один свой плагин локализовать и выложить для
международного сообщества не сможете.

Есть та, которая прописана как справка внутри кода.
help(modules) - получаете список всех модулей
help('module') - получаете справку по модулю module
dir('module') - получаете список всех функций модуля module
help('module.function') - получаете справку по функции function модуля
module
В общем я понимаю вашу горячность, но наверное имеет смысл ещё немного
подтянуть Python как таковой. Вам самому после этого будет проще.

Кроме того, что вы сможете получить вышеупомянутыми методами, там будут
разве что комментарии кода в некоторых местах.
Исходники, конечно, полезно читать, но это больше для общего знакомства, вот
как с вашей задачей по вклиниванию в загрузку и выгрузку. Тут полезно просто
открыть core.py и почитать сплошняком.
Успехов. Никита.
P.S. Кстати, существует специальный список рассылки по разработки плагинов
для NVDA, но он англоязычный. Если нужно, то могу поискать адрес подписки.

Ответить   Mon, 28 Aug 2017 00:23:34 +0300 (#3515395)

 

Здравствуйте Никита. Вы писали

Я не совсем Вас понял. Вы предлагаете мне создать пользовательский
класс,который наследуется от core и в нём переопределить соответствующие
методы,или Вы предлагаете мне модифицировать этот модуль? Кстати я так и
не понял,как работать с жестами,используя inputCore? Может есть пример
кода,который показывает,как это делать? Ещё Вы пишете

А в каких конкретно папках эти модули хранятся? Я пока что не собираюсь
назначать конкретные директории самостоятельно,поскольку я даже не
нашёл,как это в python делается. Ещё Вы пишете

Сначало у меня это не работало,но теперь я понял,что Вы имели в виду
help('modules'),поскольку согласно документации python можно в качестве
аргумента этой функции использовать строки. Но когда я даже в апострофах
или кавычках,к примеру,пишу help('api'),то мне тоже ничего не
проговаривается. А как же мне тогда получить документацию по этому
модулю и по остальным модулям nvda,используя средства python? Кстати я
тут поразмышлял на счёт Вашего первого примера в руководстве на
тифлокомпе. Читая исходники дополнений,я заметил,что в плагинах есть
тоже есть события,связанные с фокусом. В связи с этим у меня возник
вопрос,можно ли,и если да,то как,вызвать функцию nextHandler,к
примеру,только по нажатии какой-то комбинации клавиш? К примеру,я хочу
написать плагин,чтобы при нажатии стрелок nvda что-либо говорила,но не
то,что есть на экране,а при нажатии какой-либо комбинации клавиш nvda бы
снова работала так,как обычно. Я понимаю,что Вы не одобряете эту идею,но
всё же мне очень интересно,как это сделать. Логичнее всего
переопределить соответствующий метод для работы с фокусом,ноу меня
возникает вопрос,с какими аргументами его вызывать. Также мне
непонятно,каким образом при нажатии клавиш вызвать диалог. Как я понял
из исходников дополнения CapchaSolver нужно создать пользовательский
класс,наследующийся от класса соответствующего диалога,но как потом
отобразить этот диалог,я не понял. Я даже не понял,каким образом в этом
дополнении обрабатывалось нажатие на пункт меню,а вот с самим классом
диалога более или менее всё понятно. Также мне непонятно,в каких случаях
нужно предпочесть проговаривать информацию,используя модуль ui ,а в
каких модуль speech?

Ответить   Mon, 28 Aug 2017 11:42:49 +0300 (#3515466)

 

Здравствуйте, Саша Козловский.

Я предлагаю вам изучить исходный код этого модуля, в котором есть ряд
функций и классов, так как он не представляет собой монолитный объект.
Дальше уже будете решать, в какое место вам оптимально влазить, если уж
хочется делать низкоуровневую встройку кода. Там есть процедуры первой
загрузки, глобальный цикл и отдельные функции, отрабатывающие при завершении
NVDA.

Я уже, честно говоря, запутался, о чём именно вы спрашиваете, но если вы про
директории плагинов, то описание их функциональных назначений есть в
руководстве по упаковке дополнений (в т.ч. отдельной публикацией на
русском). Одноимённые директории есть в пользовательских и общих настройках,
а также в системной папке NVDA, вот к этим директориям и можно обращаться за
сторонними модулями, подставляя соответствующие переменные окружения.

проговаривается.

Проверьте по символам, что именно вы вводите. Всё должно работать. У меня по
help('api')
вываливается 5 с лишним тысяч знаков описания соответствующего модуля.

Вам определённо ещё раз надо перечитать базовое руководство, потому что там
есть пример решения этой задачи. Это буквально второй пример с написанием
скрипта, сразу после примера с обработкой события.
Такое впечатление, что вы в реальности прочитали только до первого примера с
Блокнотом.
Код плагина, отрабатывающий по нажатию клавиш, называется скриптом - это
такой же метод, просто с префиксом script_ и принимающий два аргумента:
экземпляр app module, global plugin или NVDA object, а также жест привязки.
Про это есть отдельный раздел в руководстве.

В общем подтяните Python и ещё раз перечитайте базовое руководство.
Извините, но очень утомительно пытаться объяснить то, что уже итак подробно
расписано с работающими примерами кода, попутно ещё проверяя, действительно
ли команды, которые всегда работали, вдруг перестали работать.
Ответы на большую часть вопросов, типа разницы между выводом через ui и
speech, вы найдёте, когда освоите вызов справки командами help и dir.
Успехов. Никита.

Ответить   Wed, 30 Aug 2017 20:52:14 +0300 (#3515919)

 

Здравствуйте Никита. Вы писали

Я спрашиваю о том,где хранятся,к примеру,модули,т.е к примеру куда мне
лучше загрузить,к примеру,pandass и numpy,но я уже вроде разобрался с
этим. Ещё Вы пишете

Я всё вводил правильно. Извините меня пожалуйста,это был мой косяк. Дело
в том,что я наивно рассчитывал,что nvda обязательно озвучит мне
текст,если он есть,а оказывается это не всегда работает. Я посмотрел в
историю и у меня действительно показывался текст. Кстати по-моему в
пятом примере разбирался пример,в котором подсчитывалось количество
символов в полях редактирования. Из этого примера,хоть это ранее и
объяснялось,я не понял,зачем нам создавать пользовательский
класс,наследующийся от класса,представляющего объект поле ввода? Разве
этот модуль,который я написал,не решает ту же задачу,что и модуль в
руководстве для разработчиков? Даже если бы была задача подсчитать сумму
символов во всех полях ввода данного приложения,я бы,наверное,получил
список объектов у окна приложения и потом бы суммировал количество
символов у списка children каждого дочернего объекта окна,делая при этом
соответствующие проверки,хотя,наверное,есть более правильный способ
сделать это,ведь приложение может быть многооконное. Главное как
получить список всех объектов в данном приложении. Кстати не могли бы Вы
пожалуйста привести пример ситуации,в которых без создания
пользовательского класса,наследующегося от nvda объекта нельзя улучшить
доступность приложения?
Ещё и в модуле,который в руководстве,и в моём модуле почему-то
проявляются 3 странных бага.
1. В строке,состоящей из пробелов,табов,возврата каретки и других особых
символов,не считая букв,цифр,знаков препинания,математических знаков и
других служебных знаков,nvda ничего не говорит,что не
удивительно,поскольку значение этого поля ввода почему-то равно None.
2. Почему-то если в поле ввода кроме не особых символов,присутствует
возврат каретки и,возможно,символ перевода строки,хотя в последнем я не
уверен,количество символов считается не правильно поскольку,как я
понял,возврат каретки и,возможно,перевод строки,считаются как 2 символа.
Неужели python считает возврат каретки как 2 отдельных символа - \ и
r,ведь именно так он записывается в неформатированных строках т.е \r.
3. Почему-то этот модуль не считает количество символов больше чем
4096,т.е я точно знаю,что у меня гораздо больше чем 4096 символов,но
nvda говорит,что количество символов у меня 4096. Подскажите
пожалуйста,как эти баги исправить. Ниже будет код модуля,который я
написал,чтобы доказать,что для решения задачи,поставленной в
руководстве,совсем не обязательно создавать пользовательский класс.
# -*- coding: utf-8
import appModuleHandler,ui,api,controlTypes
class AppModule(appModuleHandler.AppModule):
def script_speakHowMutchCharactersInEditableFeald(self,gesture):
obj=api.getFocusObject()
ui.message(str(len(obj.value))) if obj.windowClassName == "Edit" and
obj.role == controlTypes.ROLE_EDITABLETEXT else None
__gestures={"kb:NVDA+l":"speakHowMutchCharactersInEditableFeald"}

Ответить   Thu, 31 Aug 2017 00:14:47 +0300 (#3515936)

 

Здравствуйте, Саша Козловский <k.sasha19***@y*****.ru>.

* Исходное сообщение * РР> > Я уже, честно говоря, запутался, о чём именно вы спрашиваете

<...>

Уважаемые господа, правило разделять обсуждения по разным
письмам/темам ведь не зря придумано. Напихав всё в один текст вы
рискуете породить малоосмысленную загроможденную цитатами кучу-малу. Ее
и читать неудобно, и отвечать неудобно.

Ответить   Thu, 31 Aug 2017 17:26:20 +0300 (#3516036)

 

Здравствуйте Никита. Вы писали

Я не совсем Вас понял. Вы предлагаете мне создать пользовательский
класс,который наследуется от core и в нём переопределить соответствующие
методы,или Вы предлагаете мне модифицировать этот модуль? Кстати я так и
не понял,как работать с жестами,используя inputCore? Может есть пример
кода,который показывает,как это делать? Ещё Вы пишете

А в каких конкретно папках эти модули хранятся? Я пока что не собираюсь
назначать конкретные директории самостоятельно,поскольку я даже не
нашёл,как это в python делается. Ещё Вы пишете

Сначало у меня это не работало,но теперь я понял,что Вы имели в виду
help('modules'),поскольку согласно документации python можно в качестве
аргумента этой функции использовать строки. Но когда я даже в апострофах
или кавычках,к примеру,пишу help('api'),то мне тоже ничего не
проговаривается. А как же мне тогда получить документацию по этому
модулю и по остальным модулям nvda,используя средства python? Кстати я
тут поразмышлял на счёт Вашего первого примера в руководстве на
тифлокомпе. Читая исходники дополнений,я заметил,что в плагинах есть
тоже есть события,связанные с фокусом. В связи с этим у меня возник
вопрос,можно ли,и если да,то как,вызвать функцию nextHandler,к
примеру,только по нажатии какой-то комбинации клавиш? К примеру,я хочу
написать плагин,чтобы при нажатии стрелок nvda что-либо говорила,но не
то,что есть на экране,а при нажатии какой-либо комбинации клавиш nvda бы
снова работала так,как обычно. Я понимаю,что Вы не одобряете эту идею,но
всё же мне очень интересно,как это сделать. Логичнее всего
переопределить соответствующий метод для работы с фокусом,ноу меня
возникает вопрос,с какими аргументами его вызывать. Также мне
непонятно,каким образом при нажатии клавиш вызвать диалог. Как я понял
из исходников дополнения CapchaSolver нужно создать пользовательский
класс,наследующийся от класса соответствующего диалога,но как потом
отобразить этот диалог,я не понял. Я даже не понял,каким образом в этом
дополнении обрабатывалось нажатие на пункт меню,а вот с самим классом
диалога более или менее всё понятно. Также мне непонятно,в каких случаях
нужно предпочесть проговаривать информацию,используя модуль ui ,а в
каких модуль speech?

Ответить   Mon, 28 Aug 2017 11:43:01 +0300 (#3515467)

 

Здравствуйте, Саша!

28 Августа 2017 года в 16:42 Саша Козловский <k.sasha19***@y*****.ru> пишет
на тему "[prg] Re[4]: Вопросы по разработке плагинов и модулей
приложений для nvda":

удобнее...

28 Августа 2017 года в 16:43 Саша Козловский <k.sasha19***@y*****.ru> пишет
на тему "[prg] Re[4]: Вопросы по разработке плагинов и модулей
приложений для nvda":

удобнее...

+ - дублирование одного и того же письма.

Ответить   Mon, 28 Aug 2017 17:25:46 +0800 (#3515487)