Что нового

Создание вложенного меню по данным из ini-файла

Suppir

Продвинутый
Сообщения
967
Репутация
62
Создаю вложенное меню, согласно данным из ini-файла.

В ini-файле написано:

Файл>открыть=open
Файл>сохранить как=open_as
Файл>сохранить=save
Редактировать>копировать=fcopy
Редактировать>вставить=fpaste
Выход=fexit


и так далее. Если строка содержит ">", то это значит, что в основном меню есть сабменю "Файл", в котором есть элемент "открыть".
Если в строке нет значка ">", то это значит, что элемент добавляется в корень меню. Например элемент "Выход", которому соответствует функция fexit.

Код:
$cDummy = GUICtrlCreateDummy()
$nContextMenu = GUICtrlCreateContextMenu($cDummy)
$hContextMenu = GUICtrlGetHandle($nContextMenu)

$aIni = IniReadSection("Программа.ini", "МЕНЮ")
Dim $aMenuItems[$aIni[0][0]+1]
$aMenuItems[0] = $aIni[0][0]

For $i = 1 To $aIni[0][0]
        $aMenuItems[$i] = GUICtrlCreateMenuItem($aIni[$i][0], $nContextMenu)
Next


Каким образом создать сабменю и добавить в эти сабменю элементы?
У меня почему-то получается несколько сабменю "файл" и "редактировать", в каждом из которых по одному элементу.
А нужно создать сабменю "файл" один раз и добавить туда несколько элементов. Проблема в том, что вся информация о сабменю и элементах должна браться только из ini-файла (он может меняться).
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
snoitaleR
Посмотрел, но в твоем коде заранее известно, что написано в ini-файле.
А у меня неизвестно. Т.е. пользователь может любое меню написать, с любыми сабменю.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Вот такой вариант:
Код:
#include <GUIConstantsEx.au3>
GUICreate("My GUI  Menu", 300, 200)
$aIni = IniReadSection("Программа.ini", "МЕНЮ")
$sDef='' 
For $i = 1 To $aIni[0][0]
   $aMenu=StringRegExp ( $aIni[$i][0] ,"([^>]*)>?(.*)",3 )
   If  $sDef<>$aMenu[0] Then $hMenu=GUICtrlCreateMenu($aMenu[0])
   If  $aMenu[1] Then GUICtrlCreateMenuItem($aMenu[1], $hMenu)
   $sDef=$aMenu[0]
Next

GUISetState()
While 1
   $msg = GUIGetMsg()  
   If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd

Возможно потребуется предварительная сортировка массива : $aIni
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
Если делать меню без ограничения вложенности, то получается довольно непростая задачка...

:smile:

Settings.ini

Код:
[Menu]
Open=Файл>Открыть
Save=Файл>Сохранить
OpenAs=Файл>Сохранить как
Copy=Редактировать>Копировать
Paste=Редактировать>Вставить
Separator=*
Skin=Настройки>Интерфейс>Скин
Size=Настройки>Интерфейс>Размеры
Color=Настройки>Интерфейс>Цвет
Separator=Настройки>Интерфейс>*
Expert=Настройки>Интерфейс>Дополнительно...
Miscellaneous=Настройки>Общие
Separator=*
Exit=Выход

Здесь символ "*" обозначает разделитель (если нужен), остальное, как в оригинале.

Example.au3

Код:
#Include <Array.au3>
#Include <GUIConstantsEx.au3>

Global $ID

#cs

$ID[i][0] - ID of the menu item
$ID[i][1] - Command

#ce

GUICreate('MyGUI', 400, 400)

$Menu = GUICtrlCreateContextMenu()
If Not _IniCreateMenu($Menu, @ScriptDir & '\Settings.ini', 'Menu', $ID) Then
	MsgBox(16, 'Error', 'Ошибка создания меню.')
	Exit
Else
;	_ArrayDisplay($ID)
EndIf

GUISetState()

While 1
	$Msg = GUIGetMsg()
	Switch $Msg
		Case 0
			ContinueLoop
		Case $GUI_EVENT_CLOSE
			Exit
		Case Else
			For $i = 1 To $ID[0][0]
				If $ID[$i][0] = $Msg Then
					; Здесь проверяются все пункты меню по их ключу в .ini файле
					Switch $ID[$i][1]
;						Case 'Open'

;						Case 'Save'

;						Case ...

						Case 'Exit'
							Exit
						Case Else
							ConsoleWrite($ID[$i][1] & @CR)
					EndSwitch
					ExitLoop
				EndIf
			Next
	EndSwitch
WEnd

Func _IniCreateMenu($CtrlID, $sPath, $sSection, ByRef $aID, $sRoot = '', $iLevel = 0)

	Static $aIni

	Local $Item, $Menu, $Path

	If Not $iLevel Then
		$aIni = IniReadSection($sPath, $sSection)
		If @error Then
			Return 0
		Else
			$Item = 0
			For $i = 1 To $aIni[0][0]
				If ($aIni[$i][1]) And (StringRegExpReplace($aIni[$i][1], '^.*>', '') <> '*') Then
					$Item += 1
				EndIf
			Next
			If $Item Then
				Dim $aID[$Item + 1][2] = [[0]]
			Else
				$aID = 0
				Return 0
			EndIf
		EndIf
	EndIf
	While 1
		$aIni[0][1] += 1
		If $aIni[0][1] > $aIni[0][0] Then
			Return 1
		EndIf
		$aIni[$aIni[0][1]][1] = StringRegExpReplace(StringRegExpReplace($aIni[$aIni[0][1]][1], '\A>*|>*\Z', ''), '>+', '>')
		If Not $aIni[$aIni[0][1]][1] Then
			ContinueLoop
		EndIf
		$Path = StringRegExpReplace($aIni[$aIni[0][1]][1], '>.[^>]*\Z', '')
		$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
		If $Item[0] = 1 Then
			If $sRoot Then
				$aIni[0][1] -= 1
				Return 1
			EndIf
		Else
			If $Path <> $sRoot Then
				If (Not $sRoot) Or (StringInStr($Path, $sRoot) = 1) Then
					If $sRoot Then
						$Item = StringSplit(StringReplace($aIni[$aIni[0][1]][1], $sRoot & '>', '', 1), '>')
						$Path = $sRoot & '>' & $Item[1]
					Else
						$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
						$Path = $Item[1]
					EndIf
					$aIni[0][1] -= 1
					$Menu = GUICtrlCreateMenu($Item[1], $CtrlID)
					If Not _IniCreateMenu($Menu, '', '', $aID, $Path, $iLevel + 1) Then
						Return 0
					EndIf
				Else
					$aIni[0][1] -= 1
					Return 1
				EndIf
				ContinueLoop
			EndIf
		EndIf
		If $Item[$Item[0]] <> '*' Then
			$aID[0][0] += 1
			$aID[$aID[0][0]][0] = GUICtrlCreateMenuItem($Item[$Item[0]], $CtrlID)
			$aID[$aID[0][0]][1] = $aIni[$aIni[0][1]][0]
		Else
			GUICtrlCreateMenuItem('', $CtrlID)
		EndIf
	WEnd
	Return 1
EndFunc   ;==>_IniCreateMenu
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,659
Репутация
2,457
Кто нибудь видел как в браузере Opera реализованы меню в ini?
Мне нравится такая структура, если есть интерес могу выложить скрипт для парсинга подобной структуры. Вот примерно так оно выглядит:

INI:
[Main Menu]
SubMenu, "&Tools" = My Tools Menu, -1, 2

[My Tools Menu]
Item, "Internet Explorer" = Execute, "IExplore.exe", "http://autoit-script.ru", "%ProgramFiles%\Internet Explorer\IExplore.exe", 0
Item, "  ---------- Programming -----------" = Disabled
Item, "AutoIt" = Execute, "%ProgramFiles%\AutoIt3\AutoIt3.exe",,, 0
---0=
Item, "Command Line" = Execute, "Cmd.exe",,, 0

  • -1 после названия меню указывает на файл иконки, -1 означает что этим файлом будет служить сам скрипт.
  • 0 после -1 это номер иконки (IconID).
  • После «Item, » указывается название пункта в кавычках.
  • Execute после знака равно это команда запуска внешнего приложения (после первой запятой указывается путь к программе), там также может быть любая внутренняя команда AutoIt, будет вызвана с помощбю Execute. После второй запятой указываются параметры запуска (аргументы), после третьей указывается путь к файлу иконки, и после четвёртой номер иконки в файле.
  • «Item, "..." = Disabled» означает что пункт будет отображаться, но он не будет активен (полезно для вывода информаций о группе пунктов).
  • «---0» это первый разделитель в меню.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
CreatoR сказал(а):
Кто нибудь видел как в браузере Opera реализованы меню в ini?

IMHO, слишком сложно, да и много лишнего в рамках AutoIt. Для простого пользовательского меню, структура .ini подходит как нельзя лучше (каждая строчка - отдельный пункт меню). А обработка идет на уровне GUI. Иконки? Можно поместить в отдельный раздел "[Icons]", если конечно это нужно. Хотя я не представляю, как простым способом (без использования сторонних библиотек) их прикрутить к меню, если только это не Vista/7...

Можно конечно и свою собственную структуру создать, но то, что предложил Suppir, думаю наиболее простой вариант как для пользователей, так и для программеров. А его (файла) обработка, в любом случае потребует рекурсии...

:smile:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,659
Репутация
2,457
Yashied [?]
IMHO, слишком сложно
Согласен, но возможностей куда больше ;).

много лишнего в рамках AutoIt
Как раз таки нет, у меня сверху вываливается бар с нужными мне инструментами и ссылками, так вот иногда его нужно править, я всего лишь открываю ini и добавляю/удаляю что мне нужно, кк говорится, мегаудобно! :laugh:

Можно конечно и свою собственную структуру создать
Ну то что я показал это моя собственная, это просто сама идея (концепция) у Opera украдена :whistle:

наиболее простой вариант как для пользователей, так и для программеров
Сомневаюсь я что программёры будут (будем?) использовать такой способ, им хочется чего нибудь повкуснее :D
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
А вот мне понравилась простота описания меню...

:smile:
 

sss

Продвинутый
Сообщения
332
Репутация
96
Yashied, твой скрипт - суперский! Попытался разобраться, не получилось. Но скрипт работает, добавил свои вложенные меню за пять секунд, классно! У меня есть вопрос: можно ли так же сделать меню в трее? Надо только заменить GuictrlCreateMenu на TrayCreateMenu и GUICtrlCreateMenuItem на TrayCreateItem, и изменить GuiGetMsg на TrayGetMsg?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
Sky-WaLkeR сказал(а):
У меня есть вопрос: можно ли так же сделать меню в трее?

Код:
#NoTrayIcon

Opt('TrayMenuMode',1)

Global $ID

#cs

$ID[i][0] - ID of the menu item
$ID[i][1] - Command

#ce

If Not _IniCreateTrayMenu(-1, @ScriptDir & '\Settings.ini', 'Menu', $ID) Then
	MsgBox(16, 'Error', 'Ошибка создания меню.')
	Exit
EndIf

TraySetState()

While 1
	$Msg = TrayGetMsg()
	Switch $Msg
		Case 0
			ContinueLoop
		Case Else
			For $i = 1 To $ID[0][0]
				If $ID[$i][0] = $Msg Then
					; Здесь проверяются все пункты меню по их ключу в .ini файле
					Switch $ID[$i][1]
;						Case 'Open'

;						Case 'Save'

;						Case ...

						Case 'Exit'
							Exit
						Case Else
							MsgBox(0, '', $ID[$i][1])
					EndSwitch
					ExitLoop
				EndIf
			Next
	EndSwitch
WEnd

Func _IniCreateTrayMenu($CtrlID, $sPath, $sSection, ByRef $aID, $sRoot = '', $iLevel = 0)

	Static $aIni

	Local $Item, $Menu, $Path

	If Not $iLevel Then
		$aIni = IniReadSection($sPath, $sSection)
		If @error Then
			Return 0
		Else
			$Item = 0
			For $i = 1 To $aIni[0][0]
				If ($aIni[$i][1]) And (StringRegExpReplace($aIni[$i][1], '^.*>', '') <> '*') Then
					$Item += 1
				EndIf
			Next
			If $Item Then
				Dim $aID[$Item + 1][2] = [[0]]
			Else
				$aID = 0
				Return 0
			EndIf
		EndIf
	EndIf
	While 1
		$aIni[0][1] += 1
		If $aIni[0][1] > $aIni[0][0] Then
			Return 1
		EndIf
		$aIni[$aIni[0][1]][1] = StringRegExpReplace(StringRegExpReplace($aIni[$aIni[0][1]][1], '\A>*|>*\Z', ''), '>+', '>')
		If Not $aIni[$aIni[0][1]][1] Then
			ContinueLoop
		EndIf
		$Path = StringRegExpReplace($aIni[$aIni[0][1]][1], '>.[^>]*\Z', '')
		$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
		If $Item[0] = 1 Then
			If $sRoot Then
				$aIni[0][1] -= 1
				Return 1
			EndIf
		Else
			If $Path <> $sRoot Then
				If (Not $sRoot) Or (StringInStr($Path, $sRoot) = 1) Then
					If $sRoot Then
						$Item = StringSplit(StringReplace($aIni[$aIni[0][1]][1], $sRoot & '>', '', 1), '>')
						$Path = $sRoot & '>' & $Item[1]
					Else
						$Item = StringSplit($aIni[$aIni[0][1]][1], '>')
						$Path = $Item[1]
					EndIf
					$aIni[0][1] -= 1
					$Menu = TrayCreateMenu($Item[1], $CtrlID)
					If Not _IniCreateTrayMenu($Menu, '', '', $aID, $Path, $iLevel + 1) Then
						Return 0
					EndIf
				Else
					$aIni[0][1] -= 1
					Return 1
				EndIf
				ContinueLoop
			EndIf
		EndIf
		If $Item[$Item[0]] <> '*' Then
			$aID[0][0] += 1
			$aID[$aID[0][0]][0] = TrayCreateItem($Item[$Item[0]], $CtrlID)
			$aID[$aID[0][0]][1] = $aIni[$aIni[0][1]][0]
		Else
			TrayCreateItem('', $CtrlID)
		EndIf
	WEnd
	Return 1
EndFunc   ;==>_IniCreateTrayMenu
 

sss

Продвинутый
Сообщения
332
Репутация
96
А как можно сделать чтобы при выборе пунктов галка не ставилась? Раньше спасался тем, что в Case первой строкой прописывал команду TrayItemSetState($item,$TRAY_UNCHECKED), теперь никак не могу этот самый $item, можно как-нить по-другому?UPD: решил добавлением TrayItemSetState($ID[$i][0],$TRAY_UNCHECKED) в описание каждого пункта меню. Но такой способ "некрасивый" )
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,659
Репутация
2,457
Sky-WaLkeR [?]
как можно сделать чтобы при выборе пунктов галка не ставилась?
Замени
Код:
Opt('TrayMenuMode',1)

на
Код:
Opt('TrayMenuMode', BitOR(1, 2))
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,659
Репутация
2,457
Sky-WaLkeR [?]
Раньше спасался тем, что в Case первой строкой прописывал команду TrayItemSetState($item,$TRAY_UNCHECKED)
Можно и тут так:
Код:
If $ID[$i][0] = $Msg Then
					TrayItemSetState($ID[$i][0], 4)
					...
				EndIf

но это неправильно.
 

sss

Продвинутый
Сообщения
332
Репутация
96
CreatoR, благодарю! Теперь все работает, галка не ставится! Спасибо!
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,716
CreatoR сказал(а):
Через Opt('TrayMenuMode', BitOR(1, 2)).

Ребята, будьте попроще...

Код:
Opt('TrayMenuMode', 1 + 2)


или

Код:
Opt('TrayMenuMode', 3)


:smile:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,659
Репутация
2,457
Yashied [?]
Ребята, будьте попроще
Я пытаюсь дать пример новичкам, чтобы поняли как корректно делается объединение значений (это обычно для констант полезно).

OffTopic:
P.S
Я ещё слишком молод чтобы искать простые решения :blum: хочется препятсвий
 
Верх