Что нового

фигурные кнопки в gui

saraconor

Новичок
Сообщения
420
Репутация
3
Допустим мне нужно сделать 2 кнопки близко друг к другу в gui. если кнопки прямоугольной формы, это не проблема:
h_1418289879_7510676_938399fb6a.png

А как быть если нужно сделать 2 фигурных кнопки, близко примыкающих друг к другу?
в этом случае их прямоугольные контуры будут перекрывать друг друга:
h_1418290129_6553885_63eaa31519.png

Как сделать так чтобы кнопка нажималась только по нажатию на видимую площадь ее картинки?
h_1418290252_7661398_26a5638e92.png
 

Yashied

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

Либо определить область нажатия математически, либо с помощью, например, _GDIPlus_BitmapGetPixel(), если пиксел полностью прозрачный, то кнопка не нажимается. Во втором случае нужно иметь два раздельных .png изображения для каждой кнопки.
 
Автор
S

saraconor

Новичок
Сообщения
420
Репутация
3
вы можете привести пример? вот кнопочки:
h_1418295131_8703189_f42c3f1f5e.png
h_1418295149_3435770_229ba3cd2b.png
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
http://autoit-script.ru/index.php?topic=1384.0

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

saraconor

Новичок
Сообщения
420
Репутация
3
вы можете привести пример таких кнопок с использованием _GDIPlus_BitmapGetPixel() ?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
saraconor сказал(а):
вы можете привести пример таких кнопок с использованием _GDIPlus_BitmapGetPixel() ?

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

saraconor

Новичок
Сообщения
420
Репутация
3
вот пожалуйста, во вложении файл, с общей картиной. а вот сам код:
Код:
#include <WindowsConstants.au3>		;GUI
#include "_GUICreateAlfa.au3"		;интерфейс с прозрачностью
#include <GuiButton.au3>
#include "NoFocusLines.au3"			;отключает рамки фокуса в GUI

$1 = _GUICreateAlfa("кнопочки",@ScriptDir&"\1.png")
$2 = GUICtrlCreateButton("",15,22,48,48,$BS_ICON)
GUICtrlSetImage(-1,@ScriptDir&"\2.ico", -1)
$3 = GUICtrlCreateButton("",65,22,48,48,$BS_ICON)
GUICtrlSetImage(-1,@ScriptDir&"\3.ico", -1)

_NoFocusLines_Set($2)	;убрать фокуса c нопки
_NoFocusLines_Set($3)	;убрать фокуса c нопки

GUISetState(@SW_SHOW)

while 1
Switch GUIGetMsg()
	Case $2
	exit
	Case $3
	exit
EndSwitch
wend

у меня к сожалению не получилось сделать даже кнопки с прозрачностью. умею только само окно gui так делать.
 

Вложения

  • тест.rar
    7.1 КБ · Просмотры: 15

Yashied

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

Yashied

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

Код:
#Include <APIConstants.au3>
#Include <GDIPlus.au3>
#Include <GUIConstantsEx.au3>
#Include <WinAPIEx.au3>

Global $hBmp[4] = ['buttons.bmp', 'button1.bmp', 'button2.bmp', 'buttons_mask.bmp']
Global $ID = -1

For $i = 0 To 3
	$hBmp[$i] = _WinAPI_LoadImage(0, @ScriptDir & '\' & $hBmp[$i], $IMAGE_BITMAP, 0, 0, $LR_LOADFROMFILE)
Next

_GDIPlus_Startup()
$hPng = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\background.png')
$hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hPng)
$hParent = GUICreate('', 122, 80, -1, -1, $WS_POPUP, $WS_EX_LAYERED)
_WinAPI_UpdateLayeredWindowEx($hParent, -1, -1, $hBitmap, 255, 1)
_GDIPlus_ImageDispose($hPng)
_GDIPlus_Shutdown()

$hForm = GUICreate('', 104, 62, -1, -1, $WS_POPUP, -1, $hParent)
GUISetBkColor(0xB4B4B4)
$Pic = GUICtrlCreatePic('', 18, 11, 69, 40)
;~GUICtrlSetState(-1, $GUI_DISABLE)
GUIRegisterMsg($WM_MOVE, 'WM_MOVE')
GUIRegisterMsg($WM_NCHITTEST, 'WM_NCHITTEST')
GUIRegisterMsg($WM_SETCURSOR, 'WM_SETCURSOR')
WM_MOVE($hParent, $WM_MOVE, 0, 0)
WM_SETCURSOR($hForm, $WM_SETCURSOR, 0, 0)
GUISetState(@SW_SHOW, $hParent)
GUISetState(@SW_SHOW, $hForm)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $Pic
			Switch $ID
				Case 1, 2
					MsgBox(64, 'Test', 'Button' & $ID)
				Case Else

			EndSwitch
	EndSwitch
WEnd

Func _GetIndex($iX, $iY)

	Local $hDC, $hMemDC, $hMemSv, $iRGB

	$hDC = _WinAPI_GetDC(0)
	$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
	$hMemSv = _WinAPI_SelectObject($hMemDC, $hBmp[3])
	$iRGB = _WinAPI_GetPixel($hMemDC, $iX, $iY)
	_WinAPI_ReleaseDC(0, $hDC)
	_WinAPI_SelectObject($hMemDC, $hMemSv)
	_WinAPI_DeleteDC($hMemDC)
	Switch $iRGB
		Case 0xFF0000
			Return 1
		Case 0x00FF00
			Return 2
		Case Else
			Return 0
		EndSwitch
	EndFunc   ;==>_GetIndex

Func _SetBitmap($iCtrlID, $hBitmap)

	Local $hPrev

	$hBitmap = _WinAPI_CopyBitmap($hBitmap)
	$hPrev = GUICtrlSendMsg($iCtrlID, 0x0172, $IMAGE_BITMAP, $hBitmap)
	If $hPrev Then
		_WinAPI_DeleteObject($hPrev)
	EndIf
	$hPrev = GUICtrlSendMsg($iCtrlID, 0x0173, 0, 0)
	If $hPrev <> $hBitmap Then
		_WinAPI_DeleteObject($hBitmap)
	EndIf
EndFunc   ;==>_SetBitmap

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hParent

			Local $Pos = WinGetPos($hParent)

			If IsArray($Pos) Then
				WinMove($hForm, '', $Pos[0] + 9, $Pos[1] + 9)
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

Func WM_NCHITTEST($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hForm
			_SendMessage($hParent, $WM_SYSCOMMAND, 0xF012, 0)
		Case $hParent
			Return $HTCAPTION
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NCHITTEST

Func WM_SETCURSOR($hWnd, $iMsg, $wParam, $lParam)

	Local $Index

	Switch $hWnd
		Case $hForm

			Local $aData = GUIGetCursorInfo($hForm)

			$Index = _GetIndex($aData[0] - 18, $aData[1] - 11)
			If $ID <> $Index Then
				_SetBitmap($Pic, $hBmp[$Index])
				Switch $Index
					Case 1, 2
						GUICtrlSetCursor($Pic, 0)
					Case Else
						GUICtrlSetCursor($Pic, 2)
				EndSwitch
				$ID = $Index
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_SETCURSOR


Небольшое пояснение. Поскольку форма "кнопок" нестандартная, то проще всего отказаться от кнопок как таковых и работать с изображениями. В данном случае я объединил два изображения в одно.

h_1418313163_5526060_7c67d1fb01.png

А для того, чтобы определить область, соответствующую заданным координатам курсора, я создал маску, где каждая область, читай кнопка, раскрашена в свой цвет, в данном случае 1 - красный (0xFF0000) и 2 - зеленый (0x00FF00).

h_1418313170_2323774_395ee9c8b7.png

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

P.S

Кстати, хороший хостинг изображений.

:smile:
 

Вложения

  • Test.zip
    11.6 КБ · Просмотры: 18
Автор
S

saraconor

Новичок
Сообщения
420
Репутация
3
Ммммдааааа.... к сожалению ничего не понятно. сплошная магия и единороги(
вы можете написать в коде примечания, что происходит в функциях?)
Ситуацию еще усложняет то что вы все изображения загнали в массив переменных и не совсем понятно где в коде конкретная картинка

Я так понимаю, вы все устроили так чтобы кнопки были на однотонном фоне. а если фон не однотонный?
h_1418374289_7952717_6f652627ad.png

Для чего вообще нужен этот серый прямоугольник поверх основного фона интерфейса?

п.с. что где удалось понять - использую непременно, и за это спасибо!
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Что именно не понятно? Когда мы кликаем мышкой по кнопкам, читай по buttons.bmp, то определяем координаты курсора мыши, а затем получаем цвет, соответствующий данным координатам, из изображения buttons_mask.bmp, который находится в памяти. Исходя из полученного цвета (красный, зеленый или черный), мы определяем, какая кнопка была нажата, т.е. красный - 1, зеленый - 2, черный или любой другой - 0 (мимо). Номер кнопки возвращает функция _GetIndex():

Код:
Func _GetIndex($iX, $iY)

	Local $hDC, $hMemDC, $hMemSv, $iRGB

	$hDC = _WinAPI_GetDC(0)
	$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
	$hMemSv = _WinAPI_SelectObject($hMemDC, $hBmp[3])
	$iRGB = _WinAPI_GetPixel($hMemDC, $iX, $iY)
	_WinAPI_ReleaseDC(0, $hDC)
	_WinAPI_SelectObject($hMemDC, $hMemSv)
	_WinAPI_DeleteDC($hMemDC)
	Switch $iRGB
		Case 0xFF0000 ; Красный
			Return 1 ; Нажата кнопка 1
		Case 0x00FF00 ; Зеленый
			Return 2 ; Нажата кнопка 2
		Case Else
			Return 0
	EndSwitch
EndFunc   ;==>_GetIndex


Что касается непосредственно GUI, то основное окно программы состоит из двух слоев. Нижний слой представляет собой layered окно с в виде PNG (background.png). В таких окнах вы не можете размещать стандартные элементы (кнопки, картинки и т.д.), поэтому нужно добавить второй слой, в котором и будет реализовано все управление. Цвет фона верхнего окна совпадает с фоном нижнего, поэтому этот пирог выглядит как одно целое. Далее нужно сделать так, что бы оба окна перемещались одновременно. За это отвечает функция-обработчик WM_NCHITTEST():

Код:
Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hParent

			Local $Pos = WinGetPos($hParent)

			If IsArray($Pos) Then
				WinMove($hForm, '', $Pos[0] + 9, $Pos[1] + 9)
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE


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

Код:
Func WM_NCHITTEST($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hForm
			_SendMessage($hParent, $WM_SYSCOMMAND, 0xF012, 0)
		Case $hParent
			Return $HTCAPTION
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NCHITTEST


Вот упрощенный пример с краткими комментариями:

Код:
#Include <APIConstants.au3>
#Include <GDIPlus.au3>
#Include <GUIConstantsEx.au3>
#Include <WinAPIEx.au3>

Global $ID, $Info, $hMask

; Загружаем изображение-маску для кнопок в память
$hMask = _WinAPI_LoadImage(0, @ScriptDir & '\buttons_mask.bmp', $IMAGE_BITMAP, 0, 0, $LR_LOADFROMFILE)

; Создаем Layered (нижнее) окно из PNG
_GDIPlus_Startup()
$hPng = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\background.png')
$hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hPng)
$hParent = GUICreate('', 122, 80, -1, -1, $WS_POPUP, $WS_EX_LAYERED)
_WinAPI_UpdateLayeredWindowEx($hParent, -1, -1, $hBitmap, 255, 1)
_GDIPlus_ImageDispose($hPng)
_GDIPlus_Shutdown()

; Создаем основное (верхнее) окно
$hForm = GUICreate('', 104, 62, -1, -1, $WS_POPUP, -1, $hParent)
GUICtrlCreatePic(@ScriptDir & '\background2.bmp', 0, 0, 104, 62)
GUICtrlSetState(-1, $GUI_DISABLE)
$Pic = GUICtrlCreatePic(@ScriptDir & '\buttons.bmp', 18, 11, 69, 40)

; Регистрируем необходимые обработчики
GUIRegisterMsg($WM_MOVE, 'WM_MOVE')
GUIRegisterMsg($WM_NCHITTEST, 'WM_NCHITTEST')

; Необходимо вызвать до отображения окон для их совмещения
WM_MOVE($hParent, $WM_MOVE, 0, 0)

; Отображаем нижнее и верхнее окна
GUISetState(@SW_SHOW, $hParent)
GUISetState(@SW_SHOW, $hForm)

While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $Pic
			; Определяем координаты курсора и соответствующий им индекс нажатой кнопки (0, 1 или 2)
			$Info = GUIGetCursorInfo($hForm)
			$ID = _GetIndex($Info[0] - 18, $Info[1] - 11)
			Switch $ID
				Case 1, 2
					MsgBox(64, 'Test', 'Button' & $ID)
				Case Else

			EndSwitch
	EndSwitch
WEnd

Func _GetIndex($iX, $iY)

	Local $hDC, $hMemDC, $hMemSv, $iRGB

	$hDC = _WinAPI_GetDC(0)
	$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
	$hMemSv = _WinAPI_SelectObject($hMemDC, $hMask)
	$iRGB = _WinAPI_GetPixel($hMemDC, $iX, $iY)
	_WinAPI_ReleaseDC(0, $hDC)
	_WinAPI_SelectObject($hMemDC, $hMemSv)
	_WinAPI_DeleteDC($hMemDC)
	Switch $iRGB
		Case 0xFF0000
			Return 1
		Case 0x00FF00
			Return 2
		Case Else
			Return 0
	EndSwitch
EndFunc   ;==>_GetIndex

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hParent

			Local $Pos = WinGetPos($hParent)

			If IsArray($Pos) Then
				WinMove($hForm, '', $Pos[0] + 9, $Pos[1] + 9)
			EndIf
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

Func WM_NCHITTEST($hWnd, $iMsg, $wParam, $lParam)
	Switch $hWnd
		Case $hForm
			_SendMessage($hParent, $WM_SYSCOMMAND, 0xF012, 0)
		Case $hParent
			Return $HTCAPTION
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NCHITTEST



Добавлено:
Сообщение автоматически объединено:

saraconor сказал(а):
Я так понимаю, вы все устроили так чтобы кнопки были на однотонном фоне. а если фон не однотонный?

См. пример выше.
 

Вложения

  • Test_simple.zip
    17.8 КБ · Просмотры: 13

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
С разноцветной маской, конечно, очень круто! :thumbs_up:
Тут мастерство налицо, и не поспоришь.

Но как-то уж очень сложно... Не понятно зачем два GUI.
Расположение картинки в GUI известно, координаты клика в рабочей области окна мы можем получить и, соответственно, рассчитать координаты клика в рамках картинки. Зачем постоянно отслеживать положение второго GUI, курсора?
Непонятно, для меня лично, зачем вот эти два слоя.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
C2H5OH сказал(а):
Но как-то уж очень сложно... Не понятно зачем два GUI.
Расположение картинки в GUI известно, координаты клика в рабочей области окна мы можем получить и, соответственно, рассчитать координаты клика в рамках картинки. Зачем постоянно отслеживать положение второго GUI, курсора?

Для того, чтобы иметь возможность создать на нем стандартные элементы GUI, т.к. на layered окне этого сделать нельзя. Если вы загляните в _GUICreateAlfa.au3, который изначально присутствовал в примере ТС, то увидите, что там также создается два окна, и тоже присутствует WM_NCHITTEST. Вот еще пример на эту тему.

:smile:
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Вроде бы логично, но непонятно с ходу.
Смущает необходимость отслеживания
Код:
GUIRegisterMsg($WM_MOVE, 'WM_MOVE')
GUIRegisterMsg($WM_NCHITTEST, 'WM_NCHITTEST')


Надо подумать...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
C2H5OH сказал(а):
Вроде бы логично, но непонятно с ходу. Надо подумать...

Если окно имеет стиль WS_EX_LAYERED, а по-другому PNG в него не запихнуть (типа красивые края, тени, прозрачность и пр. плюшки), то на таком окне никаких Label, Button, Edit и т.д. создать не получится. Поэтому поверх него создается стандартное окно, в котором все это хозяйство и располагается. Если ограничиться только layered окном, то придется самому рисовать все элементы управления, писать соответствующие обработчики и динамически перерисовывать и обновлять окно. В этом случае код будет выглядеть на порядок сложнее длиннее. Вот пример, который работает по этому принципу.


Добавлено:
Сообщение автоматически объединено:

C2H5OH сказал(а):
Смущает необходимость отслеживания

Код:
GUIRegisterMsg($WM_MOVE, 'WM_MOVE')
GUIRegisterMsg($WM_NCHITTEST, 'WM_NCHITTEST')

Закомментируйте эти строки и посмотрите что будет.
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
Yashied [?]
Если окно имеет стиль WS_EX_LAYERED, <...> то на таком окне никаких Label, Button, Edit и т.д. создать не получится.
А как же пример к функции
Код:
_WinAPI_SetLayeredWindowAttributes()


Вот часть примера с работающей кнопкой и текстом
Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WINAPI.au3>

$hGui = GUICreate("Тест прозрачности", 300, 400, -1, -1, -1, $WS_EX_LAYERED)
GUICtrlCreateLabel("Этот текст на прозрачном слое GUI", 10, 10, 200, 20, -1, $GUI_WS_EX_PARENTDRAG)
GUICtrlSetTip(-1, "Тяни за этот текст, чтобы перетащить многослойное окно")
$layButt = GUICtrlCreateButton("Кнопка", 10, 40, 40)
GUISetBkColor(0xABCDEF)
_WinAPI_SetLayeredWindowAttributes($hGui, 0xABCDEF)
GUISetState()

Do
  Switch GUIGetMsg()
    Case -3
      Exit
    Case $layButt
      MsgBox(0, "", "Pressed")
  EndSwitch
Until 0
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
InnI сказал(а):
Вот часть примера с работающей кнопкой и текстом...

Во-первых, выглядит это непотребно, т.к. все элементы, имеющие сглаживание или тени будут "вырублены" на фоне (см. Label), а во-вторых, мы же говорим здесь про подложку в виде PNG, не правда ли.
 

InnI

AutoIT Гуру
Сообщения
4,982
Репутация
1,460
Yashied [?]
говорим здесь про подложку в виде PNG
Я так и понял, что только стиля WS_EX_LAYERED недостаточно для невозможности создания элементов - нужна ещё "проблема" в виде картинки.
Но решил уточнить...
 
Верх