C# Console always keep progress bar on the bottom - c#

What I am doing is reading logfile records into console.
I want to keep a progressbar in the bottom and shows progress.
Problem is: the updating record will override the progressbar.
private static void DrawProgressBar(int complete, int maxVal, int barSize, char progressCharacter)
{
Console.CursorVisible = false;
int left = Console.CursorLeft;
//int top = Console.CursorTop;
//Console.CursorTop = Console.WindowTop + Console.WindowHeight - 1;
decimal perc = (decimal)complete / (decimal)maxVal;
int chars = (int)Math.Floor(perc / ((decimal)1 / (decimal)barSize));
string p1 = String.Empty, p2 = String.Empty;
for (int i = 0; i < chars; i++) p1 += progressCharacter;
for (int i = 0; i < barSize - chars; i++) p2 += progressCharacter;
Console.ForegroundColor = ConsoleColor.Green;
Console.Write(p1);
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.Write(p2);
Console.ResetColor();
Console.Write(" {0}%", (perc * 100).ToString("N2"));
Console.CursorLeft = left;
//Console.SetCursorPosition(left, top);
}

You need to set the cursor to the correct position.
Here is a simple example of SetCursorPosition:
for (int i = 0; i < 10; i++) {
Console.SetCursorPosition(0, 0);
Console.Write("i = {0}", i);
System.Threading.Thread.Sleep(1000);
}
Before you print the progressbar you need to find out where to start and resetting the cursor before writing the progressbar.

There's an easy way to do this and several very difficult ways to do this.
The easy way is to re-display the progress bar after every call to Console.WriteLine. That way it doesn't matter if a scrolled line overwrites the progress bar, because you're just going to re-display it.
If you want to get fancy, you need to handle scrolling yourself. That means creating a TextWriter descendant that sees you writing to the last line (where the status line is), and , so that whenever some code wants to write to the bottom line where the progress bar is, you scroll the rest of the screen and write the new line in the vacated spot.
There are several problems with the second approach, the first being that in order to handle your own scrolling you have to call the Windows API; there is no support in the .NET BCL for manipulating the console screen buffer. I have code you can use to do it (see http://mischel.com/toolutil/consoledotnet.zip), but it's kind of involved. Plus, you have additional problems if the line you're writing is wider than the current window width. If the line wraps, then it'll overwrite your progress bar.
It really is simpler to just re-draw the status bar after every line, or perhaps group of lines if you're writing multiple lines at a time.

Related

C# Redraw/recreate an image externally with a brush tool

Phew, first burden here is to explain what I'm looking for within a short title.
Basically, I want to improve my existing pixel draw bot.
Main objective: It needs to be fast.
I am fairly content with its performance for now, but it can be better. What I am doing in a nutshell is very primitive; to sum it up:
Get an image and process it to be black and white (by setting a specific ColorMatrix)
Loop through every pixel in the image and decide to click the mouse, if it's black, and skip if not.
My main (performance) concerns right now that I am not sure how to solve:
I am currently still clicking 500 times in a row for drawing a black line in the image for example, instead of clicking and dragging once from x1 to x2
Use of System.Threading.Sleep(1): If I don't do that, the target application won't process and doesn't draw, except for a few pixels (testing in MS Paint). Shouldn't I be working with wait async etc.? I never quite learned how to successfully replace Sleep();
I hear img.GetPixel(x, y) is a slow approach and should be replaced with Bitmap.LockBits()? Agreements, concerns?
I'm importing RegisterHotKey via user32.dll and overriding WndProc to listen for a global system hotkey (to start the drawing process), could this also be better solved with native C# elements? I notice sometimes on building the solution, or while debugging that it takes up to a few seconds until my program runs initially.
Finally, the most important thing -- the codeZ:
Drawing:
void Draw()
{
Bitmap imgClipBoard = MakeBlackAndWhite((Bitmap)Clipboard.GetImage());
Point startPos = Cursor.Position;
ClickMouse(MouseButtons.Left, startPos.X, startPos.Y, true);
ClickMouse(MouseButtons.Left, startPos.X, startPos.Y, false);
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x++)
{
Color c = img.GetPixel(x, y);
if (c.B == 0)
{
int drawX = startPos.X + x;
int drawY = startPos.Y + y;
MoveMouse(drawX, drawY);
ClickMouse(MouseButtons.Left, drawX, drawY, true);
ClickMouse(MouseButtons.Left, drawX, drawY, false);
Thread.Sleep(1);
}
if (Keyboard.IsKeyDown(Keys.Escape))
return;
}
}
}
What I am also wondering: Are there any more intelligent ways to redraw an image programmatically, than pixel by pixel? For instance, identifying paths (lines) in the image to trace with one stroke instead of looping from top to bottom and clicking pixels, or is this something very advanced already?
Edit #1:
Here is a demonstration in GIF format regarding my first performance concern: Click and drag is infinitely faster than clicking pixel by pixel (this test still using GetPixel()).
Edit #2
-- Snip --
Let's forget about LockBits() for now.
My main concern is: How can I scan each pixel "mass", either by line or ideally the entire picture, so I can tell my program: Okay, here is this black and white image: I don't want you to click hundreds of times in a row for each black pixel, but instead I want this logic: scan -> first black pixel -> hold down mouse -> keep going till you hit a white pixel -> release. Rinse and repeat.
Please check Edit 1 to see why I believe this to make sense: Much faster image printing!
This following test is too slow for my purposes! SEE ANIMATED GIF
Edit #3:
I expect a nice rectangle but get zigzags
private void DrawRect()
{
int x = Cursor.Position.X;
int y = Cursor.Position.Y;
for (int counter = 0; counter < 100; counter++)
{
MoveMouse(x, y + counter);
ClickMouse(MouseButtons.Left, x, y + counter, true);
Thread.Sleep(1);
MoveMouse(x + 100, y + counter);
Thread.Sleep(1);
}
Thread.Sleep(15);
ClickMouse(MouseButtons.Left, 0, 0, false);
}
I know it's because of the short sleep intervals, and if I increase them I get exactly what I want: I suspect that I will have to use wait async if I want this to run as fast as possible, without having to try out several numbers for the sleep delay.

Working on each frame from a Vimba camera

I am working with an Allied Vision Vimba camera to monitor an experiment involving a laser.
I have written a code that allows me to take one frame from the camera live imaging (displayed in pictureBoxLiveCamera, in black and white), put it in another PictureBox (pictureBoxFixe1) and work on it. The code works fine for the most part, even though it is not finished.
My problem is that, eventually, I will need to work continuously, meaning on every frame from the camera (about 15-20 per second), and I am starting to feel like this is going to be complicated.
I am not asking you to help me through the whole process, but I have a precise exemple of something that is not working and I would like to understand. I am working with the Vimba .NET API, so some of the functions called might not be familiar to most of you (and they are not much more to me, really), but I'll try my best to explain them in the code (the API manual is kinda cryptic).
My code has a part that, when I isolate one frame from the camera to work on it (via a button-clickevent, draws an histogram that graphs the number of pixels on the vertical axis and digital number value on the horizontal axis. This way I know if some of the pixels are saturated (at 255), and I can lower the exposure time. This works fine, here is the code for the button that isolate one frame and draws the histogram :
private void fixButton_Click(object sender, EventArgs e)
{
if (m_Acquiring == true) //mm_Acquiring being true indicates that the camera is sending images continuously, and that they are toggled in pictureBoxLiveCamera
{
pictureBoxFixe1.Image = SaveBitmap; //SaveBitmap is the bitmap isolated from the live camera in an earlier code portion
Array.Clear(PixelColorCount, 0, 256);
foreach (var Series in chartHist.Series)
{
Series.Points.Clear();
}
//Creating a bitmap from pictureBoxFixe1 to draw the histogram from
Bitmap Couleur = new Bitmap(pictureBoxFixe1.Image);
//Collecting data from pictureBoxFixe1
for (int i = 0; i < 1023; i++)
{
for (int j = 157; j < 288; j++)
{
PixelColorCount[Couleur.GetPixel(i + 1, j).B] += 1;
}
for (int j = 484; j < 615; j++)
{
PixelColorCount[Couleur.GetPixel(i + 1, j).B] += 1;
}
}
//Plotting the pixel counter, to detect saturation
for (int i = 0; i < 256; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chart 1 to warn the user
if (PixelColorCount[255] > 1)
{
chartHist.Titles["Title1"].Visible = false;
chartHist.Titles["Title2"].Visible = true;
}
else
{
chartHist.Titles["Title1"].Visible = true;
chartHist.Titles["Title2"].Visible = false;
}
}
else
{
MessageBox.Show("Acquisition is not running.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
Now what I want is to make this work live, with every frame incoming from the camera. But it doesn't work and I can't find why. Here is what I tried :
private void BtAcquisitionLiveCam_Click(object sender, EventArgs e)
{
//Here is the part of the code that starts the live acquisition when I click the button, I don't copy it completely because I don't think this is relevant.
this.m_Acquiring = true;
mycamera.OnFrameReceived += new Camera.OnFrameReceivedHandler(this.OnFrameReceived); //This is the event handler for the reception of a frame for the camera, it is part of the Vimba API
mycamera.StartContinuousImageAcquisition(1); //According to the manual, this "Starts streaming and allocates the needed frames", with the argument being "count of Frame(s) which should be used for this method"
}
private void OnFrameReceived(Frame frame) //Frame is the class in which the code puts the data from the camera, frame is the last pack of data received from the camera
{
Bitmap myBitmap = null;
if (true == m_Acquiring)
{
try
{
mycamera.QueueFrame(frame);
}
catch (Exception)
{
MessageBox.Show("Frame queuing failed.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else
{
MessageBox.Show("Acquisition is not running.", "Something went wrong !", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
featureExposureTime.FloatValue = (double)hScrollBarLiveExposureTime.Value;
frame.Fill(ref myBitmap);
pictureBoxLiveCamera.Image = myBitmap;
SaveBitmap = myBitmap; //Up until here the code works perfectly, because I can use SaveBitmap without problem later in the code (not shown here)
Array.Clear(PixelColorCount, 0, 256);
foreach (var Series in chartHist.Series)
{
Series.Points.Clear();
}
//Collecting data from pictureBoxFixe1
for (int i = 1; i < 1023; i++)
{
for (int j = 1; j < 767; j++)
{
PixelColorCount[SaveBitmap.GetPixel(i, j).B] += 1;
}
}
//Plotting the pixel counter, to detect saturation
for (int i = 0; i < 256; i++)
{
chartHist.Series["Pixel count"].Points.AddXY(i, PixelColorCount[i]);
}
//If there are saturated pixels : toggle a title on chart 1 to warn the user
if (PixelColorCount[255] > 1)
{
chartInit.Titles["Title1"].Visible = true;
}
else { }
}
Sorry for the amount of code, but I thought I couldn't do otherwise. My problem is that the histogram doesn't show at all (well it is set as Visible, I see the title, but nothing gets plotted in it). Does anyone have any idea ?
The thing that is driving me crazy is that the VimbaViewer program that's provided with the camera does exactly what I want in a flawless way, but I don't have access to it's source code...
Thanks !
If I follow correctly, your OnFrameReceived event is firing correctly, but the WinForm application UI is not updating after each frame is processed.
It appears that "chartInit" is being referenced in your OnFrameReceived instead of "chartHist", is that supposed to be the case?
Whatever your chart control is, try "chart.Refresh()" or "chart.Update()". It might not be updating the UI thread from the event loop.
Another thing you'll need to think about is how fast these images are coming in. Will it draw and redraw the chart 15-20 times per second? Is that reasonable? Might need some sort of queue/averaging mechanism to only update the chart every x seconds, or perhaps if some threshold value is exceeded with respect to change in the images. Just a thought.

Display an image in real time as it is being created in a loop

In my code, the output is an image each pixel of which is determined using nested loops.
1) How can I force a window to open and show the output image as it is being constructed in the loop? (The window shows up when everything is finished. I don't want this.)
2) How can I have the output be displayed line by line (or even pixel by pixel) as the loop goes on. User must have the sense of getting the output in real-time.
outImage = new Image<Hsv, Byte>(numberOfColumns, numberOfRows);
byte[,,] pixelValue = outImage.Data;
for (int i = 0; i < numberOfRows - 1; i++)
{
for (int j = 0; j < numberOfColumns - 1; j++)
{
//pixelValue[i, j, k] is determined here using some other functions
imageBox1.Image = outImage; //too slow and impossible
}
}
You can display an image pixel by pixel in real time by putting it on a separate thread and using GetPixel and SetPixel. Keep in mind though that these methods are slow and if you are displaying high resolution pictures, it will take a while.
What you'll want to do is create a form with a picture box control on it. Next, create a Bitmap object containing the image you'll want to display. You can do this using a line like this:
Bitmap _bmp = new Bitmap(Image.FromFile(#"C:\MyImage.jpg"));
Next, in your form's shown event, spin off a new task to do the work, so the GUI doesn't lock up:
private void Form1_Shown(object sender, EventArgs e)
{
Task.Factory.StartNew(ShowImage);
}
This line will spin off a new thread every time the form is displayed. The thread will fork off and call ShowImage(), which should look like this:
private void ShowImage()
{
Graphics g = pbImage.CreateGraphics();
for (int x = 0; x < _bmp.Width; x++)
{
for (int y = 0; y < _bmp.Height; y++)
{
Color c = _bmp.GetPixel(x, y);
if (pbImage.InvokeRequired)
{
var x1 = x;
var y1 = y;
pbImage.BeginInvoke((Action) (() =>
{
g.FillRectangle(new SolidBrush(c), x1, y1, 1, 1);
}));
}
else
{
g.FillRectangle(new SolidBrush(c), x, y, 1, 1);
}
System.Threading.Thread.Sleep(1);
}
}
}
If you wanted to speed this up a bit, you could spin up two or more tasks, each task working in parallel (e.g. one thread starts at the beginning, another at the end, another in the middle maybe, etc). Just make sure your threads don't "overlap".
Another way to speed this up is to use pointers instead of GetPixel() and SetPixel(). You'd have to mark your code as unsafe though.
put your code in background Worker => Do Work
A separate thread would be initiated
I am not a WinForms expert I am more of a WPF type. But I have an application running a solid 30fps and that is faster than humans can detect. I really do not quite understand what you want to do here. You have to blit each pixel individually but have display in real time? An ImageBox derives from the Windows Forms PictureBox, that won't work I do not think.
You could try moving to WPF, and use a WriteableBitmap for a ImageSource for an Image object or the background of a Canvas Object. A WriteableBitmap will let you access each pixel, but the refresh rate is controlled by WPF and the monitor refresh rate is controlled by the AC current frequency.
Doug

How does the DoWork method work in C# for Async Communication (ELI5)

I am using Visual Studio C# 2010 on Windows 10.
EDIT: My objective is to create an abort button which allows me to use the UI to cancel a loop.
I'm using an example I found online which is similar to this:
Here is the code:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = "";
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(50); //do some intense task here.
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
(There was a bit of code sharing the date/time but I removed it since it was superfluous to the basic example).
How I'm understanding this code is that, 100 times, the code pauses the main thread and works on "some intense task" for 50ms. Is this the case?
If so, I think I've ran into a problem. I have this code I want to run:
private void btn_runtest_Click(object sender, EventArgs e)
{
// Exterior Platform Loop
Output.AppendText("Starting Test \n");
for (int i = 0; i <= 3200; i += 178)
{
// Interior Platform Loop
for (int ii = 0; i <= 6400; ii += 178)
{
comport7_interior.Write("#1N178\r"); //change to have actual length?
comport7_interior.Write("#1G\r");
Output.AppendText("Interior shift 5 degrees \n");
Thread.Sleep(4000);
// ***********************************************************************************
// Read data from comport2_lightsensor, Parse byte, store Lux value, store angle (i,ii)
// ***********************************************************************************
}
comport8_exterior.Write("#0N178\r");
comport8_exterior.Write("#0G\r");
Output.AppendText("Exterior shift 5 degrees \n");
Thread.Sleep(4000);
//flip direction for internal
if (IsOdd(jjj))
{
//
comport7_interior.Write("#1-\r");
Output.AppendText("Interior switching direction to counter clockwise \n");
}
else
{
comport7_interior.Write("#1+\r");
Output.AppendText("Interior switching direction to clockwise \n");
}
jjj = jjj + 1;
// ***********************************************************************************
// Read data from compart2_lightsensor, Parse byte, store Lux value, store angle (i,ii)
// ***********************************************************************************
}
Output.AppendText("Loop Ended");
// manually reverse mount direction
// repeat the whole double for loop
}
This is pretty "fatty" code, so to summarize, it controls two stepper motors, rotates them to follow the desired path, pauses for 4 seconds, and will eventually log data. We could do the exact math but simply estimating we can realize this code will take 1-2 hours. With all of this pausing, it does not seem conducive to being split into 100 little chunks on worked on separately (if my understanding of the previously stated code is correct). Correct me I'm wrong but I don't think this code would fit in the above code.
Does this mean I'm looking at the wrong approach?
Thank you all in advance.

Why does it seem like operations are not being performed in the order of the code?

Here's some background. I'm working on game similar to "Collapse." Blocks fill up at the bottom and when all twelve blocks have been filled they push up on to the playfield. I have a counter called (intNextSpawn) that not only tells when to "push up" the next row, as well as calculating vectors for the graphics. It resets to 0 when the blocks have been pushed up.
I've added some debug text on the screen to try and see what is happening, but I can't seem to hunt down the issue. It almost seems like it is still incrementing the counter while trying to randomize the the block that's supposed to appear (things acting out of order). I end up getting "blank" blocks and it causes some really screwy effects while testing. It gets worse when jack up the speed.
I'm willing to post any additional code that might help. Below are the two main blocks where this is could happening. Is there something I might be doing wrong or may there be a way I can prevent this from happening (if that's what it's doing)?
Any help would be greatly appreciated.
Edit... The first code block is in the "Update" method
// Calculate time between spawning bricks
float spawnTick = fltSpawnSpeed * fltSpawnSpeedModifier;
fltSpawn += elapsed;
if (fltSpawn > spawnTick)
{
// Fetch a new random block.
poNextLayer[intNextSpawn] = RandomSpawn();
// Increment counter
intNextSpawn++;
// Max index reached
if (intNextSpawn == 12)
{
// Push the line up. Returns true if lines go over the top.
if (PushLine())
{
gmStateNew = GameState.GameOver;
gmStateOld = GameState.Playing;
}
// Game still in play.
else
{
// Reset spawn row to empty bricks.
for (int i = 0; i < 12; i++)
poNextLayer[i] = new PlayObject(ObjectType.Brick, PlayColor.Neutral, Vector2.Zero);
intNextSpawn = 0; // Reset spawn counter.
intLines--; // One less line to go...
}
}
fltSpawn -= spawnTick;
}
private bool PushLine()
{
// Go through the playfield top down.
for (int y = 14; y >= 0; y--)
{
// and left to right
for (int x = 0; x < 12; x++)
{
// Top row contains an active block (GameOver)
if ((y == 14) && (poPlayField[x, y].Active))
// Stop here
return true;
else
{
// Not bottom row
if (y > 0)
{
// Copy from block below
poPlayField[x, y] = poPlayField[x, y - 1];
// Move drawing position up 32px
poPlayField[x, y].MoveUp();
}
// Bottom row
else
{
// Copy from spawning row
poPlayField[x, y] = poNextLayer[x];
// Move drawing position up 32px (plus 4 more)
poPlayField[x, y].MoveUp(4);
// Make the block active (clickable)
poPlayField[x, y].Active = true;
}
}
}
}
// Game still in play.
return false;
}
So, the largest part of your problem is that you are decrementing some timers based on the size of a tick, and then running some comparisons. Wrather than do all that for timers (especially with the rounding and precision loss of float, etc...) instead just to time-basic comparisons.
Example, you do fltSpawn += elapsed and later fltSpawn -= spawnTick which will lead to floating point rounding / precission errors.
Try something more like this:
int64 spawnDelay = 1000000; // 10,000 ticks per ms, so this is 100ms
...
if (DateTime.Now() > nextSpawn)
{
// Fetch a new random block.
poNextLayer[intNextSpawn] = RandomSpawn();
...
// At some point set the next spawn time
nextSpawn += new TimeSpan(spawnDelay * spawnSpeedModifier);
}

Categories

Resources