Share this post

Link to post

Share on other sites

InunoTaishou 178

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

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

neazoi 0

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

Share on other sites

neazoi 0

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 March 16, 2016 by neazoi

Share this post

Link to post

Share on other sites

InunoTaishou 178

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

neazoi 0

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.

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.

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.

Share this post

Link to post

Share on other sites

InunoTaishou 178

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.

Link to post

Share on other sites

neazoi 0

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()GlobalConst$iGuiWidth=1501;Window widthGlobalConst$iGuiHeight=1010;Window heightGlobalConst$iTablePadding=8;padding of components cellsGlobalConst$hSepPen=_GDIPlus_PenCreate(0xFF000000,2);bgcolor and size of the separator lineGlobalConst$hWorkBrush=_GDIPlus_BrushCreateSolid(0xFFFFFFFF);bgcolor of the schematic areaGlobalConst$hBackBrush=_GDIPlus_BrushCreateSolid(0x4FFFFFFF);bgcolor of the dragged imageGlobal$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 imageGlobal$iImageWidth=0Global$iImageHeight=0Global$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)Exit0EndSwitchWEndFunc LoadGdiImageList(ByRef$iMaxWidth,ByRef$iMaxHeight,ConstByRef$iGuiWidth,$iGuiHeight)Local$aImageList[330];how many images to be displayedLocal$iCurrentIndex=0Local$iRows=0Local$iColumns=0For$i=0ToUBound($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=0ToUBound($aImageListReturn,$UBOUND_ROWS)-1For$iColumn=0ToUBound($aImageListReturn,$UBOUND_COLUMNS)-1$aImageListReturn[$iRow][$iColumn]=$aImageList[$iCurrentIndex]$iCurrentIndex+=1If($iCurrentIndex>=UBound($aImageList))ThenFor$i=$iColumnTo$iColumns$aImageListReturn[$iRow][$iColumn]=$aImageList[0]NextExitLoop2EndIfNextNextReturn$aImageListReturnEndFunc;==>LoadGdiImageListFunc DisposeGdiImageList(ByRef$aImageList)If(IsArray($aImageList))ThenFor$iRow=0ToUBound($aImageList,$UBOUND_ROWS)-1For$iColumn=0toUBound($aImageList,$UBOUND_COLUMNS)-1_GDIPlus_ImageDispose($aImageList[$iRow][$iColumn])NextNextElseReturnSetError(-1,0,0)EndIfReturn1EndFunc;==>DisposeGdiImageListFunc Max(ConstByRef$lhs,ConstByRef$rhs)Return($lhs>$rhs?$lhs:$rhs)EndFunc;==>MaxFunc GetTableDimensions(ByRef$iRows,ByRef$iColumns,$iMaxWidth,$iMaxHeight,$iCount,ConstByRef$iImageWidth,ConstByRef$iImageHeight)If($iImageWidth=0Or$iImageHeight=0Or$iMaxWidth<=20Or$iMaxHeight<=20)ThenReturnSetError(-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)Return1EndFunc;==>GetTableDimensionsFunc DrawGdipArea(Const$bDrawAtEnd=True)_GDIPlus_GraphicsClear($hBuffer,0xFFE1E1E1);bgcolor of the components areaFor$iRow=0ToUBound($aGDIImageList,$UBOUND_ROWS)-1For$iColumn=0ToUBound($aGDIImageList,$UBOUND_COLUMNS)-1Local$iLeft=$iTablePadding+($iTablePadding*$iColumn)+($iColumn*$iImageWidth)Local$iTop=$iTablePadding+($iTablePadding*$iRow)+($iRow*$iImageHeight)Local$iRight=$iLeft+$iImageWidthLocal$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)NextNext; 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 rowIf($aGDIPBuffer[0][0])ThenFor$iBuffer=0ToUBound($aGDIPBuffer,$UBOUND_ROWS)-1_GDIPlus_GraphicsDrawImageRect($hBuffer,$aGDIPBuffer[$iBuffer][0],$aGDIPBuffer[$iBuffer][1],$aGDIPBuffer[$iBuffer][2],$iImageWidth,$iImageHeight)NextEndIf_GDIPlus_GraphicsDrawLine($hBuffer,0,$iBottom+25,$iGuiWidth,$iBottom+25,$hSepPen)If($bDrawAtEnd)Then_GDIPlus_GraphicsDrawImage($hGraphic,$hBitmap,0,0)ReturnTrueEndFunc;==>DrawGdipAreaFunc WM_PAINT($hWndFrom,$iMsg,$wParam,$lParam)
DrawGdipArea()Return$GUI_RUNDEFMSGEndFunc;==>WM_PAINTFunc WM_LBUTTONDOWN($hWndFrom,$iMsg,$wParam,$lParam)Local$iLButtonX=_WinAPI_LoWord($lParam)Local$iLButtonY=_WinAPI_HiWord($lParam)Local$iImageRow=-1Local$iImageColumn=-1Local$aCursorInfo=0Local$iGdipBufferIndex=UBound($aGDIPBuffer,$UBOUND_ROWS)Switch($hWndFrom)Case$frmMainGuiFor$iRow=0ToUBound($aGDIImageList,$UBOUND_ROWS)-1For$iColumn=0ToUBound($aGDIImageList,$UBOUND_COLUMNS)-1Local$iLeft=$iTablePadding+($iTablePadding*$iColumn)+($iColumn*$iImageWidth)Local$iTop=$iTablePadding+($iTablePadding*$iRow)+($iRow*$iImageHeight)Local$iRight=$iLeft+$iImageWidthLocal$iBottom=$iTop+$iImageHeightIf(_WinAPI_PtInRectEx($iLButtonX,$iLButtonY,$iLeft,$iTop,$iRight,$iBottom))Then$iImageRow=$iRow$iImageColumn=$iColumnExitLoopEndIfNextNextIf($iImageRow>-1And$iImageColumn>-1)Then$aCursorInfo=GUIGetCursorInfo($hWndFrom)If(@error)ThenReturn$GUI_RUNDEFMSGWhile($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)ThenReturn$GUI_RUNDEFMSGWEnd$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+$iImageHeightReDim$aGDIPBuffer[$iGdipBufferIndex+1][5]
DrawGdipArea()EndIfEndSwitchReturn$GUI_RUNDEFMSGEndFunc;==>WM_LBUTTONDOWN

Edited March 18, 2016 by neazoi

Share this post

Link to post

Share on other sites

InunoTaishou 178

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>-1And$iImageColumn>-1)Then; Code for this sectionElseIf(_WinAPI_PtInRectEx($iLButtonX,$iLButtonY,$tRectLabel.x,$tRectLabel.y,$tRectLabel.x+$iGuiWidthInput,$tRectLabel.y+$iGuiHeightInput))Then; Code for this sectionElse; New elseEndIf

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

Share this post

Link to post

Share on other sites

JAPP 2

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

InunoTaishou 178

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.