Sign in to follow this  
Followers 0
neazoi

Image pick and place into grid

19 posts in this topic

Hi,

I have made a javascript program that can create online electronics schematics http://5.54.115.143/jlab/cad/schematic/index.html

It works like a puzzle. You pick an image from the table above and then you place it into the table below (blank area). That way you create schematics, like a puzzle.

I would like to move this functionality from javascript to autoit.

Is there any example of puzzle (pick and place into grid functionality), so that it can help me begin with the development?

Thank you

Share this post


Link to post
Share on other sites



I don't think I've seen anything like that, but what you're wanting to do could be done in GDI+ pretty easily. I had most of these already done from past scripts so it was mostly just copied/pasted. Except for the table, I did spend a little extra time making the images look like a table just because I thought it looked pretty :)

It's up to you to create your work area and snap the images together.

#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinApi.au3>
#include <WindowsConstants.au3>
#include <Array.au3>

_GDIPlus_Startup()
Global Const $iGuiWidth = 808
Global Const $iGuiHeight = 600
Global Const $iTablePadding = 12
Global Const $hSepPen = _GDIPlus_PenCreate(0xFF000000, 4)
Global Const $hWorkBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
Global Const $hBackBrush = _GDIPlus_BrushCreateSolid(0x4F97FFFE)
Global $aGDIPBuffer[1][5] ; Buffer holds the information of the work area. [n][0] will be the image [n][1] is left position, [n][2] is top position, [n][3] is right position, [n][4] is bottom position of the image
Global $iImageWidth = 0
Global $iImageHeight = 0
Global $aGDIImageList = LoadGdiImageList($iImageWidth, $iImageHeight, $iGuiWidth, $iGuiHeight)
Global $frmMainGui = GUICreate("Puzzle", $iGuiWidth, $iGuiHeight)
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($frmMainGui)
Global $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iGuiWidth, $iGuiHeight, $hGraphic)
Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)

GUIRegisterMsg($WM_PAINT, WM_PAINT)
GUIRegisterMsg($WM_LBUTTONDOWN, WM_LBUTTONDOWN)

GUISetState(@SW_SHOW, $frmMainGui)

While (True)
    Switch (GUIGetMsg())
        Case $GUI_EVENT_CLOSE
            ; Always clena up your resources when you're done with GDI+
            DisposeGdiImageList($aGDIImageList)
            _GDIPlus_PenDispose($hSepPen)
            _GDIPlus_BrushDispose($hWorkBrush)
            _GDIPlus_BrushDispose($hBackBrush)
            _GDIPlus_Shutdown()

            GUIDelete($frmMainGui)
            Exit 0
    EndSwitch
WEnd


Func LoadGdiImageList(ByRef $iMaxWidth, ByRef $iMaxHeight, Const ByRef $iGuiWidth, $iGuiHeight)
    Local $aImageList[51]
    Local $iCurrentIndex = 0
    Local $iRows = 0
    Local $iColumns = 0

    For $i = 0 To UBound($aImageList) - 1
        $aImageList[$i] = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\" & $i & ".gif")
        $iImageWidth = Max(_GDIPlus_ImageGetWidth($aImageList[$i]), $iMaxWidth)
        $iImageHeight = Max(_GDIPlus_ImageGetHeight($aImageList[$i]), $iMaxHeight)
    Next

    GetTableDimensions($iRows, $iColumns, $iGuiWidth, $iGuiHeight, UBound($aImageList), $iImageWidth, $iImageHeight)

    Local $aImageListReturn[$iRows][$iColumns]

    For $iRow = 0 To UBound($aImageListReturn, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aImageListReturn, $UBOUND_COLUMNS) - 1
            $aImageListReturn[$iRow][$iColumn] = $aImageList[$iCurrentIndex]
            $iCurrentIndex += 1
            If ($iCurrentIndex >= UBound($aImageList)) Then
                For $i = $iColumn To $iColumns
                    $aImageListReturn[$iRow][$iColumn] = $aImageList[0]
                Next
                ExitLoop 2
            EndIf
        Next
    Next

    Return $aImageListReturn
EndFunc   ;==>LoadGdiImageList

Func DisposeGdiImageList(ByRef $aImageList)
    If (IsArray($aImageList)) Then
        For $iRow = 0 To UBound($aImageList, $UBOUND_ROWS) - 1
            For $iColumn = 0 to UBound($aImageList, $UBOUND_COLUMNS) - 1
                _GDIPlus_ImageDispose($aImageList[$iRow][$iColumn])
            Next
        Next
    Else
        Return SetError(-1, 0, 0)
    EndIf

    Return 1
EndFunc   ;==>DisposeGdiImageList

Func Max(Const ByRef $lhs, Const ByRef $rhs)
    Return ($lhs > $rhs ? $lhs : $rhs)
EndFunc   ;==>Max

Func GetTableDimensions(ByRef $iRows, ByRef $iColumns, $iMaxWidth, $iMaxHeight, $iCount, Const ByRef $iImageWidth, Const ByRef $iImageHeight)
    If ($iImageWidth = 0 Or $iImageHeight = 0 Or $iMaxWidth <= 20 Or $iMaxHeight <= 20) Then Return SetError(-1, 0, 0)

    ; Get the max columns. $iTablePadding for the left, right, and top of the table and $iTablePadding between each iamge
    $iColumns = Int(($iMaxWidth - ($iTablePadding * 2)) / ($iImageWidth + $iTablePadding))
    $iRows = Ceiling($iCount / $iColumns)

    Return 1
EndFunc   ;==>GetTableDimensions

Func DrawGdipArea(Const $bDrawAtEnd = True)
    _GDIPlus_GraphicsClear($hBuffer, 0xFFF0F0F0)

    For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
            Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
            Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
            Local $iRight = $iLeft + $iImageWidth
            Local $iBottom = $iTop + $iImageHeight

            ; Draw border around image, 4px offset
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iLeft - 4, $iBottom + 4) ; Left
            _GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 4, $iTop - 4, $iRight + 4, $iBottom + 4) ; Right
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iRight + 4, $iTop - 4) ; Top
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iBottom + 4, $iRight + 4, $iBottom + 4) ; Bottom
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iRow][$iColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
        Next
    Next

    ; Draw border around the whole table, 8px offset
    ; Just to make it look pretty :)
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iTablePadding - 8, $iBottom + 8) ; Left
    _GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 8, $iTablePadding - 8, $iRight + 8, $iBottom + 8) ; Right
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iRight + 8, $iTablePadding - 8) ; Top
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iBottom + 8, $iRight + 8, $iBottom + 8) ; Bottom

    _GDIPlus_GraphicsFillRect($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iGuiHeight - $iBottom, $hWorkBrush)

    ; There's an image at the top row
    If ($aGDIPBuffer[0][0]) Then
        For $iBuffer = 0 To UBound($aGDIPBuffer, $UBOUND_ROWS) - 1
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIPBuffer[$iBuffer][0], $aGDIPBuffer[$iBuffer][1], $aGDIPBuffer[$iBuffer][2], $iImageWidth, $iImageHeight)
        Next
    EndIf
    _GDIPlus_GraphicsDrawLine($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iBottom + 25, $hSepPen)

    If ($bDrawAtEnd) Then _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

    Return True
EndFunc   ;==>DrawGdipArea

Func WM_PAINT($hWndFrom, $iMsg, $wParam, $lParam)
    DrawGdipArea()

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_PAINT

Func WM_LBUTTONDOWN($hWndFrom, $iMsg, $wParam, $lParam)
    Local $iLButtonX = _WinAPI_LoWord($lParam)
    Local $iLButtonY = _WinAPI_HiWord($lParam)
    Local $iImageRow = -1
    Local $iImageColumn = -1
    Local $aCursorInfo = 0
    Local $iGdipBufferIndex = UBound($aGDIPBuffer, $UBOUND_ROWS)

    Switch ($hWndFrom)
        Case $frmMainGui
            For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
                For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
                    Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
                    Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
                    Local $iRight = $iLeft + $iImageWidth
                    Local $iBottom = $iTop + $iImageHeight

                    If (_WinAPI_PtInRectEx($iLButtonX, $iLButtonY, $iLeft, $iTop, $iRight, $iBottom)) Then
                        $iImageRow = $iRow
                        $iImageColumn = $iColumn
                        ExitLoop
                    EndIf
                Next
            Next

            If ($iImageRow > -1 And $iImageColumn > -1) Then
                $aCursorInfo = GUIGetCursorInfo($hWndFrom)

                If (@error) Then Return $GUI_RUNDEFMSG

                While ($aCursorInfo[2])
                    Local $iLeft = $aCursorInfo[0] - Int($iImageWidth / 2)
                    Local $iTop = $aCursorInfo[1] - Int($iImageHeight / 2)
                    DrawGdipArea(False)
                    _GDIPlus_GraphicsFillRect($hBuffer, $iLeft, $iTop, $iImageWidth, $iImageHeight, $hBackBrush)
                    _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iImageRow][$iImageColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
                    _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

                    $aCursorInfo = GUIGetCursorInfo($hWndFrom)
                    If (@error) Then Return $GUI_RUNDEFMSG
                WEnd

                $aGDIPBuffer[$iGdipBufferIndex - 1][0] = $aGDIImageList[$iImageRow][$iImageColumn]
                $aGDIPBuffer[$iGdipBufferIndex - 1][1] = $iLeft
                $aGDIPBuffer[$iGdipBufferIndex - 1][2] = $iTop
                $aGDIPBuffer[$iGdipBufferIndex - 1][3] = $iLeft + $iImageWidth
                $aGDIPBuffer[$iGdipBufferIndex - 1][4] = $iTop + $iImageHeight
                ReDim $aGDIPBuffer[$iGdipBufferIndex + 1][5]

                DrawGdipArea()
            EndIf
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_LBUTTONDOWN

Some notes:

Spoiler
Quote

You'll notice $aGDIPBuffer at the top of the script. I used something like this when I was trying to make a small image editor in GDI+. This holds the information of what to draw, where, and how tall in your work area. The [n][0] position holds the image to draw, [n][1] is the left position, [n][2] is the top, [n][3] is the right, and [n][4] is the bottom position of the image to draw. Having your  buffer like this will also make it easier to delete images from the work area.

In the WM_LBUTTONDOWN function I used _WinAPI_PtInRectEx to check what available images is being selected, based on the same algorithm I used to draw the images in the DrawGdipArea function. When you want to remove or move your images in the work area you can use the same thing since you stored all the points you need. You could do this with labels/pic control to figure out what image you're over but I used this way. Do whatever you prefer.

If you wanted to use some kind of button to change your available circuits, you'll have to either create a separate GUI to hold the buttons or create them in an area outside the $hBitmap area (Currently it's the whole GUI).

 

Anyways, it's a good base to get you started, since you probably don't know much, anything, about GDI+.

1 person likes this

Share this post


Link to post
Share on other sites
7 hours ago, InunoTaishou said:

I don't think I've seen anything like that, but what you're wanting to do could be done in GDI+ pretty easily. I had most of these already done from past scripts so it was mostly just copied/pasted. Except for the table, I did spend a little extra time making the images look like a table just because I thought it looked pretty :)

It's up to you to create your work area and snap the images together.

#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinApi.au3>
#include <WindowsConstants.au3>
#include <Array.au3>

_GDIPlus_Startup()
Global Const $iGuiWidth = 808
Global Const $iGuiHeight = 600
Global Const $iTablePadding = 12
Global Const $hSepPen = _GDIPlus_PenCreate(0xFF000000, 4)
Global Const $hWorkBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
Global Const $hBackBrush = _GDIPlus_BrushCreateSolid(0x4F97FFFE)
Global $aGDIPBuffer[1][5] ; Buffer holds the information of the work area. [n][0] will be the image [n][1] is left position, [n][2] is top position, [n][3] is right position, [n][4] is bottom position of the image
Global $iImageWidth = 0
Global $iImageHeight = 0
Global $aGDIImageList = LoadGdiImageList($iImageWidth, $iImageHeight, $iGuiWidth, $iGuiHeight)
Global $frmMainGui = GUICreate("Puzzle", $iGuiWidth, $iGuiHeight)
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($frmMainGui)
Global $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iGuiWidth, $iGuiHeight, $hGraphic)
Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)

GUIRegisterMsg($WM_PAINT, WM_PAINT)
GUIRegisterMsg($WM_LBUTTONDOWN, WM_LBUTTONDOWN)

GUISetState(@SW_SHOW, $frmMainGui)

While (True)
    Switch (GUIGetMsg())
        Case $GUI_EVENT_CLOSE
            ; Always clena up your resources when you're done with GDI+
            DisposeGdiImageList($aGDIImageList)
            _GDIPlus_PenDispose($hSepPen)
            _GDIPlus_BrushDispose($hWorkBrush)
            _GDIPlus_BrushDispose($hBackBrush)
            _GDIPlus_Shutdown()

            GUIDelete($frmMainGui)
            Exit 0
    EndSwitch
WEnd


Func LoadGdiImageList(ByRef $iMaxWidth, ByRef $iMaxHeight, Const ByRef $iGuiWidth, $iGuiHeight)
    Local $aImageList[51]
    Local $iCurrentIndex = 0
    Local $iRows = 0
    Local $iColumns = 0

    For $i = 0 To UBound($aImageList) - 1
        $aImageList[$i] = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\" & $i & ".gif")
        $iImageWidth = Max(_GDIPlus_ImageGetWidth($aImageList[$i]), $iMaxWidth)
        $iImageHeight = Max(_GDIPlus_ImageGetHeight($aImageList[$i]), $iMaxHeight)
    Next

    GetTableDimensions($iRows, $iColumns, $iGuiWidth, $iGuiHeight, UBound($aImageList), $iImageWidth, $iImageHeight)

    Local $aImageListReturn[$iRows][$iColumns]

    For $iRow = 0 To UBound($aImageListReturn, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aImageListReturn, $UBOUND_COLUMNS) - 1
            $aImageListReturn[$iRow][$iColumn] = $aImageList[$iCurrentIndex]
            $iCurrentIndex += 1
            If ($iCurrentIndex >= UBound($aImageList)) Then
                For $i = $iColumn To $iColumns
                    $aImageListReturn[$iRow][$iColumn] = $aImageList[0]
                Next
                ExitLoop 2
            EndIf
        Next
    Next

    Return $aImageListReturn
EndFunc   ;==>LoadGdiImageList

Func DisposeGdiImageList(ByRef $aImageList)
    If (IsArray($aImageList)) Then
        For $iRow = 0 To UBound($aImageList, $UBOUND_ROWS) - 1
            For $iColumn = 0 to UBound($aImageList, $UBOUND_COLUMNS) - 1
                _GDIPlus_ImageDispose($aImageList[$iRow][$iColumn])
            Next
        Next
    Else
        Return SetError(-1, 0, 0)
    EndIf

    Return 1
EndFunc   ;==>DisposeGdiImageList

Func Max(Const ByRef $lhs, Const ByRef $rhs)
    Return ($lhs > $rhs ? $lhs : $rhs)
EndFunc   ;==>Max

Func GetTableDimensions(ByRef $iRows, ByRef $iColumns, $iMaxWidth, $iMaxHeight, $iCount, Const ByRef $iImageWidth, Const ByRef $iImageHeight)
    If ($iImageWidth = 0 Or $iImageHeight = 0 Or $iMaxWidth <= 20 Or $iMaxHeight <= 20) Then Return SetError(-1, 0, 0)

    ; Get the max columns. $iTablePadding for the left, right, and top of the table and $iTablePadding between each iamge
    $iColumns = Int(($iMaxWidth - ($iTablePadding * 2)) / ($iImageWidth + $iTablePadding))
    $iRows = Ceiling($iCount / $iColumns)

    Return 1
EndFunc   ;==>GetTableDimensions

Func DrawGdipArea(Const $bDrawAtEnd = True)
    _GDIPlus_GraphicsClear($hBuffer, 0xFFF0F0F0)

    For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
            Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
            Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
            Local $iRight = $iLeft + $iImageWidth
            Local $iBottom = $iTop + $iImageHeight

            ; Draw border around image, 4px offset
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iLeft - 4, $iBottom + 4) ; Left
            _GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 4, $iTop - 4, $iRight + 4, $iBottom + 4) ; Right
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iRight + 4, $iTop - 4) ; Top
            _GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iBottom + 4, $iRight + 4, $iBottom + 4) ; Bottom
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iRow][$iColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
        Next
    Next

    ; Draw border around the whole table, 8px offset
    ; Just to make it look pretty :)
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iTablePadding - 8, $iBottom + 8) ; Left
    _GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 8, $iTablePadding - 8, $iRight + 8, $iBottom + 8) ; Right
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iRight + 8, $iTablePadding - 8) ; Top
    _GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iBottom + 8, $iRight + 8, $iBottom + 8) ; Bottom

    _GDIPlus_GraphicsFillRect($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iGuiHeight - $iBottom, $hWorkBrush)

    ; There's an image at the top row
    If ($aGDIPBuffer[0][0]) Then
        For $iBuffer = 0 To UBound($aGDIPBuffer, $UBOUND_ROWS) - 1
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIPBuffer[$iBuffer][0], $aGDIPBuffer[$iBuffer][1], $aGDIPBuffer[$iBuffer][2], $iImageWidth, $iImageHeight)
        Next
    EndIf
    _GDIPlus_GraphicsDrawLine($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iBottom + 25, $hSepPen)

    If ($bDrawAtEnd) Then _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

    Return True
EndFunc   ;==>DrawGdipArea

Func WM_PAINT($hWndFrom, $iMsg, $wParam, $lParam)
    DrawGdipArea()

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_PAINT

Func WM_LBUTTONDOWN($hWndFrom, $iMsg, $wParam, $lParam)
    Local $iLButtonX = _WinAPI_LoWord($lParam)
    Local $iLButtonY = _WinAPI_HiWord($lParam)
    Local $iImageRow = -1
    Local $iImageColumn = -1
    Local $aCursorInfo = 0
    Local $iGdipBufferIndex = UBound($aGDIPBuffer, $UBOUND_ROWS)

    Switch ($hWndFrom)
        Case $frmMainGui
            For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
                For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
                    Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
                    Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
                    Local $iRight = $iLeft + $iImageWidth
                    Local $iBottom = $iTop + $iImageHeight

                    If (_WinAPI_PtInRectEx($iLButtonX, $iLButtonY, $iLeft, $iTop, $iRight, $iBottom)) Then
                        $iImageRow = $iRow
                        $iImageColumn = $iColumn
                        ExitLoop
                    EndIf
                Next
            Next

            If ($iImageRow > -1 And $iImageColumn > -1) Then
                $aCursorInfo = GUIGetCursorInfo($hWndFrom)

                If (@error) Then Return $GUI_RUNDEFMSG

                While ($aCursorInfo[2])
                    Local $iLeft = $aCursorInfo[0] - Int($iImageWidth / 2)
                    Local $iTop = $aCursorInfo[1] - Int($iImageHeight / 2)
                    DrawGdipArea(False)
                    _GDIPlus_GraphicsFillRect($hBuffer, $iLeft, $iTop, $iImageWidth, $iImageHeight, $hBackBrush)
                    _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iImageRow][$iImageColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
                    _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

                    $aCursorInfo = GUIGetCursorInfo($hWndFrom)
                    If (@error) Then Return $GUI_RUNDEFMSG
                WEnd

                $aGDIPBuffer[$iGdipBufferIndex - 1][0] = $aGDIImageList[$iImageRow][$iImageColumn]
                $aGDIPBuffer[$iGdipBufferIndex - 1][1] = $iLeft
                $aGDIPBuffer[$iGdipBufferIndex - 1][2] = $iTop
                $aGDIPBuffer[$iGdipBufferIndex - 1][3] = $iLeft + $iImageWidth
                $aGDIPBuffer[$iGdipBufferIndex - 1][4] = $iTop + $iImageHeight
                ReDim $aGDIPBuffer[$iGdipBufferIndex + 1][5]

                DrawGdipArea()
            EndIf
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_LBUTTONDOWN

Some notes:

  Reveal hidden contents

 

Anyways, it's a good base to get you started, since you probably don't know much, anything, about GDI+.

There must be an error somewhere, or something is happening with the includes.

C:\Users\Desktop\schematic.au3 (23) : ==> Missing separator character after keyword.:
GUIRegisterMsg($WM_PAINT, WM_PAINT)
GUIRegisterMsg($WM_PAINT, WM_PAINT^ ERROR

Share this post


Link to post
Share on other sites

Did you modify it?

What version of AutoIt do you have?

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

16 minutes ago, InunoTaishou said:

Did you modify it?

What version of AutoIt do you have?

I did not modify it.

I just installed the newest version of autoit just to make sure.

Now the error is:

"C:\Users\Desktop\Schematix.au3" (125) : ==> Variable used without being declared.:
_GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iTablePadding - 8, $iBottom + 8)
_GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iTablePadding - 8, ^ ERROR

Edited by neazoi

Share this post


Link to post
Share on other sites

You need to load the images properly. I named them all 0-49

Use this Example.rar

Example.rar

1 person likes this

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Ok I declared the

$iLeft
$iTop
$iRight
$iBottom

at the beginning and it worked.

Thanks so much for the script!

thumbs up!!

How would you do it if you wanted the pieces to be dropped into predefined positions, like in the javascript program I sent you?
 That way, one does not need to accurately place one image next to the other.

PS. I wonder, why it did not work in the previous version...

Edited by neazoi

Share this post


Link to post
Share on other sites

The reason that error occurred is because they're declared inside the for loops and will only get declared when you actually have the images loaded (or image, it only needs 1). If the images for the grid are not loaded then it never goes into the double for loop and never declares the variables needed to draw the border around the images properly.

1 person likes this

Share this post


Link to post
Share on other sites
45 minutes ago, InunoTaishou said:

The reason that error occurred is because they're declared inside the for loops and will only get declared when you actually have the images loaded (or image, it only needs 1). If the images for the grid are not loaded then it never goes into the double for loop and never declares the variables needed to draw the border around the images properly.

Yes I have noticed that, thanks a lot.

How would you do it if you wanted the pieces to be dropped into predefined positions, like in the javascript program I sent you?
That way, one does not need to accurately place one image next to the other. and the pieces are pasted always in predefined positions onto a grid.

Any ideas?

Share this post


Link to post
Share on other sites

Well I see that the first three rows are always the same. You could load the images into a const array of the default images

So modify the LoadGdiImageList function a bit

Global $aGDIImageList = LoadGdiImageListFromFolder(@ScriptDir & "\Images\Default\", $iImageWidth, $iImageHeight, $iGuiWidth, $iGuiHeight)

Func LoadGdiImageListFromFolder(Const ByRef $sFilePath, ByRef $iMaxWidth, ByRef $iMaxHeight, Const ByRef $iGuiWidth, $iGuiHeight)
    Local $aImageList[0]
    Local $iCurrentIndex = 0
    Local $iRows = 0
    Local $iColumns = 0
    Local $aImages = _FileListToArray($sFilePath, "*.gif", $FLTA_FILES)

    If (@error) Then Return SetError(-1, 0, $aImageList)

    _ArrayDelete($aImages, 0)

    For $i = 0 To UBound($aImages) - 1
        For $p = $i To UBound($aImages) - 1
            If (Number(StringLeft($aImages[$i], StringInStr($aImages[$i], ".") - 1)) > Number(StringLeft($aImages[$p], StringInStr($aImages[$p], ".") - 1))) Then
                _ArraySwap($aImages, $i, $p)
            EndIf
        Next
    Next

    For $i = 0 To UBound($aImages) - 1
        Local $iIndex = UBound($aImageList)
        ReDim $aImageList[$iIndex + 1]
        $aImageList[$iIndex] = _GDIPlus_ImageLoadFromFile($sFilePath & $aImages[$i])
        $iImageWidth = Max(_GDIPlus_ImageGetWidth($aImageList[$iIndex]), $iMaxWidth)
        $iImageHeight = Max(_GDIPlus_ImageGetHeight($aImageList[$iIndex]), $iMaxHeight)
    Next

    GetTableDimensions($iRows, $iColumns, $iGuiWidth, $iGuiHeight, UBound($aImageList), $iImageWidth, $iImageHeight)

    Local $aImageListReturn[$iRows][$iColumns]

    For $iRow = 0 To UBound($aImageListReturn, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aImageListReturn, $UBOUND_COLUMNS) - 1
            $aImageListReturn[$iRow][$iColumn] = $aImageList[$iCurrentIndex]
            $iCurrentIndex += 1
            If ($iCurrentIndex >= UBound($aImageList)) Then
                For $i = $iColumn To $iColumns
                    $aImageListReturn[$iRow][$iColumn] = $aImageList[0]
                Next
                ExitLoop 2
            EndIf
        Next
    Next

    Return $aImageListReturn
EndFunc   ;==>LoadGdiImageListFromFolder

Set all the default images in a folder called default. These are the first 3 rows that did not change. Now when you want to change out the last row you call the same function but with the directory you want and store the result in a temporary array (it's still a 2d array but it should have 1 row and 15 columns). Free the images in the $aGDIImageList that you're replacing (you can make the function however you want to do that, or modify the DisposeGdiImageList to something like DisposeGdiImageList(ByRef $aImageList, $iRowStart, $iRowEnd, $iColumnStart, $iColumnEnd). Finally, copy over the loaded images from the temporary array to the $aGDIImageList array.

You'll have to think about if there is an image in the work area that's no longer in your table. Once you dispose the image in GDI+ it won't be able to draw it anymore. Maybe instead of working with one GDIImageList array you could work with 1 default one that's always drawn and another 27 for the other components, or a 2d GDIImageList array. Having the default images in the 0 index. Then, depending on which component to see, you draw all the images at the n index. That would solve the issue of drawing an image that was replaced by some other image, since we're not storing the index for the GDIImageList array where the image is but the address to the image in the Buffer. If that makes sense.

I'd personally just load all of the possible images and just use a button to scroll left/right or up/down through the images, or buttons that will scroll to where the specific components are. It's up to you, it's your project, I can get you started but I won't do it all for you.

1 person likes this

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

19 hours ago, InunoTaishou said:

Well I see that the first three rows are always the same. You could load the images into a const array of the default images

So modify the LoadGdiImageList function a bit

Global $aGDIImageList = LoadGdiImageListFromFolder(@ScriptDir & "\Images\Default\", $iImageWidth, $iImageHeight, $iGuiWidth, $iGuiHeight)

Func LoadGdiImageListFromFolder(Const ByRef $sFilePath, ByRef $iMaxWidth, ByRef $iMaxHeight, Const ByRef $iGuiWidth, $iGuiHeight)
    Local $aImageList[0]
    Local $iCurrentIndex = 0
    Local $iRows = 0
    Local $iColumns = 0
    Local $aImages = _FileListToArray($sFilePath, "*.gif", $FLTA_FILES)

    If (@error) Then Return SetError(-1, 0, $aImageList)

    _ArrayDelete($aImages, 0)

    For $i = 0 To UBound($aImages) - 1
        For $p = $i To UBound($aImages) - 1
            If (Number(StringLeft($aImages[$i], StringInStr($aImages[$i], ".") - 1)) > Number(StringLeft($aImages[$p], StringInStr($aImages[$p], ".") - 1))) Then
                _ArraySwap($aImages, $i, $p)
            EndIf
        Next
    Next

    For $i = 0 To UBound($aImages) - 1
        Local $iIndex = UBound($aImageList)
        ReDim $aImageList[$iIndex + 1]
        $aImageList[$iIndex] = _GDIPlus_ImageLoadFromFile($sFilePath & $aImages[$i])
        $iImageWidth = Max(_GDIPlus_ImageGetWidth($aImageList[$iIndex]), $iMaxWidth)
        $iImageHeight = Max(_GDIPlus_ImageGetHeight($aImageList[$iIndex]), $iMaxHeight)
    Next

    GetTableDimensions($iRows, $iColumns, $iGuiWidth, $iGuiHeight, UBound($aImageList), $iImageWidth, $iImageHeight)

    Local $aImageListReturn[$iRows][$iColumns]

    For $iRow = 0 To UBound($aImageListReturn, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aImageListReturn, $UBOUND_COLUMNS) - 1
            $aImageListReturn[$iRow][$iColumn] = $aImageList[$iCurrentIndex]
            $iCurrentIndex += 1
            If ($iCurrentIndex >= UBound($aImageList)) Then
                For $i = $iColumn To $iColumns
                    $aImageListReturn[$iRow][$iColumn] = $aImageList[0]
                Next
                ExitLoop 2
            EndIf
        Next
    Next

    Return $aImageListReturn
EndFunc   ;==>LoadGdiImageListFromFolder

Set all the default images in a folder called default. These are the first 3 rows that did not change. Now when you want to change out the last row you call the same function but with the directory you want and store the result in a temporary array (it's still a 2d array but it should have 1 row and 15 columns). Free the images in the $aGDIImageList that you're replacing (you can make the function however you want to do that, or modify the DisposeGdiImageList to something like DisposeGdiImageList(ByRef $aImageList, $iRowStart, $iRowEnd, $iColumnStart, $iColumnEnd). Finally, copy over the loaded images from the temporary array to the $aGDIImageList array.

You'll have to think about if there is an image in the work area that's no longer in your table. Once you dispose the image in GDI+ it won't be able to draw it anymore. Maybe instead of working with one GDIImageList array you could work with 1 default one that's always drawn and another 27 for the other components, or a 2d GDIImageList array. Having the default images in the 0 index. Then, depending on which component to see, you draw all the images at the n index. That would solve the issue of drawing an image that was replaced by some other image, since we're not storing the index for the GDIImageList array where the image is but the address to the image in the Buffer. If that makes sense.

I'd personally just load all of the possible images and just use a button to scroll left/right or up/down through the images, or buttons that will scroll to where the specific components are. It's up to you, it's your project, I can get you started but I won't do it all for you.

 

Thank you very much for the code snippet, It has been very helpful!

To draw electronics schematics one needs to put labels for the components, near them. I do not want to convert the text into image and place the image to a position, but instead I was wondering if this drag and drop for images you have done, can be done for text as well?

What I need is to type the text into a form box and then to be able to place this text (drag and drop? or click?) to a specific area of the schematic (puzzle). The text positions should be held in the $aGDIPBuffer, similar to the images, because I want to export/import it's data later on.

Would that it be easy to be done somehow?

Edited by neazoi

Share this post


Link to post
Share on other sites

If you're wanting to draw a string Then you can use _GDIPlus_GraphicsDrawString or _GDIPlus_GraphicsDrawStringEx

_GDIPlus_GraphicsDrawStringEx will draw a string to fit in a defined rectangle and only draw what will fit. I.e., if you just want the string to fit in a label that's 100px wide and 20px tall.

_GDIPlus_GraphicsDrawString will draw the string until all of the letters are drawn.

Here's a small snippit to get you going. I just used DrawString but you can use whichever you prefer. Also, I created my graphic a bit different in this example than the other. I created it using an HDC based off of a label I created. This lets me place all of my "stuff" only where the label is, instead of the whole GUI. I need to use this instead of CreateFromHwnd because of the input box. Since FromHwnd would have created a graphic object the size of the whole gui, whenever you draw any images it's going to cover up the input box.

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>
#include <WinAPI.au3>

_GDIPlus_Startup()
; Width and Height of my work area
Global const $iWidth = 380
Global Const $iHeight = 260
Global $hGUI = GUICreate("GDI+", 400, 300)
Global $hInput = GUICtrlCreateInput("Custom String", 10, 10, 100, 20)
Global $lblDc = GUICtrlCreateLabel("", 10, 30, $iWidth, $iHeight)
Global $hDc = _WinAPI_GetWindowDC(GUICtrlGetHandle($lblDc))
Global $hGraphic = _GDIPlus_GraphicsCreateFromHDC($hDc)
Global $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphic)
Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
Global $hBitmapLabel = 0

GUIRegisterMsg($WM_COMMAND, WM_COMMAND)

GUISetState(@SW_SHOW)

DrawCustomLabel(GUICtrlRead($hInput))

While (True)
    Local $aCursorInfo
    Switch (GUIGetMsg())
        Case $GUI_EVENT_CLOSE
            _GDIPlus_GraphicsDispose($hGraphic)
            _GDIPlus_Shutdown()
            GUIDelete($hGUI)
            Exit 0
        Case $lblDc
            $aCursorInfo = GUIGetCursorInfo()
            If (Not @Error) Then
                If (_WinAPI_PtInRectEx($aCursorInfo[0], $aCursorInfo[1], 10, 30, 110, 130)) Then
                    $hBitmapLabel = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, 101, 21)
                    While ($aCursorInfo[2])
                        MoveLabel($aCursorInfo[0], $aCursorInfo[1], GUICtrlRead($hInput))
                        Sleep(100)
                        $aCursorInfo = GUIGetCursorInfo()
                    WEnd
                EndIf
            EndIf
    EndSwitch
WEnd

Func MoveLabel(Const ByRef $iNewX, Const ByRef $iNewY, Const ByRef $sText)
    DrawCustomLabel($sText)
    ; Draw it 10px to the left and 30px up because my work area is based off the position of the $lblDc label. Which is 10px right and 30px down.
    _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmapLabel, $iNewX - 10, $iNewY - 30)
EndFunc

Func DrawCustomLabel(Const ByRef $sText)
    _GDIPlus_GraphicsClear($hBuffer, 0xFFF0F0F0)
    _GDIPlus_GraphicsDrawString($hBuffer, $sText, 0, 5)
    _GDIPlus_GraphicsDrawRect($hBuffer, 0, 0, 100, 20)
    _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)
EndFunc

Func WM_COMMAND($hWndFrom, $iMsg, $wParam, $lParam)
    Local $iCode = _WinAPI_HiWord($wParam)
    Local $iIdFrom = _WinAPI_LoWord($wParam)

    Switch ($hWndFrom)
        Case $hGUI
            Switch ($iIdFrom)
                Case $hInput
                    Switch ($iCode)
                        Case $EN_CHANGE
                            DrawCustomLabel(GUICtrlRead($hInput))
                    EndSwitch
            EndSwitch
    EndSwitch
    
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

I'm rushed for time but if you have questions post and I or someone else will answer.

1 person likes this

Share this post


Link to post
Share on other sites

Just to add on to that. I used

_GDIPlus_BitmapCloneArea($hBitmap, 0, 0, 101, 21)

Because we've already drawn the string (and I drew it with a border, 101, 21). So I just copied the drawn area. You can just redraw the string each time if you want.

1 person likes this

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

22 hours ago, InunoTaishou said:

Just to add on to that. I used

_GDIPlus_BitmapCloneArea($hBitmap, 0, 0, 101, 21)

Because we've already drawn the string (and I drew it with a border, 101, 21). So I just copied the drawn area. You can just redraw the string each time if you want.

I saw your example and this is exactly what needed. However due to my lack of good knowledge obviously, I am confused in embedding it into my (yours) application.

The code I have is this (minor graphics mods to yours). I just need the functuonality you provided but each label must be a new copy to the area, so that the previous label is not overwritten or deleted, each time I put a new label.

The position and the text label should be kept into the $aGDIPBuffer, like it is done for the images (if I understand this correctly), so that I can export it later on. Instead of the image name, the label text could be kept.

It would be great if you could help me embed it to this example.

Thanks so much for your time!

;You'll notice $aGDIPBuffer at the top of the script. I used something like this when I was trying to make a small image editor in GDI+.
;This holds the information of what to draw, where, and how tall in your work area. The [n][0] position holds the image to draw, [n][1] is the left position,
;[n][2] is the top, [n][3] is the right, and [n][4] is the bottom position of the image to draw. Having your  buffer like this will also make it easier
;to delete images from the work area.
;In the WM_LBUTTONDOWN function I used _WinAPI_PtInRectEx to check what available images is being selected, based on the same algorithm I used to draw
;the images in the DrawGdipArea function. When you want to remove or move your images in the work area you can use the same thing since you stored all
;the points you need. You could do this with labels/pic control to figure out what image you're over but I used this way. Do whatever you prefer.
;If you wanted to use some kind of button to change your available circuits, you'll have to either create a separate GUI to hold the buttons or
;create them in an area outside the $hBitmap area (Currently it's the whole GUI).


#include <GUIConstants.au3>
#include <GUIConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinApi.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>
#include <Array.au3>

_GDIPlus_Startup()
Global Const $iGuiWidth = 1501  ;Window width
Global Const $iGuiHeight = 1010 ;Window height
Global Const $iTablePadding = 8 ;padding of components cells
Global Const $hSepPen = _GDIPlus_PenCreate(0xFF000000, 2)   ;bgcolor and size of the separator line
Global Const $hWorkBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)    ;bgcolor of the schematic area
Global Const $hBackBrush = _GDIPlus_BrushCreateSolid(0x4FFFFFFF)    ;bgcolor of the dragged image
Global $aGDIPBuffer[1][5]  ;Buffer holds the information of the work area.
                           ;[n][0] will be the image,
                           ;[n][1] is left position,
                           ;[n][2] is top position,
                           ;[n][3] is right position,
                           ;[n][4] is bottom position of the image
Global $iImageWidth = 0
Global $iImageHeight = 0
Global $aGDIImageList = LoadGdiImageList($iImageWidth, $iImageHeight, $iGuiWidth, $iGuiHeight)
Global $frmMainGui = GUICreate("Schematix v0.1 by sv3ora", $iGuiWidth, $iGuiHeight)
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($frmMainGui)
Global $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iGuiWidth, $iGuiHeight, $hGraphic)
Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)

GUIRegisterMsg($WM_PAINT, WM_PAINT)
GUIRegisterMsg($WM_LBUTTONDOWN, WM_LBUTTONDOWN)

GUISetState(@SW_SHOW, $frmMainGui)
GUISetCursor("5")   ;set the window cursor to a predefined image (0-16)

While (True)
    Switch (GUIGetMsg())
        Case $GUI_EVENT_CLOSE
            ; Always clean up your resources when you're done with GDI+
            DisposeGdiImageList($aGDIImageList)
            _GDIPlus_PenDispose($hSepPen)
            _GDIPlus_BrushDispose($hWorkBrush)
            _GDIPlus_BrushDispose($hBackBrush)
            _GDIPlus_Shutdown()

            GUIDelete($frmMainGui)
            Exit 0
    EndSwitch
WEnd



Func LoadGdiImageList(ByRef $iMaxWidth, ByRef $iMaxHeight, Const ByRef $iGuiWidth, $iGuiHeight)
    Local $aImageList[330]  ;how many images to be displayed
    Local $iCurrentIndex = 0
    Local $iRows = 0
    Local $iColumns = 0

    For $i = 0 To UBound($aImageList) - 1
        $aImageList[$i] = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Imagestodelete\" & $i & ".gif")
        $iImageWidth = Max(_GDIPlus_ImageGetWidth($aImageList[$i]), $iMaxWidth)
        $iImageHeight = Max(_GDIPlus_ImageGetHeight($aImageList[$i]), $iMaxHeight)
    Next

    GetTableDimensions($iRows, $iColumns, $iGuiWidth, $iGuiHeight, UBound($aImageList), $iImageWidth, $iImageHeight)

    Local $aImageListReturn[$iRows][$iColumns]

    For $iRow = 0 To UBound($aImageListReturn, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aImageListReturn, $UBOUND_COLUMNS) - 1
            $aImageListReturn[$iRow][$iColumn] = $aImageList[$iCurrentIndex]
            $iCurrentIndex += 1
            If ($iCurrentIndex >= UBound($aImageList)) Then
                For $i = $iColumn To $iColumns
                    $aImageListReturn[$iRow][$iColumn] = $aImageList[0]
                Next
                ExitLoop 2
            EndIf
        Next
    Next

    Return $aImageListReturn
EndFunc   ;==>LoadGdiImageList




Func DisposeGdiImageList(ByRef $aImageList)
    If (IsArray($aImageList)) Then
        For $iRow = 0 To UBound($aImageList, $UBOUND_ROWS) - 1
            For $iColumn = 0 to UBound($aImageList, $UBOUND_COLUMNS) - 1
                _GDIPlus_ImageDispose($aImageList[$iRow][$iColumn])
            Next
        Next
    Else
        Return SetError(-1, 0, 0)
    EndIf

    Return 1
EndFunc   ;==>DisposeGdiImageList




Func Max(Const ByRef $lhs, Const ByRef $rhs)
    Return ($lhs > $rhs ? $lhs : $rhs)
EndFunc   ;==>Max




Func GetTableDimensions(ByRef $iRows, ByRef $iColumns, $iMaxWidth, $iMaxHeight, $iCount, Const ByRef $iImageWidth, Const ByRef $iImageHeight)
    If ($iImageWidth = 0 Or $iImageHeight = 0 Or $iMaxWidth <= 20 Or $iMaxHeight <= 20) Then Return SetError(-1, 0, 0)

    ; Get the max columns. $iTablePadding for the left, right, and top of the table and $iTablePadding between each iamge
    $iColumns = Int(($iMaxWidth - ($iTablePadding * 2)) / ($iImageWidth + $iTablePadding))
    $iRows = Ceiling($iCount / $iColumns)

    Return 1
EndFunc   ;==>GetTableDimensions




Func DrawGdipArea(Const $bDrawAtEnd = True)
    _GDIPlus_GraphicsClear($hBuffer, 0xFFE1E1E1)    ;bgcolor of the components area

    For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
        For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
            Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
            Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
            Local $iRight = $iLeft + $iImageWidth
            Local $iBottom = $iTop + $iImageHeight

            ; Draw border around image, 4px offset
            ;_GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iLeft - 4, $iBottom + 4) ; Left
            ;_GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 4, $iTop - 4, $iRight + 4, $iBottom + 4) ; Right
            ;_GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iTop - 4, $iRight + 4, $iTop - 4) ; Top
            ;_GDIPlus_GraphicsDrawLine($hBuffer, $iLeft - 4, $iBottom + 4, $iRight + 4, $iBottom + 4) ; Bottom
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iRow][$iColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
        Next
    Next

    ; Draw border around the whole table, 8px offset
    ; Just to make it look pretty :)
    ;_GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iTablePadding - 8, $iBottom + 8) ; Left
    ;_GDIPlus_GraphicsDrawLine($hBuffer, $iRight + 8, $iTablePadding - 8, $iRight + 8, $iBottom + 8) ; Right
    ;_GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iTablePadding - 8, $iRight + 8, $iTablePadding - 8) ; Top
    ;_GDIPlus_GraphicsDrawLine($hBuffer, $iTablePadding - 8, $iBottom + 8, $iRight + 8, $iBottom + 8) ; Bottom

    _GDIPlus_GraphicsFillRect($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iGuiHeight - $iBottom, $hWorkBrush)

    ; There's an image at the top row
    If ($aGDIPBuffer[0][0]) Then
        For $iBuffer = 0 To UBound($aGDIPBuffer, $UBOUND_ROWS) - 1
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIPBuffer[$iBuffer][0], $aGDIPBuffer[$iBuffer][1], $aGDIPBuffer[$iBuffer][2], $iImageWidth, $iImageHeight)
        Next
    EndIf
    _GDIPlus_GraphicsDrawLine($hBuffer, 0, $iBottom + 25, $iGuiWidth, $iBottom + 25, $hSepPen)

    If ($bDrawAtEnd) Then _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

    Return True
EndFunc   ;==>DrawGdipArea




Func WM_PAINT($hWndFrom, $iMsg, $wParam, $lParam)
    DrawGdipArea()

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_PAINT




Func WM_LBUTTONDOWN($hWndFrom, $iMsg, $wParam, $lParam)
    Local $iLButtonX = _WinAPI_LoWord($lParam)
    Local $iLButtonY = _WinAPI_HiWord($lParam)
    Local $iImageRow = -1
    Local $iImageColumn = -1
    Local $aCursorInfo = 0
    Local $iGdipBufferIndex = UBound($aGDIPBuffer, $UBOUND_ROWS)

    Switch ($hWndFrom)
        Case $frmMainGui
            For $iRow = 0 To UBound($aGDIImageList, $UBOUND_ROWS) - 1
                For $iColumn = 0 To UBound($aGDIImageList, $UBOUND_COLUMNS) - 1
                    Local $iLeft = $iTablePadding + ($iTablePadding * $iColumn) + ($iColumn * $iImageWidth)
                    Local $iTop = $iTablePadding + ($iTablePadding * $iRow) + ($iRow * $iImageHeight)
                    Local $iRight = $iLeft + $iImageWidth
                    Local $iBottom = $iTop + $iImageHeight

                    If (_WinAPI_PtInRectEx($iLButtonX, $iLButtonY, $iLeft, $iTop, $iRight, $iBottom)) Then
                        $iImageRow = $iRow
                        $iImageColumn = $iColumn
                        ExitLoop
                    EndIf
                Next
            Next

            If ($iImageRow > -1 And $iImageColumn > -1) Then
                $aCursorInfo = GUIGetCursorInfo($hWndFrom)

                If (@error) Then Return $GUI_RUNDEFMSG

                While ($aCursorInfo[2])
                    Local $iLeft = $aCursorInfo[0] - Int($iImageWidth / 2)
                    Local $iTop = $aCursorInfo[1] - Int($iImageHeight / 2)
                    DrawGdipArea(False)
                    _GDIPlus_GraphicsFillRect($hBuffer, $iLeft, $iTop, $iImageWidth, $iImageHeight, $hBackBrush)
                    _GDIPlus_GraphicsDrawImageRect($hBuffer, $aGDIImageList[$iImageRow][$iImageColumn], $iLeft, $iTop, $iImageWidth, $iImageHeight)
                    _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

                    $aCursorInfo = GUIGetCursorInfo($hWndFrom)
                    If (@error) Then Return $GUI_RUNDEFMSG
                WEnd

                $aGDIPBuffer[$iGdipBufferIndex - 1][0] = $aGDIImageList[$iImageRow][$iImageColumn]
                $aGDIPBuffer[$iGdipBufferIndex - 1][1] = $iLeft
                $aGDIPBuffer[$iGdipBufferIndex - 1][2] = $iTop
                $aGDIPBuffer[$iGdipBufferIndex - 1][3] = $iLeft + $iImageWidth
                $aGDIPBuffer[$iGdipBufferIndex - 1][4] = $iTop + $iImageHeight
                ReDim $aGDIPBuffer[$iGdipBufferIndex + 1][5]

                DrawGdipArea()
            EndIf
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_LBUTTONDOWN

 

Edited by neazoi

Share this post


Link to post
Share on other sites

Alright, I modified my example to show you how to draw your display label and store the label in the buffer.

I didn't like having the background color of 0xF0F0F0 (the default background color for all windows forms) so I decided to draw the string and then clone where the label ends up (after you let go of the mouse button). This gives you a background of wherever the label is placed. I believe I commented everything properly and explained it well. Adding #Region tags where I changed code and where I added new code.

When you get the point where you want to move already placed images that are stored in your buffer you'll have to add an Else statement for the WM_LBUTTONDOWN function.

If ($iImageRow > -1 And $iImageColumn > -1) Then
    ; Code for this section
ElseIf (_WinAPI_PtInRectEx($iLButtonX, $iLButtonY, $tRectLabel.x, $tRectLabel.y, $tRectLabel.x + $iGuiWidthInput, $tRectLabel.y + $iGuiHeightInput)) Then
    ; Code for this section
Else
    ; New else
EndIf

In the Else you'll then go through the GDIBuffer checking the [1], [2], [1] + [3], [2] + [4] (x, y, right, bottom). Then it's up to you to figure out how to move it on your work area ;)

Example.rar

Share this post


Link to post
Share on other sites

This is some cool stuff you two are doing here - I'm doing some things with stereo speaker crossover designs right now and I like this. I've tried some of the free schematics software on the net but I've found most of it overkill to the point where it is useless to me. I really love the straight-forward simplicity of this - I'll keep watching!

Share this post


Link to post
Share on other sites
1 hour ago, JAPP said:

I really love the straight-forward simplicity of this

I concur ... and am following this, also.  But (if it's not too much trouble) would someone mind posting a screen capture that will make the points easier to understand for me and others?

Thanks.

Share this post


Link to post
Share on other sites

GDI+ can get pretty complex but everything here is pretty simple so far. Some of the more complex stuff might just be some of the GDI+ function calls.

First off, $aGDIPBuffer is just a workplace buffer. In simplistic terms it has the image to draw ([row][0]), where to draw it ([row][1] and [row][2] and the dimensions ([row][3] and [row][4]. aka. width, height)

Next is Global $aGDIImageList = LoadGdiImageListFromFolder. The function (LoadGdiImageListFromFolder, in my example) just looks for all the .gif files in the specified folder. On the first for loop, in the function, (For $i = 0 and For $p = 0) all I'm doing is sorting the images logically. Since the images are just 0-49 (in my folder) and the _FileListToArray function doesn't get files alphabetically/numerically, I sorted them numerically. The second For loop loads all of the images into the $aImageList array (just a 1d array right now since I'll be arranging them a row, column array next) and saves the width and height of the widest and tallest image (all of his images from the website were 37x37px, but there could be an instance where one is 35x37. That 2px difference might not sound like much but when all the images are lined up in a grid, you might be able to see the difference). And finally, the last for loop. This is the real array. This is going to sort all of the images and store them in 2d array ([row][column]) so that it's displayed in the table on the gui. Where the images are saved in this array determines where they show up on the table. I.e., if image 37 is in row 1 column 6 in this array, it's in row 1 column 6 on the table.

Everything else up to the GUIRegisterMsg stuff is just creating the work area of the GUI, creating the GUI, creating an input box (for the custom label), and creating the necessary objects for drawing a string (the label). Which you can read about from the help file.

I register the WM_PAINT message because when a window is minimized then the GDI+ graphic object is erased. In fact, even on a regular GUI with regular controls, the controls are erased. But when you restore your GUI then windows tells that GUI you need to redraw your stuff. Default controls windows will handle, but GDI+ needs to be redrawn by the user.

The WM_LBUTTONDOWN is for clicking on one of the images (more on this later) and WM_COMMAND is just to detect when the user is typing in the input box (so it updates the available label directly next to the input box. When you type in the box, it's redrawing the available label with your text)

The DrawGdipArea function is where we draw everything for our graphic. There are much simpler examples in the help file and on the forums for drawing GDI+ so I'd suggest starting there first. My first for loop is the table at the very top of our GUI. I draw 4 lines around where the image is going to be (I'm drawing a border around where the image will be so the image looks like it's in a box, so it looks like a table). Then I draw the image, and I'm drawing it using DrawImageRect so that all of the images are the same width and height. The $bMoveOnce is because I didn't know how tall the table would be and I (personally) wanted the input box for the label to be directly below the table. You could draw the table lower and just put the input box at the top and not have to worry about it here in the loop. I then draw the string from the input box (using DrawStringEx so that the string fits into the defined area. If we just use DrawString and the input box has 100 characters then all 100 characters are going to be drawn on one line all the way to the end until everything is drawn or there's no more room on the GUI). Next is the border around the table, just to make it look pretty (I was just trying to mimic what his website looked like). And finally the buffer, going through each row of the buffer drawing the specified image at the correct position on the GUI.

The WM_LBUTTONDOWN function might look intimidating but, basically, all it's doing is checking to see if the mouse is clicking on one of the images in the table. If it is then it will DrawGdipArea and then draw the image that you're clicking on. If you're left clicking an image from the table then it's going to draw that image where you move your mouse, if you're clicking on the custom label then it's going to draw that label where your mouse is. Finally, when you release the left button it's going to save the image, or the label, in the buffer and save where to draw it.

TLDR; Creating a 2d array with the table images, detect if you click on an image or the label, if you're clicking an image or a label then draw that image or label where the mouse is, and save the image or label in your buffer.

Like I said, this is actually pretty simple. GDI+ can get pretty complex and do so much more. I actually really liked this post and it gave me an idea. I'm making a puzzle game using wallpapers.

Share this post


Link to post
Share on other sites

InunoTaishou,

Thanks for the explanation. I've been working/playing with AutoIt for several years now, but I've never touched GDI+. This will help me a lot.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0