Что нового

Создание вложенного меню по данным из 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 711
Если делать меню без ограничения вложенности, то получается довольно непростая задачка...

: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 472
Репутация
2 401
Кто нибудь видел как в браузере 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 711
CreatoR сказал(а):
Кто нибудь видел как в браузере Opera реализованы меню в ini?
IMHO, слишком сложно, да и много лишнего в рамках AutoIt. Для простого пользовательского меню, структура .ini подходит как нельзя лучше (каждая строчка - отдельный пункт меню). А обработка идет на уровне GUI. Иконки? Можно поместить в отдельный раздел "[Icons]", если конечно это нужно. Хотя я не представляю, как простым способом (без использования сторонних библиотек) их прикрутить к меню, если только это не Vista/7...

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

:smile:
 

CreatoR

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

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

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

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

Yashied

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

:smile:
 

sss

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

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
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 472
Репутация
2 401
Sky-WaLkeR [?]
как можно сделать чтобы при выборе пунктов галка не ставилась?
Замени
Код:
Opt('TrayMenuMode',1)

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

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 472
Репутация
2 401
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 711
CreatoR сказал(а):
Через Opt('TrayMenuMode', BitOR(1, 2)).
Ребята, будьте попроще...

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


или

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


:smile:
 

CreatoR

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

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