How to write info in console window on the position [duplicate] - c#

I have a console application project in C# 2.0 that needs to write something to the screen in a while loop. I do not want the screen to scroll because using Console.Write or Console.Writeline method will keep displaying text on console screen incremently and thus it starts scrolling.
I want to have the string written at the same position. How can i do this?
Thanks

Use Console.SetCursorPosition to set the position. If you need to determine it first, use the Console.CursorLeft and Console.CursorTop properties.

Function to write the progress of a loop. Your loop counter can be used as the x position parameter. This prints on line 1, modify for your needs.
/// <summary>
/// Writes a string at the x position, y position = 1;
/// Tries to catch all exceptions, will not throw any exceptions.
/// </summary>
/// <param name="s">String to print usually "*" or "#"</param>
/// <param name="x">The x postion, This is modulo divided by the window.width,
/// which allows large numbers, ie feel free to call with large loop counters</param>
protected static void WriteProgress(string s, int x) {
int origRow = Console.CursorTop;
int origCol = Console.CursorLeft;
// Console.WindowWidth = 10; // this works.
int width = Console.WindowWidth;
x = x % width;
try {
Console.SetCursorPosition(x, 1);
Console.Write(s);
} catch (ArgumentOutOfRangeException e) {
} finally {
try {
Console.SetCursorPosition(origCol, origRow);
} catch (ArgumentOutOfRangeException e) {
}
}
}

Related

Centering Console.WriteLine text in the line

I am making a program based on the console. I want to make it look as clean as possible and I want the texts to be centered in their lines.
I tried
string msg = "Hello";
Console.SetCursorPosition((Console.WindowWidth - string.Length) / 2, Console.CursorTop);
Console.WriteLine(msg);
It worked. BUT it doesn't quite solve my problem. You see, I want multiple lines to be centered. For example
string title = "--------Message-------";
Console.SetCursorPosition((Console.WindowWidth - string.Length) / 2, Console.CursorTop);
Console.WriteLine(title);
Console.WriteLine(msg);
Now the entire thing is messed up. I hope someone can give a solution to my issue. Thanks
It is not possible to create Console extension methods, but you may try something like this:
public static class ConsoleExtensions
{
public static void WriteLineCentered(string msg)
{
Console.SetCursorPosition((Console.WindowWidth - msg.Length) / 2, Console.CursorTop);
Console.WriteLine(msg);
}
}
And then use this method, when you want to center a text:
string title = "--------Message-------";
string msg = "Hello";
ConsoleExtensions.WriteLineCentered(msg);
ConsoleExtensions.WriteLineCentered(title);
Another option, use one of the following methods to start at top of screen or begin at center of screen.
public class ConsoleHelpers
{
/// <summary>
/// Center lines horizontally and vertically
/// </summary>
public static void CenterLines(params string[] lines)
{
int verticalStart = (Console.WindowHeight - lines.Length) / 2;
int verticalPosition = verticalStart;
foreach (var line in lines)
{
int horizontalStart = (Console.WindowWidth - line.Length) / 2;
Console.SetCursorPosition(horizontalStart, verticalPosition);
Console.Write(line);
++verticalPosition;
}
}
/// <summary>
/// Center lines vertically starting at top of screen
/// </summary>
public static void CenterLinesFromTop(params string[] lines)
{
int verticalPosition = 0;
foreach (var line in lines)
{
int horizontalStart = (Console.WindowWidth - line.Length) / 2;
Console.SetCursorPosition(horizontalStart, verticalPosition);
Console.Write(line);
++verticalPosition;
}
}
}
Usage ConsoleHelpers.CenterLinesFromTop("Hello world","Enjoy the ride"); and ConsoleHelpers.CenterLines("Hello world","Enjoy the ride");
You need to centre each line individually. It's helpful to make a helper function that simply prints out a line of text in the middle of the current line rather than doing it over and over manually.
Of course, that's only going to work for text that fits within a single line - but then again, multi-line text shouldn't really be centred in the first place; that's always going to be awful to look at.

Iterating through all the possible combinations of several lists items

I have a face model with 12 blendshapes where each blendshape is simply a list of float values between 0 (neutral facial expression) and 1 (maximum activated expression) but I am starting off with only the first 2 blendshapes; i.e. only two lists for now, say smile and skeptic looks.
My intention is to go through all the possible combinations of all the items in these two lists and make a footage (movie clip) of the facial movements, to display how all the possible combinations of blendshape values/weights look like.
So, I wrote the following to facilitate this scenario for only two blendshapes for now, and I save them to file as soon as the application closes:
public class BlendShapesVAL : MonoBehaviour
{
private List<float> _weightValues_Preset1_smile = new List<float>();
private List<float> _weightValues_Preset2_skeptic = new List<float>();
public bool _TransitionAnimation = true;
public float _TransitionAnimationSpeed = 2f;
public BlendShapesPresetController _BSPC;
private List<float> _weightsList = new List<float>();
public List<bool> _ActivationsList = new List<bool>();
public List<string> _PresetsNamesList = new List<string>();
private void Awake()
{
_weightsList.Clear();
_ActivationsList.Clear();
for (int i = 0; i < _PresetsNamesList.Count; ++i)
{
_ActivationsList.Add(false);
_weightsList.Add(0);
}
}
private void Start()
{
if (_BSPC != null)
{
// . . .
}
else
{
_BSPC = GetComponent<BlendShapesPresetController>();
}
StartCoroutine("Interpolate");
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file when the application quits.
/// </summary>
///
private void OnApplicationQuit()
{
SaveBlendShapesValues(_weightValues_Preset1_smile);
SaveBlendShapesValues(_weightValues_Preset2_skeptic);
PlayerPrefs.DeleteAll();
}
/// <summary>
/// Goes thorugh all the possible combinations of blendshape weights.
/// For now, only the first two though!
/// </summary>
///
private IEnumerator Interpolate()
{
for (int i = 0; i <= 100; i++)
{
float weightValuesmile = (float)i / 100.0f;
_BSPC.SetWeight("Preset1_smile", weightValuesmile);
_weightValues_Preset1_smile.Add(weightValuesmile);
for (int j = 0; j <= 100; j++)
{
float weightValueSkeptic = (float)j / 100.0f;
_BSPC.SetWeight("Preset2_skeptic", weightValueSkeptic);
_weightValues_Preset2_skeptic.Add(weightValueSkeptic);
}
yield return null;
}
}
/// <summary>
/// Writes (i.e. saves) blendshape values to file.
/// <param name="blendShapesValuesFilePath">
/// The path to the file that will store the list of float values;
/// i.e. "Application.dataPath" plus the name of the CSV file.
/// </param>
/// <param name="values">
/// The float values that are the blendshape weights.
/// </param>
/// </summary>
///
private static void SaveBlendShapesValues(List<float> values)
{
List<string> lines = new List<string>
{
/// Add a header row for the labels.
"TimeStamp,Preset1_smile,Preset2_skeptic"
};
foreach (var value in values)
{
/// Iterate through all the elements.
lines.Add(DateTime.Now + "," + value);
}
/// Load the old counter.
int counter = PlayerPrefs.GetInt("_counter_", 0);
/// Concatenate the file name constituents and combine it with the application data path.
string fileName = string.Format("BlendShapesValues_{0}.csv", counter.ToString() );
string tempPath = Path.Combine(Application.dataPath, fileName);
try
{
File.WriteAllLines(tempPath, lines.ToArray() );
Debug.Log("Saved blendshape weight values to: " + tempPath);
/// Increment the counter.
counter++;
/// Save the current counter.
PlayerPrefs.SetInt("_counter_", counter);
PlayerPrefs.Save();
}
catch (Exception e)
{
Debug.LogWarning("Failed to save to PlayerPrefs: " + tempPath);
Debug.LogWarning("Error: " + e.Message);
}
}
}
In the Unity Editor, the blendshapes are displayed with values from 0 to 100, hence my conversion in the code, as seen in this screenshot:
The first file has 101 values (0...100 plus a top row for column labels) a snippet can be seen in this screenshot:
The second file has 10201 values. My first question is whether this method of saving the iterated values to file after the app stops is a good solution, given the large growth in the values as I add more lists (i.e. blendshapes)?
My second question is how I can slow down the iterations, because (in the first screenshot) the smile values start counting up from 0 to 100 and I can see them (the face moves slowly in a visible manner) but as that is happening, I notice that the second list (skeptic) apparently jumps to 100 immediately, so it is done so quickly that it cannot be recorded by the Win screen recorder...
Well i was not sure if this could be a comment or an answer so let me know if i am missing something.
For your first question if you are okay with those saved files being non-readable by humans i would suggest using BinaryReader. Because size of saved files would be smaller and when you want to read them back to make your clip it will read it faster. Also, considering you want to have 12 blend shapes and their combination this file can be huge.
For your second question iteration jumps to 100 because you only yield when the inner loop is completed. Therefore, in each frame 100 combinations of skeptical for 1 smile are added to the list. I would recommend using multi threading or Unity job system for such a task because it can be computationally costly with 12 blendshapes and all the combinations.
Let me know if i can help further. Good luck!

How to get the window Automation id of controls in 3rd party application with UIAutomation at specific screen coordinates

I am using UIAutomation and trying to get the window Id of any control in 3rd party applications. I want to know how to find out the ID of the control at a specific coordinate on the screen.
Example: I have Calculator, Notepad, and Word running on the desktop. All of them are running and sharing the screen partially. I want to be able to run my program and then click at any location on the screen and get the window ID of the underlying control (if there is any under the mouse).
What do I need to use to achieve this functionality. I understand that I need to have some sort of mouse hook, but the real problem is how to get the window ID (not the window handle) of the control on the screen where the mouse was clicked.
AutomationElement.FromPoint() will return the automation element at a given point. Once you have that, you can trivially get the automation ID:
private string ElementFromCursor()
{
// Convert mouse position from System.Drawing.Point to System.Windows.Point.
System.Windows.Point point = new System.Windows.Point(Cursor.Position.X, Cursor.Position.Y);
AutomationElement element = AutomationElement.FromPoint(point);
string autoIdString;
object autoIdNoDefault = element.GetCurrentPropertyValue(AutomationElement.AutomationIdProperty, true);
if (autoIdNoDefault == AutomationElement.NotSupported)
{
// TODO Handle the case where you do not wish to proceed using the default value.
}
else
{
autoIdString = autoIdNoDefault as string;
}
return autoIdString;
}
If i understand it Correctly, what your are trying to achieve is->
Click at any point of screen, get the window id of the underlying element ,if any, from the running elements:
If that's the case,the following should help/give an idea(NOTE: This would extend for not only cursor position but will continue search along X-axis for 100 pixels with a interval of 10):
/// <summary>
/// Gets the automation identifier of underlying element.
/// </summary>
/// <returns></returns>
public static string GetTheAutomationIDOfUnderlyingElement()
{
string requiredAutomationID = string.Empty;
System.Drawing.Point currentLocation = Cursor.Position;//add you current location here
AutomationElement aeOfRequiredPaneAtTop = GetElementFromPoint(currentLocation, 10, 100);
if (aeOfRequiredPaneAtTop != null)
{
return aeOfRequiredPaneAtTop.Current.AutomationId;
}
return string.Empty;
}
/// <summary>
/// Gets the element from point.
/// </summary>
/// <param name="startingPoint">The starting point.</param>
/// <param name="interval">The interval.</param>
/// <param name="maxLengthToTraverse">The maximum length to traverse.</param>
/// <returns></returns>
public static AutomationElement GetElementFromPoint(Point startingPoint, int interval, int maxLengthToTraverse)
{
AutomationElement requiredElement = null;
for (Point p = startingPoint; ; )
{
requiredElement = AutomationElement.FromPoint(new System.Windows.Point(p.X, p.Y));
Console.WriteLine(requiredElement.Current.Name);
if (requiredElement.Current.ControlType.Equals(ControlType.Window))
{
return requiredElement;
}
if (p.X > (startingPoint.X + maxLengthToTraverse))
break;
p.X = p.X + interval;
}
return null;
}

For Loops, Application Hanging

Well I'm creating an application, I'm using for loops to basically read every pixel of an image looking for patterns in pixel color (simple stuff) Anyway for some reason my application simply locks up and never reverts back to normal. I've loop through the code time and time again without seeing any real problems.
The only thing I've noticed, is the for loop in ScanPixelsLater may be exiting early. I've commented the code as much as possible,
private Point topLeftc, bottomLeftc, topRightc, bottomRightc;
/// <summary>
/// Starts the initial looping process, designed only to loop through ONCE, ScanPixelsLater takes over
/// </summary>
/// <param name="img">Image to scan</param>
public void ScanPixels(Bitmap img)
{
int whitePixel = 0;
for (int y = 100; y < img.Height; y++)
{
for (int x = 100; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
// img.SetPixel(x, y, Color.Green);
whitePixel++;
}
else { whitePixel = 0; }
if (whitePixel == 18)
{
whitePixel = 0;
topLeftc = new Point(x - 18, y);
DetectNextWhiteLine(topLeftc, img);
}
}
}
}
/// <summary>
/// First creates the TopRight value via using the last pixel in x range, and using the current Y value
/// Then a Y loop is started 10 pixels down, within this loop is another X loop which scans the X range
/// If 10 consecutive white pixels are found, the TopLeft X value and the current Y value are used to map the
/// BottomLeft and BottomRight coordinates. Finally a new Point is created which starts (x = 1) and (y = currentYValue + 2)
/// The ScanPixelsLater method is then called, passing the new Point (newLocation).
///
/// </summary>
/// <param name="p">The x and y value of where the pixels were found</param>
/// <param name="img">Image being used</param>
private void DetectNextWhiteLine(Point p, Bitmap img)
{
int whitePixel = 0;
topRightc = new Point(img.Width, topLeftc.Y);
for (int y = p.Y + 10; y < img.Height; y++)
{
for (int x = p.X - 5; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
whitePixel++;
}
else
{
whitePixel = 0;
}
if (whitePixel == 10)
{
bottomLeftc = new Point(topLeftc.X, y);
bottomRightc = new Point(img.Width, y);
Cords.Add(new Coordinates(topLeftc, topRightc, bottomLeftc, bottomRightc));
Point newLocation = new Point(1, y + 2);
calls++;
ScanPixelsLater(newLocation, img); //rescan the image from new y axis
}
}
}
}
/// <summary>
/// Loops through the pixels based on the p parameter, if 15 white pixels are found, the location (x & y) is
/// passed to the DetectNextWhiteLine method which fixes the next line with a similar number of white pixels.
/// </summary>
/// <param name="p">The Point(x,y) at which to start scanning</param>
/// <param name="img"></param>
private void ScanPixelsLater(Point p, Bitmap img)
{
int whitePixel = 0;
for (int y = p.Y; y < img.Height; y++)
{
for (int x = p.X; x < img.Width; x++)
{
if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
{
whitePixel++;
}
else
{
whitePixel = 0;
}
if (whitePixel == 15)
{
bottomLeftc = new Point(topLeftc.X, y);
topLeftc = new Point(x - 15, y);
calls++;
DetectNextWhiteLine(topLeftc, img);
}
}
}
// only want this to execute after all the pixels within the entire img have been read
// possibly executing early.
DrawWhiteLines(img);
AppArgs aa = new AppArgs(true);
Change(this, aa); // custom event handler, fired behind form to update GUI
}
So, to understand why your application is hanging you need to know a little bit about how WinForm applications work.
The thread that your UI is running on also has what is called a message pump. This message pump contains messages that get passed from the operating system (and other sources) to all of the UI elements. They tell them when to change state, when to redraw themselves, etc. When you have a long running loop like yours the message pump cannot process messages. The get queued up, but never processed, and this is what it means for an application to 'hang'.
It is unlikely that your application will never recover. Your loop will eventually end and your UI will become responsive again (assuming I didn't miss an infinite loop somewhere, but I don't think that I did). However, the GDI+ GetPixel method is really very slow, and if your image is large at all that set of loops is going to take a long time to complete. You will likely have to delve into an unsafe context and obtain a pointer to the image's memory using LockBits. There are many examples of how to do that floating around here.
EDIT: After looking at your code a bit more closely it is also apparent that it is relatively inefficient. You have at least 6 levels of nested for loops going on there, so you are essentially scanning the image multiple times when only a single scan is needed.
Image processing is a resource intensive process. You need to be careful to do all of your performance intensive work as efficiently as possible.
migrated from my comments (now deleted)
It is properly not this which causes you trouble since it never comes to normal but you should never call GetPixel in a loop. It is incredible slow to use that. Instead you can use pointers or an pixel array Search for "getpixel slow" with google or stackoverflow and a large number of solutions come up.
Updated: I have looked a little bit on the code now... In the main code (ScanPixels) which is a nested for-loop, you call DetectNextWhiteLine which is also a nested for-loop and finally this calls ScanPixelsLater which is ALSO a nested for-loop. Now you potentially got a 6-level-deep nested for loop O(n^6) which calls an relatively expensive method (GetPixel). You should only iterate over the pixels a few times. This may be why it never stops because this is potential something like 1000^6*~100 instructions :)

Get current cursor lower left position so that tooltip could be displayed properly

I'm trying to display the tooltip by calling "ToolTip.Show(String, IWin32Window, Point)", but I wanted to do it like what Windows explorer does - displays the tooltip at the lower left corner of the cursor.
I could get the mouse position by "MousePosition", but how could I get its lower left corner position?
Thanks,
If nobody comes up with a better answer you can try this:
toolTip1.Show("Am I where you want me to be?", this, this.PointToClient(MousePosition).X,
this.PointToClient(MousePosition).Y + Cursor.Size.Height * 2);
Adjust the text positioning by playing with the x/y parameters. It works on my machine but I'm not sure how it would look under different settings.
ToolTip fun tip: put this line in your Form's MouseMove event.
I think Explorer puts the tooltip under the cursor's hotspot so you don't have to correct the X-position. This looked good:
private void panel1_MouseClick(object sender, MouseEventArgs e) {
int x = e.X;
int y = e.Y + Cursor.Current.Size.Height - Cursor.Current.HotSpot.Y;
toolTip1.Show("test", panel1, x, y);
}
The only way to do this is to scan the cursors MASK and find the distance between the last set pixel in the cursor mask and the cursors Y hotspot, I had to do this up today, so heres the code:
#define useUnsafe
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System;
using System.Windows.Forms;
namespace Utils
{
/// <summary>
/// Provides extension methods for the Cursor class
/// </summary>
/// <remarks>By Aaron Murgatroyd</remarks>
public static class CursorExtensionMethods
{
#region API Functions
/// <summary>
/// Contains the icon information for a Windows API icon
/// </summary>
private struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
/// <summary>
/// Gets the icon information for a Windows API icon
/// </summary>
/// <param name="hIcon">The icon to get the info for</param>
/// <param name="pIconInfo">The object to receive the info</param>
/// <returns>True on success, false on failure</returns>
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
#endregion
#region Private Static Methods
/// <summary>
/// Scans bits in bitmap data for a set or unset bit
/// </summary>
/// <param name="byteData">The pointer to the first byte of the first scanline</param>
/// <param name="start">The vertical position to start the scan</param>
/// <param name="lineInc">The number of bytes to move per line</param>
/// <param name="maxLines">The number of lines to scan</param>
/// <param name="set">True to scan for set bits, false to scan for unset bits</param>
/// <param name="fromBottom">True to scan from the bottom of the bitmap, false to scan from the top</param>
/// <returns>The number of lines scanned before a bit was found, or -1 if none found before reaching max lines</returns>
#if useUnsafe
private static unsafe int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#else
private static int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#endif
{
// Calculate the starting byte of the first scanline
#if useUnsafe
byte* lbLine = ((byte*)byteData) + (start * lineInc);
#else
int lbLine = ((int)(byteData) + (start * lineInc));
#endif
int liLine = 0;
// Use lineInc to determines bytes per line
int liBytesPerLine = (lineInc < 0 ? -lineInc : lineInc);
// If we want to search in reverse order
if (fromBottom)
{
// Move to the START of the line
lbLine += lineInc * (maxLines - 1);
// Negate the line increment
lineInc = -lineInc;
}
while (maxLines > 0)
{
// Setup the line scan
#if useUnsafe
byte* lbData = lbLine;
#else
int lbData = lbLine;
#endif
int liByte = liBytesPerLine;
// For each byte in the line
while (liByte > 0)
{
#if !useUnsafe
byte lbByte = Marshal.ReadByte((IntPtr)lbData);
#endif
// If we want set bits, and a bit is set
#if useUnsafe
if (set && *lbData != 0)
#else
if (set && lbByte != 0)
#endif
// Return the line number
return liLine;
else
// If we want unset bits and any bits arent set
#if useUnsafe
if (!set && *lbData != byte.MaxValue)
#else
if (!set && lbByte != byte.MaxValue)
#endif
// Return the line number
return liLine;
// Next byte for scan line
liByte--;
lbData++;
}
// Next scan line
liLine++;
maxLines--;
lbLine += lineInc;
}
// If all lines were scanned, return -1
if (maxLines == 0)
return -1;
else
// Return number of lines scanned
return liLine;
}
#endregion
#region Public Static Methods
/// <summary>
/// Gets the number of pixels between the Y hotspot
/// and the last physical line of a cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>
/// The number of lines between the Y hotspot
/// and the last physical line of the cursor
/// </returns>
public static int GetBaseLineHeight(this Cursor cursor)
{
return GetBaseLine(cursor) - cursor.HotSpot.Y;
}
/// <summary>
/// Gets the physical base line of the cursor, that is,
/// the distance between the top of the virtual cursor
/// and the physical base line of the cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>The number of lines between the top of the virtual cursor
/// and the physical base line of the curosr</returns>
public static int GetBaseLine(this Cursor cursor)
{
IconInfo liiInfo = new IconInfo();
if (!GetIconInfo(cursor.Handle, ref liiInfo))
return cursor.Size.Height;
Bitmap lbmpBitmap = Bitmap.FromHbitmap(liiInfo.hbmMask);
try
{
BitmapData lbdData = lbmpBitmap.LockBits(
new Rectangle(0, 0, lbmpBitmap.Width, lbmpBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
try
{
// Calculate number of lines in AND scan before any found
int liLine = ScanBits(lbdData.Scan0, 0, lbdData.Stride, cursor.Size.Height, false, true);
// If no AND scan bits found then scan for XOR bits
if (liLine == -1 && lbdData.Height == cursor.Size.Height * 2)
liLine = ScanBits(lbdData.Scan0, cursor.Size.Height, lbdData.Stride, cursor.Size.Height, true, true);
return cursor.Size.Height-liLine;
}
finally
{
lbmpBitmap.UnlockBits(lbdData);
}
}
finally
{
DeleteObject(liiInfo.hbmMask);
DeleteObject(liiInfo.hbmColor);
lbmpBitmap.Dispose();
}
}
#endregion
}
}
You can undefine the conditional define "useUnsafe" at the top so you dont have to enable unsafe code if you like, but be warned it will run slower in this mode.
So this uses extension methods, so all you have to do is add Cursor.Current.GetBaseLineHeight() to your Cursor.Position.Y and that will be the first blank line under the cursor.
ie.
Point lptBlankLineUnderCursor = new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Current.GetBaseLineHeight())

Categories