I am looking for help with the drawpolygon method. I haven't had any luck getting anything to work. Looking to do 5 triangles, same size, next to each other. Problem is I have to use a do-while loop. Thanks for taking the time to help me!!
Just define the points in an array an then write them with a pen:
Dim blackPen As New Pen(Color.Black, 3)
Dim point1 As New Point(50, 50)
Dim point2 As New Point(100, 25)
Dim curvePoints As Point() = {point1, point2}
Me.CreateGraphics.DrawPolygon(blackPen, curvePoints)
Take a look at the MSDN Documentation about it.
It doesn't matter if you do anything in a loop, depends how. If this didn't solve your problem post your code in order to help you more.
Example with a loop:
Do While i < 3
point1 As New Point(50 + i * 10, 50)
point2 As New Point(100 + i * 7, 25)
curvePoints = {point1, point2}
Me.CreateGraphics.DrawPolygon(blackPen, curvePoints)
i += 1
Loop
I haven't actually tested this construction but it's all snipped out of a working project; I suspect it'll work and some of this GDI+ stuff is really grungy to pick up the first time.
Public Class Form1
Private subject As Image
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
If Not subject Is Nothing Then
Dim g As Graphics = e.Graphics
g.DrawImage(subject, New Point(1, 1))
End If
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim tempBM As New Bitmap(subject)
tempBM.SetResolution(subject.HorizontalResolution, subject.VerticalResolution)
Using g As Graphics = Graphics.FromImage(tempBM)
g.DrawPolygon(OutlinePen, Polygon.GetPoints)
End Using
subject = tempBM
Invalidate()
End Sub
End Class
Oh, Polygon is a class in my code that you won't have. But just replace Polygon.GetPoints with whatever array of points you want to use.
Related
Below is code for a simple voting system I am coding.
Public Class Form1
Dim winner As String
Dim maxVotes As Integer
Dim votes() As String
Dim index As String
Dim candidates As String
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
If Not isValidInput(txtNewCandidate.Text) Then
Exit Sub
End If
lstCandidates.Items.Add(txtNewCandidate.Text)
txtNewCandidate.Clear()
txtNewCandidate.Focus()
ReDim Preserve votes(index)
index += 1
End Sub
Private Function isValidInput(ByRef firstName As String) As Boolean
If IsNumeric(txtNewCandidate.Text) Or txtNewCandidate.Text = "" Then
MsgBox("Please input a valid candidate name.")
txtNewCandidate.Focus()
Return False
Else
Return True
End If
End Function
Private Sub btnTally_Click(sender As Object, e As EventArgs) Handles btnTally.Click
lstTallies.Visible = True
lblTally.Visible = True
For i = 0 To lstCandidates.Items.Count - 1
lstTallies.Items.Add(lstCandidates.Items(i).ToString & " - " & votes(i))
Next
End Sub
Private Sub lstCandidates_DoubleClick(sender As Object, e As EventArgs) Handles lstCandidates.DoubleClick
If lstCandidates.SelectedIndex = -1 Then
MsgBox("Select a candidate by double-clicking")
End If
votes(lstCandidates.SelectedIndex) += 1
MsgBox("Vote Tallied")
End Sub
Private Sub pbxWinner_Click(sender As Object, e As EventArgs) Handles pbxWinner.Click
End Sub
End Class
The voter must double click on their choice of candidate in the first list box. The user then tallies the votes by clicking on a button and a second list box will appear with the votes per candidate.
Now I need to display the winner (or winners, if there is a tie) in a picture box, pbxWinner. I am not sure how to accomplish this. Any clues?
Here is what i am trying to do, though the code below doesn't work.
Private Function candidateWinner(ByRef winner As String) As Boolean
For i As Integer = 0 To lstCandidates.SelectedIndex - 1
If votes(i) > maxVotes Then
maxVotes += 1
End If
Next
g = pbxWinner.CreateGraphics
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("Arial", 7, FontStyle.Regular), Brushes.DarkBlue, New PointF(0, 0))
Return True
End Function
Your code is actually working fine for an initial paint, but when the picture box image doesn't have its own bitmap set, a number of events can repaint its graphics behind the scenes(even as simple as minimizing/mazimizing the form, and a whole bunch of other ones), so in effect your text seems to never appear at all or disappear almost instantly when in reality it's probable getting repainted. To fix this, use a bitmap for the graphics object's reference, paint the bitmap's graphics, and then assign the bitmap to the picturebox's image property. This will make the image persistent...give this code a try in your candidateWinner function after the for loop:
Dim bmp As New Bitmap(pbxWinner.Width, pbxWinner.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("arial", 7, FontStyle.Regular), Brushes.DarkBlue, 0, 0)
pbxWinner.Image = bmp
...If you still aren't seeing text, make sure the winner string has the correct value set, I tested this code and it showed my test string correctly
Edit for Comment:
That's because of the logic you're using to calculate the winner...you are just checking to see if the currently selected candidate's vote count is higher than maxVotes and then incrementing the max by 1. If you wanted to stick with that sort of logic for picking the winner, you would want to iterate through ALL of the candidates(not just those from index 0 to the currently selected one), and if their vote count is higher than the max, then set the max EQUAL to their vote count. Then the next candidate in the loop will have their count checked against the previous max. However, tracking the winner could be done a lot easier if you just use a dictionary since you are allowing candidates to be added, and you must change your "winner" logic to actually check who has the most votes out of everyone entered. A bare bones example of that would look like this:
Dim dctTally As Dictionary(Of String, Integer)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
dctTally = New Dictionary(Of String, Integer)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
dctTally.Add(txtNewCandidate.Text, 0)
lstCandidates.Items.Add(txtNewCandidate.Text)
End Sub
Private Sub lstCandidates_DoubleClick(sender As Object, e As EventArgs) Handles lstCandidates.DoubleClick
dctTally(lstCandidates.text) += 1
End Sub
Private Sub pbxWinner_Click(sender As Object, e As EventArgs) Handles pbxWinner.Click
Dim winner = dctTally.Aggregate(Function(l, r) If(l.Value > r.Value, l, r)).Key
Dim bmp As New Bitmap(pbxWinner.Width, pbxWinner.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(winner, New Font("arial", 7, FontStyle.Regular), Brushes.DarkBlue, 0, 0)
pbxWinner.Image = bmp
End Sub
This way, the program allows as many names as you want to be added to the candidates list, and will add a vote count to their name each time their name is double-clicked on. Then, when your winner pixturebox is clicked, it will find the dictionary with the highest vote count and display their name in the winner-box.
You can try this to draw the winners:
Private Sub candidateWinner()
Dim y As Single = 0
maxVotes = votes.Select(Function(x) Convert.ToInt32(x)).Max()
For i = 0 To UBound(votes)
If votes(i) = maxVotes.ToString() Then
g = pbxWinner.CreateGraphics
g.TranslateTransform(10.0F, 0.0F)
g.DrawString(lstCandidates.Items(i).ToString(), New Font("Arial", 7, FontStyle.Regular), Brushes.DarkBlue, New PointF(0, y))
y += 10
g.Dispose()
End If
Next
End Sub
I needed an app that observed numbers in my screen and then make calculations with it, so after some days on researching the best and easiest method i found this video
(https://www.youtube.com/watch?v=Kjdu8SjEtG0) that leaded me to OCR and EMGU-Tesseract on Visual Basic 2010 express. I understanded the video and I made my own variation of the code on the description of the video.
I imported:
Imports Emgu.CV
Imports Emgu.Util
Imports Emgu.CV.OCR
Imports Emgu.CV.Structure
then i make this based on the original code:
Dim OCRz As Tesseract = New Tesseract("tessdata", "eng", Tesseract.OcrEngineMode.OEM_TESSERACT_ONLY)
Dim picStc1 As Bitmap = New Bitmap(149, 28)
Dim gfxSTK1 As Graphics = Graphics.FromImage(picStc1)
Dim picNam1 As Bitmap = New Bitmap(149, 28)
Dim gfxNAM1 As Graphics = Graphics.FromImage(picNam1)
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
gfxSTK1.CopyFromScreen(New Point(Me.Location.X + Stk1.Location.X + 5, Me.Location.Y + Stk1.Location.Y + 24), New Point(0, 0), picStc1.Size)
Stk1.Image = picStc1
gfxNAM1.CopyFromScreen(New Point(Me.Location.X + Nome1.Location.X + 5, Me.Location.Y + Nome1.Location.Y + 24), New Point(0, 0), picNam1.Size)
Nome1.Image = picNam1
And when i pressed the button i get this :
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
OCRz.Recognize(New Image(Of Bgr, Byte)(picStc1))
BOXSTK1.Text = OCRz.GetText
OCRz.Recognize(New Image(Of Bgr, Byte)(picNam1))
BoxNAME1.Text = OCRz.GetText
I now have the text read from the PictureBoxes (picStc1) and (picNam1) thru the OCR engine and its writen on the RichTextBoxes (BoxSTK1) and (NAME1) after i pressed the button.
The numbers on the RichTextBox (BoxSTK1) come with commas and other simbols but i just want to grab the numbers. So i found this (https://code.google.com/p/tesseract-ocr/wiki/FAQ#How_do_I_recognize_only_digits?) but i cant implement it on the project, any help on this?
(I´m using Emgu 2.9.0.1922, dont know how to see the version of Tesseract)
This digit-based "whitelist" appears to be something you'd set when you initialize the object.
Check out this question
So you will need to change,
Dim OCRz As Tesseract = New Tesseract("tessdata", "eng", Tesseract.OcrEngineMode.OEM_TESSERACT_ONLY)
To something like this,
Dim OCRz As Tesseract = New Tesseract()
OCRz.SetVariable("tessedit_char_whitelist", "0123456789")
OCRz.init("tessdata", "eng", false)
Ok people, this problem is solved! Thanks to Mr.Jimmy Smith!
Now we dont need to train any tesseract.
By converting the OCR value to a string!
First define the whitelist by using this:
OCRz.SetVariable("tessedit_char_whitelist", ",$0123456789")
Then convert the string like this and print it:
RichTextBox1.Text = Convert.ToString(OCRz.GetText).Replace("$", "").Replace(",", "")
At the end we get this:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
OCRz.SetVariable("tessedit_char_whitelist", ",$0123456789")
OCRz.Init("tessdata", "eng", False)
OCRz.Recognize(New Image(Of Bgr, Byte)(pic))
RichTextBox1.Text = Convert.ToString(OCRz.GetText).Replace("$", "").Replace(",", "")
I will thank Jimmy Smith again for his fast answers and really useful, mind yourselves to up vote this guy ;)
On fix and download:
Dim OCRz As Tesseract =
New Tesseract("tessdata", "eng",Tesseract.OcrEngineMode.OEM_DEFAULT)
Dim pic As Bitmap = New Bitmap(270, 100)
Dim gfx As Graphics = Graphics.FromImage(pic)
I am developing one vb application. In that I have one list box. I want to add different types of Items. Like Different Colored and differently aligned text(Like one item item is right aligned and one more is left aligned). Can you please tell me how can i do the same.
Thanks.
This is how I did it in one of my projects (the original code is in c#):
Public Class ColoringStepCommandListBox
Inherits CommandListBox
Const ItemHeight As Integer = 20
Public Sub New()
listBox.ItemHeight = ItemHeight
listBox.DrawMode = DrawMode.OwnerDrawFixed
End Sub
Protected Overrides Sub OnDrawItem(sender As Object, e As DrawItemEventArgs)
Const textFormatFlags__1 As TextFormatFlags = TextFormatFlags.EndEllipsis Or TextFormatFlags.PreserveGraphicsClipping Or TextFormatFlags.VerticalCenter
Const colorRectangleWidth As Integer = 100, textLeft As Integer = 110
If e.Index >= 0 Then
'Cast the listbox item to your custom type (ColoringStep in my example).
Dim coloringStep = TryCast(listBox.Items(e.Index), ColoringStep)
e.DrawBackground()
'Do custom coloring and rendering, draw icons etc.
Dim colorRect = New Rectangle(2, e.Bounds.Top + 2, colorRectangleWidth, ItemHeight - 5)
Dim innerRect = New Rectangle(colorRect.Left + 1, colorRect.Top + 1, colorRect.Width - 1, colorRect.Height - 1)
e.Graphics.DrawRectangle(Pens.Black, colorRect)
DrawingHelper.DrawGradient(coloringStep, e.Graphics, innerRect, LinearGradientMode.Horizontal)
'Draw the text (this does not happen automatically any more with owner draw modes).
Dim textRect = New Rectangle(textLeft, e.Bounds.Top, e.Bounds.Width - textLeft, e.Bounds.Height)
TextRenderer.DrawText(e.Graphics, coloringStep.ToString(), e.Font, textRect, e.ForeColor, textFormatFlags__1)
e.DrawFocusRectangle()
End If
End Sub
End Class
I got simple solution for this. Below is the code
Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
e.DrawBackground()
Dim textFont As New Font(e.Font.FontFamily, e.Font.Size * 4)
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), textFont, New SolidBrush(Color.BlueViolet), RectangleF.op_Implicit(e.Bounds))
e.DrawFocusRectangle()
End Sub
Private Sub listBox1_MeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs) Handles ListBox1.MeasureItem
e.ItemHeight = e.ItemHeight * 4
End Sub
You can add extra code inside ListBox1_DrawItem method to customize Items
I'm writing a program that (amongst other things) provides an IDE-like environment for the user where they can select one or more objects with a rectangualr selection tool.
All selections will be a simple rectangle, and all selectable objects will be simple rectangles as well.
I already have the code (VB.Net) to create the rubber-banding effect visually - what I need is an efficient algorithm that will tell me what objects have at least a portion of their area within the final selection rectangle.
If it helps to visualize, what I want to do would be identical to dragging a selection box over icons on the Windows desktop... whichever icons have even a portion of their areas located within that selection marquee are highlighted (selected).
Any help would be appreciated... thank you in advance
Dim Rect1 As New Rectangle(10, 10, 20, 20)
Dim Rect2 As New Rectangle(5, 5, 20, 20)
Debug.Print(Rect1.IntersectsWith(Rect2))
IntersectsWith works as BigFunger already has mentioned. But aditionally you should check if a rectangle contains another rectangle(intersectsWith only checks for intersection).
A small sample-form that demonstrates it:
Public Class SelectionRectangle
Private first As Point
Private allRectangles As New List(Of RectangleF)
Private Sub form_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseDown
first = New Point(e.X, e.Y)
End Sub
Private Sub form_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseUp
Dim p As New Pen(Brushes.Black, 2)
Dim g As Graphics
Dim second As New Point(e.X, e.Y)
Dim x, y, w, h As Int32
x = DirectCast(IIf(first.X > second.X, second.X, first.X), Int32)
y = DirectCast(IIf(first.Y > second.Y, second.Y, first.Y), Int32)
w = Math.Abs(second.X - first.X)
h = Math.Abs(second.Y - first.Y)
Dim nextRec As New RectangleF(x, y, w, h)
Dim intersects As Boolean = False
For Each rec As RectangleF In allRectangles
If rec.Contains(nextRec) OrElse rec.IntersectsWith(nextRec) Then
intersects = True
Exit For
End If
Next
If Not intersects Then
p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot
g = Me.CreateGraphics()
g.DrawLine(p, first.X, first.Y, second.X, first.Y)
g.DrawLine(p, second.X, second.Y, first.X, second.Y)
g.DrawLine(p, first.X, first.Y, first.X, second.Y)
g.DrawLine(p, second.X, second.Y, second.X, first.Y)
allRectangles.Add(nextRec)
Else
Beep()
End If
End Sub
End Class
UPDATE: changed this code to 1.first check in both directions and 2. and more important for you: checks also if one rectangle not only intersects another but additionally if it contains another.
I have tried to do this both with GDI+/Invalidate and by using a Line Shape Control. In both cases the memory spirals out of control. To demonstrate, create a windows form application with a timer object which is set to 100ms and enabled and use the following code:
Public Class Form1
Private Y As Integer
Private intDirection As Integer = 1
Private Sub timTest_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles timTest.Tick
Me.Invalidate()
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
Dim g As Graphics = Me.CreateGraphics
Dim myPen As New Pen(Color.Black)
myPen.Width = 1
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.DrawLine(myPen, 10, 10, 200, Y)
Y += intDirection
If Y > Me.Height - 20 Then intDirection = -1
If Y < 0 Then intDirection = 1
g.Dispose()
myPen.Dispose()
End Sub
End Class
So the code above causes a memory leak as the line moves. I think the reason is that there are unmanaged bitmaps created behind the scenes to paint the form which are not being released because on the managed side it is just a pointer.
If I add the following code at the start of the paint function
Dim intAlloc As Integer = Me.Width * Me.Height * 16
GC.AddMemoryPressure(intAlloc)
and at the end of the function I call
GC.RemoveMemoryPressure(intAlloc)
The memory utilization grows a little and shrinks a little but never grows out of control. The AddMemoryPressure and RemoveMemoryPressure seems to alert the GC that it needs to run. Is there a better way to do this or is this correct? The code above is just a simplification for example purposes to get to the root of a problem I have in a larger component with several moving lines. Also is this the best way to calculate the proper value to place in AddMemoryPressure?