Automatically increment filename - c#

Right now I have this code:
int number = 0;
DirectoryInfo di = new DirectoryInfo(scpath + #"Screenshots\");
if (di.Exists) {
} else {
di.Create();
}
int screenWidth = Screen.GetBounds(new Point(0, 0)).Width;
int screenHeight = Screen.GetBounds(new Point(0, 0)).Height;
Bitmap bmpScreenShot = new Bitmap(screenWidth, screenHeight);
Graphics gfx = Graphics.FromImage((Image)bmpScreenShot);
gfx.CopyFromScreen(0, 0, 0, 0, new Size(screenWidth, screenHeight));
bmpScreenShot.Save(di + "Screenshot_" + number, ImageFormat.Jpeg);
Program takes a screenshot (which works) and saves it. What I want to do is to have the program check and see if a screenshot exists ("Screenshot_*") and to create it if it doesn't. If it does, increment file name till it hits a number that hasn't been used at the end of "Screenshot_"
Not sure how to go about this given that it's more with files and incrementing. I'm thinking about a for loop but I'm playing with it now.

Getting the name of a file that does not exist sounds like a job for a method.
string IndexedFilename(string stub, string extension)
{
int ix = 0;
string filename = null;
do {
ix++;
filename = String.Format("{0}{1}.{2}", stub, ix, extension);
} while (File.Exists(filename));
return filename;
}
There is a race condition if you call this from multiple threads.
Assuming you have just one app and one thread in the app asking for filenames, then this ought to work.
The code to use the method looks like this:
string di = Path.Combine(scpath, "Screenshots");
if (!Directory.Exists(di) {
Directory.Create(di);
}
int screenWidth = Screen.GetBounds(new Point(0, 0)).Width;
int screenHeight = Screen.GetBounds(new Point(0, 0)).Height;
Bitmap bmpScreenShot = new Bitmap(screenWidth, screenHeight);
Graphics gfx = Graphics.FromImage((Image)bmpScreenShot);
gfx.CopyFromScreen(0, 0, 0, 0, new Size(screenWidth, screenHeight));
string filename = IndexedFilename(Path.Combine(di,"Shot_"),"jpg");
bmpScreenShot.Save(filename, ImageFormat.Jpeg);

Like #Quintin said, use datetime for filename:
string filename = Path.Combine(
di.FullName,
String.Format("{0}.jpg", DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss")));
bmpScreenShot.Save(filename, ImageFormat.Jpeg);

This is a possibility
string[] files = System.IO.Directory.GetFiles(scpath, "Screenshot_*.jpg");
string baseName = Path.Combine(scpath, "Screenshot_");
string filename;
int i = 0;
do {
filename = baseName + ++i + ".jpg";
} while (files.Contains(filename));
The advantage of this approach is that the file system is queried only once. If the file number gets large, consider adding the file names to a hash set to speed up the checks even more:
var files = new HashSet<string>(Directory.GetFiles(scpath, "Screenshot_*.jpg"));

Instead of using a number as a way to differentiate between screenshots use a timestamp:
string currentDT = string.Format("{0:D4}.{1:D2}.{2:D2}-{3:D2}.{4:D2}.{5:D2}",
DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day,
DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second)
bmpScreenShot.Save(di + "Screenshot_" + currentDT, ImageFormat.Jpeg);

I'd use GUID...
try{
bmpScreenShot.Save(di + "Screenshot_" + Guid.NewGuid().ToString(), ImageFormat.Jpeg);
}catch(Exception e)
{
//handle the problems here, for example if file already exists, try again
}
This should work well until you run out of unique GUIDs...

public static string MakeUniqueFileName(string file)
{
string dir = Path.GetDirectoryName(file);
string fn;
for (int i = 0; ; ++i)
{
fn = Path.Combine(dir, string.Format(file, i));
if (!File.Exists(fn))
return fn;
}
}
Use it like this:
string file = scpath + #"Screenshots\" + "Screenshot_{0}.png";
bmpScreenShot.Save(MakeUniqueFileName(file), ImageFormat.Jpeg);

This will create output_0.jpg output_1.jpg ... output_n.jpg:
int filecount = 0;
string path = Environment.CurrentDirectory;
for (int i = 0; File.Exists(path + #"\output_" + i + ".jpg"); i++)
{
filecount = i + 1;
}
File.Create(path + #"\output_" + filecount + ".jpg");

private static string GetUniqueFile(string path, string file, string ext)
{
int filecount = 0;
int i = 0;
for (i = 0; File.Exists(path + "\\" + file + "_" + i + "." + ext); i++)
{
filecount = i + 1;
}
return path + "\\" + file + "_" + i.ToString() + "." + ext;
}

In case the directory with the screenshots contains many images, it might be beneficial to find the next available filename using binary search. This way the File.Exists method will be called far fewer times than doing an incremental search.
/// <summary>
/// Performs a binary search in the Int32 range, and returns the first element
/// that satisfies a condition.
/// </summary>
public static TElement BinarySearchFirst<TElement>(
Func<int, TElement> selector,
Predicate<TElement> predicate,
int start = 1)
{
ArgumentNullException.ThrowIfNull(selector);
ArgumentNullException.ThrowIfNull(predicate);
if (start < 0) throw new ArgumentOutOfRangeException(nameof(start));
long lo = start;
long hi = 1;
(TElement Value, bool HasValue) maxFound = default;
// First stage, find an approximate upper limit of the search space.
while (hi < Int32.MaxValue)
{
hi = Math.Min(hi * 10, Int32.MaxValue);
if (hi < start) continue;
TElement item = selector((int)hi);
bool accepted = predicate(item);
if (accepted)
{
maxFound = (item, true);
hi--;
break;
}
lo = hi + 1;
}
// Second stage, perform binary search between lo and hi.
while (lo <= hi)
{
long mid = lo + ((hi - lo) >> 1);
TElement item = selector((int)mid);
bool accepted = predicate(item);
if (accepted)
{
maxFound = (item, true);
hi = mid - 1;
}
else
lo = mid + 1;
}
if (maxFound.HasValue) return maxFound.Value;
throw new InvalidOperationException("Element not found in the Int32 range.");
}
Usage example:
string pathFound = BinarySearchFirst(
i => Path.Combine(#"C:\Screenshots", $"Screenshot-{i}.png"),
path => !File.Exists(path));
In a folder with 200 screenshots, the above code will check for the existence of the files below:
Screenshot-10.png
Screenshot-100.png
Screenshot-1000.png
Screenshot-550.png
Screenshot-325.png
Screenshot-212.png
Screenshot-156.png
Screenshot-184.png
Screenshot-198.png
Screenshot-205.png
Screenshot-201.png
Screenshot-199.png
Screenshot-200.png
...before returning the value "C:\Screenshots\Screenshot-201.png" as the result.
Online demo.

Related

New line after foreach loop

This code works as it should
foreach (char c in partToDecode)
{
var sum = c - 48;
if (sum > 40)
{
sum = sum - 8;
}
string suma = Convert.ToString(sum, 2).PadLeft(6, '0');
File.AppendAllText(#"C:\Users\jakub\Pulpit\binary.txt", suma);
}
We're inside invisible for i loop (i meant that foreach and this newline is inside of for loop named I) so I need a new line in for loop after foreach loop.
This code isn't working as i wish to, i've got a new line at the beginning and in 2nd line a plain text, the desire is to have a new line after each finish of the foreach loop
string sumaaa = Convert.ToString(sum, 2).PadLeft(6, '0');
File.AppendAllText(#"C:\Users\jakub\Pulpit\binary.txt", sumaaa);
}
FileStream stream = new FileStream(#"C:\Users\jakub\Pulpit\binary.txt", FileMode.Open);
StreamWriter sw = new StreamWriter(stream);
sw.Write("\n");
sw.Close();
}
This is kinda small, after that I have a code for splitting bits to convert small pieces of it to decimal. It worked earlier but i inserted it inside a comment and now it writes nothing. This loop stands alone after all, runs after clicking ok, then i have a form2 which is displayed always, no errors.
for (var k = 0; k < binlines.Count(); k++)
{
var binline = binlines[k];
if (binline != null) //in case it was an empty line error (it's not)
{
string partmessage = binline.Substring(0, 6);
string partdecoded = Convert.ToInt32(partmessage, 2).ToString();
string partmessage2 = binline.Substring(6, 2);
string partdecoded2 = Convert.ToInt32(partmessage2, 2).ToString();
string partmessage3 = binline.Substring(8, 30);
string partdecoded3 = Convert.ToInt32(partmessage3, 2).ToString();
string so on to 15
File.AppendAllText(#"C:\Users\jakub\Pulpit\decimal.txt", partdecoded + "," + partdecoded2 + "," partdecoded3 + "," + partdecoded4...)
}
else
{
}
Now i've got an exception of out of range "index and length must reference to location in string"
I suggest not using a csv format to start with, and write your native binary data to disk. In that case you could just use BinaryReader and BinaryWriter that makes this trivial.
Also it probably worth making some healper methods or a class that can take care of the reading of binary data to int.
Given
public struct BinReader
{
private readonly string _line;
private int _index;
public BinReader(string line)
{
_line = line;
_index = 0;
}
public int GetInt(int length)
{
var result = Convert.ToInt32(_line.Substring(_index, length), 2);
_index += length;
return result;
}
}
Usage
// some test data
var binLines = new List<string>()
{
Convert.ToString(1, 2) + Convert.ToString(2, 2) + Convert.ToString(3, 2) + Convert.ToString(4, 2) + Convert.ToString(5, 2),
Convert.ToString(1, 2) + Convert.ToString(2, 2) + Convert.ToString(3, 2) + Convert.ToString(4, 2) + Convert.ToString(5, 2),
};
// write to file
using (var sw = new BinaryWriter(new FileStream("SomeFileName.dat", FileMode.Create, FileAccess.ReadWrite)))
{
foreach (var reader in binLines.Select(line => new BinReader(line)))
{
sw.Write(reader.GetInt(1));
sw.Write(reader.GetInt(2));
sw.Write(reader.GetInt(2));
sw.Write(reader.GetInt(3));
sw.Write(reader.GetInt(3));
}
}
// read from file
using (var br = new BinaryReader(new FileStream("SomeFileName.dat", FileMode.Open, FileAccess.ReadWrite)))
{
foreach (var reader in binLines.Select(line => new BinReader(line)))
{
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadInt32());
}
}
Proof it works
1
2
3
4
5
1
2
3
4
5

Merge / optimize substring statements

How can I optimize this code snippet?
string page = wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB");
int pos;
while ((pos = page.IndexOf("/watch?v=")) > 0) {
page = page.Substring(pos);
page = page.Substring(page.IndexOf("video-time"));
page = page.Substring(page.IndexOf("aria-label"));
page = page.Substring(page.IndexOf(#"\u003e") + 6);
string vt = page.Substring(0, page.IndexOf(#"\u003c"));
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("title=") + 1);
page = page.Substring(page.IndexOf("\\\"") + 2);
string tt = page.Substring(0, page.IndexOf("\\\" aria-describedby="));
}
Sadly, I can't just skip some of the Substring lines as this seems to be the only way to find the right occurance of vt and tt.
Since Substring always returns a new string I tried a solution with StringBuilder:
System.Text.StringBuilder sb=new System.Text.StringBuilder(wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB"));
int pos;
while ((pos = sb.ToString().IndexOf("/watch?v=")) > 0) {
sb.Remove(0,pos);
sb.Remove(0,sb.ToString().IndexOf("video-time"));
sb.Remove(0,sb.ToString().IndexOf("aria-label"));
sb.Remove(0,sb.ToString().IndexOf(#"\u003e") + 6);
string vt =sb.ToString(0,sb.ToString().IndexOf(#"\u003c"));
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("title=") + 1);
sb.Remove(0,sb.ToString().IndexOf("\\\"") + 2);
string tt =sb.ToString(0,sb.ToString().IndexOf("\\\" aria-describedby="));
}
I was suprised to find out that this solution, although it doesn't look like due to all the ToString(), is indeed slightly faster.
Now, is there a way to optimize this even further? Maybe even make it look better?
As suggested by #harold
string page = wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB");
int pos;
while ((pos = page.IndexOf("/watch?v=")) > 0) {
int subPos=pos;
subPos=page.IndexOf("video-time",subPos);
subPos=page.IndexOf("aria-label",subPos);
subPos=page.IndexOf(#"\u003e",subPos);
subPos+=6;
string vt=page.Substring(subPos,(subPos=page.IndexOf(#"\u003c",subPos)));
subPos=page.IndexOf("title=",subPos);
subPos++;
subPos=page.IndexOf("title=",subPos);
subPos++;
subPos=page.IndexOf("title=",subPos);
subPos=page.IndexOf("\\\"",subPos);
subPos+=2;
string tt=page.Substring(subPos,(subPos=page.IndexOf("\\\" aria-describedby=", subPos)));
page=page.Substring(subPos);
}
Seems to be quite a lot faster than using StringBuilder with ToString()
As suggested by #dbc, using these extension methods
System.Text.StringBuilder sb=new System.Text.StringBuilder(wc.DownloadString("https://www.youtube.com/browse_ajax?action_continuation=1&continuation=4qmFsgI8EhhVQ2ZXdHFQeUJNR183aTMzT2VlTnNaWncaIEVnWjJhV1JsYjNNZ0FEQUJPQUZnQVdvQWVnRTB1QUVB"));
int pos;
while ((pos = sb.ToString().IndexOf("/watch?v=")) > 0) {
sb.Remove(0,pos);
sb.Remove(0,sb.IndexOf("video-time"));
sb.Remove(0,sb.IndexOf("aria-label"));
sb.Remove(0,sb.IndexOf(#"\u003e") + 6);
string vt =sb.ToString(0,sb.IndexOf(#"\u003c"));
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("title=") + 1);
sb.Remove(0,sb.IndexOf("\\\"") + 2);
string tt =sb.ToString(0,sb.IndexOf("\\\" aria-describedby="));
}
Should be faster than using StringBuilder with ToString(), my test results are a bit jerky here.

.PadLeft not properly inserting padding characters

I have been attempting to write a piece of code for a file manifesting tool. I am attempting to use .PadLeft to shift the characters to replace the directory text.
int count = 0;
temp1.AddRange(Directory.GetFiles(path));
foreach (string file in temp1)
{
string temp = "\\";
int padCount = (path.Length + file.Length + temp.Length + 1); //+1 because it counts temp as only one character.
temp.PadLeft(padCount, '-');
temp2.Add(temp + Path.GetFileName(temp1[count]));
count++;
}
return temp2;
The input is a directory on the drive. The snippet above is part of the code that reads the files and puts them a in List<>.
Wanted Output
Z:\Documents
\~~Manifest.txt
\Anti-Tau.png
\Anti-Tau.rosz
\Army.png
Actual Output
Z:\Documents
\~~Manifest.txt
\Anti-Tau.png
\Anti-Tau.rosz
The output doesn't reflect that I am padding the left of the temp string. I have attempted changing the temp string, but that does not seem to do anything.
I have been watching VS2012's locals window and that seems to be the only thing that isn't working as intended.
You need to assign the value back to temp:
temp = temp.PadLeft(padCount, '-');
Here is a complete working method:
public List<string> GetFileList(string path)
{
int count = 0;
var temp1 = Directory.GetFiles(path);
List<string> temp2 = new List<string>();
foreach (string file in temp1)
{
string temp = "\\";
int padCount = (path.Length + file.Length + temp.Length + 1); //+1 because it counts temp as only one character.
temp = temp.PadLeft(padCount, '-');
temp2.Add(temp + Path.GetFileName(temp1[count]));
count++;
}
return temp2;
}
Try this:
String path = "D:\\";
List<String> temp1 = new List<string>();
List<String> temp2 = new List<string>();
temp1.AddRange(Directory.GetFiles(path));
Console.WriteLine(path);
foreach (string file in temp1)
{
string temp = "\\";
int padCount = path.Length+temp.Length;//(path.Length + file.Length + temp.Length + 1); //+1 because it counts temp as only one character.
temp=temp.PadLeft(padCount, '-');
temp2.Add(temp + Path.GetFileName(file));
Console.WriteLine(temp + Path.GetFileName(file));
}

Unable to plot multiple lines in Fastline chart

I am trying to have a Fastline Chart (type chosen as I get lots of point to plot). I want to plot in the data I just got plus an average on the same chart. Should be easy but I keep getting an error "An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll".
The function is below, if I comment out the second series called [AverageTime"] I compile and work with one line.
Please note the function is async (.net 4.5) which I use for "await Task.Delay(Int32.Parse(txtbx_timing_delay.Text));". Not sure if this has anything to do with it as I presume I have a thread I am waiting for.
Any ideas on how to get round the issue?
private async void btn_timing_send_Click(object sender, EventArgs e)
{
int recursive_counter = 0, num = 0, num2 = 0;
double total_time = 0, average_time = 0, min_time = 0, max_time = 0, ave_plot = 0;
num2 = Int32.Parse(txtbx_iterations.Text); // number of required iterations
/*
* Just a word on timings - Windows is bad at doing timings in lower Microseconds and below so we need to do a few things to try and get
* accurate timings.
*
* As the general rule, when testing comms with completion time in the range of several microseconds it's recommended to implement
* the loop running 10,000...100,000 iterations in order to increase the accuracy of the measurement
*/
string str1 = "";
Stream stm = tcpclnt.GetStream();
byte[] payload;
string ID = txtbx_timing_send_data.Text;
str1 = "No Connection";
ID = ID.ToCharArray().Aggregate("", (result, c) => result += ((!string.IsNullOrEmpty(result) && (result.Length + 1) % 3 == 0) ? " " : "") + c.ToString());//add space every two chars to make hex
payload = ID.Split().Select(s => Convert.ToByte(s, 16)).ToArray(); //split the bytes into the array
if (ckbx_plot.Checked)
{
chrt_timings.Series["ResponseTimes"].ChartType = SeriesChartType.FastLine; //set type
chrt_timings.Series["ResponseTimes"].Color = Color.Blue; //set colour
chrt_timings.Series["AverageTime"].ChartType = SeriesChartType.FastLine; //set type
// chrt_timings.Series["AverageTime"].Color = Color.Red; //set colour
// chrt_timings.Series["AverageTime"].BorderDashStyle = ChartDashStyle.Dash;
chrt_timings.Legends.Clear(); // We do not need a legend
chrt_timings.ChartAreas[0].AxisX.IsMarginVisible = false;
}
do
{
try
{
Stopwatch timer = Stopwatch.StartNew();
long frequency = Stopwatch.Frequency;
long nanosecPerTick = (1000L * 1000L * 1000L) / frequency;
long startTick = timer.ElapsedTicks; //start of timed section
stm.Write(payload, 0, payload.Length);
byte[] input = new byte[tcpclnt.ReceiveBufferSize];
int k = stm.Read(input, 0, tcpclnt.ReceiveBufferSize);
Array.Resize(ref input, k); //crop the array to the amount of items it read in
str1 = string.Join(" ", input.Select(b => string.Format("{0:X2} ", b))); //format as hex bytes
long stopTick = timer.ElapsedTicks; //end of timed section
var timestamp = Convert.ToDouble((stopTick - startTick) * nanosecPerTick) / 1000000;
timer.Reset();
rchtxbox_timings.SelectionColor = Color.LimeGreen;
rchtxbox_timings.AppendText(str1 + "\r");
rchtxbox_timings.SelectionColor = Color.Yellow;
rchtxbox_timings.AppendText(timestamp + "ms\r\r");
rchtxbox_timings.ScrollToCaret();
if (num == 0) min_time = timestamp;
if (num2 > 1)
{
total_time = total_time + timestamp;
if (max_time < timestamp) max_time = timestamp;
if (min_time > timestamp) min_time = timestamp;
}
if (chkBx_LogData.Checked)
{
using (StreamWriter sw = new StreamWriter(timing_filename, true))
{
str1 = str1.Replace(" ", ""); //take out the spaces
sw.WriteLine(str1 + "," + timestamp.ToString() + "\r");
}
}
//Plot graph if required
if (ckbx_plot.Checked)
{
ave_plot = timestamp / num;
if (ckbx_restrict_graph.Checked)
{
if (chrt_timings.Series["ResponseTimes"].Points.Count() >= Convert.ToInt16(txtbx_axispoints.Text)) chrt_timings.Series["ResponseTimes"].Points.RemoveAt(0);
chrt_timings.Series["ResponseTimes"].Points.Add(timestamp);
chrt_timings.Series["ResponseTimes"].ToolTip = timestamp.ToString();
// if (chrt_timings.Series["AverageTime"].Points.Count() >= Convert.ToInt16(txtbx_axispoints.Text)) chrt_timings.Series["Average Time"].Points.RemoveAt(0);
// chrt_timings.Series["AverageTime"].Points.Add(ave_plot);
chrt_timings.ResetAutoValues();
}
else
{
recursive_counter++;
chrt_timings.Series["ResponseTimes"].Points.AddXY(recursive_counter, timestamp);
chrt_timings.Series["ResponseTimes"].ToolTip = timestamp.ToString();
// chrt_timings.Series["AverageTime"].Points.AddXY(ave_plot, timestamp);
}
}
num = num + 1;
timestamp = 0;
await Task.Delay(Int32.Parse(txtbx_timing_delay.Text));
}
catch (Exception d)
{
rchTxtBx_output.AppendText("red..... " + d.StackTrace);
}
} while (num2 > num);
if (num2 > 1)
{
//write out min, max and ave times
average_time = total_time / num;
rchtxbox_timings.SelectionColor = Color.LightBlue;
rchtxbox_timings.AppendText("\rMinimum Time = " + min_time + "\r");
rchtxbox_timings.SelectionColor = Color.LightBlue;
rchtxbox_timings.AppendText("Maximum Time = " + max_time + "\r");
rchtxbox_timings.SelectionColor = Color.LightBlue;
rchtxbox_timings.AppendText("Average Time = " + average_time + "\r\r");
rchtxbox_timings.ScrollToCaret();
}
}
Thanks for help, after much head scratching I solved the issue. I missed one small point and that was I did not have the second series, Series["AverageTime"], declared as a member of the charting canvas.
To fix this I went to the Properties then click on collection and Add the member, AverageTime. Once that was done I could draw its points to the Chart Canvas.
So now I initialise the lines below
if (ckbx_plot.Checked)
{
chrt_timings.Series["ResponseTimes"].ChartType = SeriesChartType.FastLine; //set type
chrt_timings.Series["ResponseTimes"].Color = Color.Blue; //set colour
chrt_timings.Series["AverageTime"].ChartType = SeriesChartType.FastLine; //set type
chrt_timings.Series["AverageTime"].Color = Color.Red; //set colour
chrt_timings.Series["AverageTime"].BorderDashStyle = ChartDashStyle.Dash;
chrt_timings.Legends.Clear(); // We do not need a legend
chrt_timings.ChartAreas[0].AxisX.IsMarginVisible = false;
}
and now I add the data
if (ckbx_restrict_graph.Checked)
{ //shows just set number of points, add a new point remove an old point
if (chrt_timings.Series["ResponseTimes"].Points.Count() >= Convert.ToInt16(txtbx_axispoints.Text)) chrt_timings.Series["ResponseTimes"].Points.RemoveAt(0);
chrt_timings.Series["ResponseTimes"].Points.Add(timestamp);
chrt_timings.Series["ResponseTimes"].ToolTip = timestamp.ToString();
if (chrt_timings.Series["AverageTime"].Points.Count() >= Convert.ToInt16(txtbx_axispoints.Text)) chrt_timings.Series["Average Time"].Points.RemoveAt(0);
chrt_timings.Series["AverageTime"].Points.Add(ave_plot);
chrt_timings.ResetAutoValues();
}
else
{ //squash all points onto the same canvas
recursive_counter++;
chrt_timings.Series["ResponseTimes"].Points.AddXY(recursive_counter, timestamp);
chrt_timings.Series["ResponseTimes"].ToolTip = timestamp.ToString();
chrt_timings.Series["AverageTime"].Points.AddXY(ave_plot, timestamp);
}

MODI MiSelectRects gets coordinates wrong

I have a Windows Form app that, when executed, launches Firefox, grabs the process and handle of the window, and does a screen capture of Firefox, saves it to disk (temp.bmp) and calls ProcessGetWindow. I'm basically using MiSelectRects in MODI to capture the rectangle around the word that I'm looking for, and then I use AutoIT to mouse click on the word.
The problem is that my coordinates are off by about 10 pixels from the top.
Any ideas what might be wrong? Here's the function that does the processing. I have commented out the AutoIT processing, and I'm just debugging with a MessageBox to show me the actual coordinates. I then confirm with AutoIT's Window Info tool and it's definitely off... am I doing something wrong or is there something screwed up with MODI?
public void ProcessGetWindow(Bitmap image)
{
Document modiDoc = null;
MiDocSearch modiSearch = null;
IMiSelectableItem modiTextSel = null;
MiSelectRects modiSelectRects = null;
MiSelectRect modiSelectRect = null;
MiRects modiRects = null;
int intSelInfoPN;
string intSelInfoTop;
int intSelInfoBottom;
string intSelInfoLeft;
int intSelInfoRight;
// Load an existing image file.
modiDoc = new Document();
modiDoc.Create(#"C:\\temp.bmp");
// Perform OCR.
modiDoc.Images[0].OCR();
// Search for the selected word.
modiSearch = new MiDocSearch();
modiSearch.Initialize(modiDoc, "Click Me", 0, 0, false, false);
modiSearch.Search(null, ref modiTextSel);
try
{
modiSelectRects = modiTextSel.GetSelectRects();
}
catch (COMException)
{
MessageBox.Show("Me thinks that the OCR didn't work right!");
}
foreach (MiSelectRect mr in modiSelectRects)
{
//intSelInfoPN = mr.PageNumber.ToString();
intSelInfoTop = mr.Top.ToString();
//intSelInfoBottom = mr.Bottom;
intSelInfoLeft = mr.Left.ToString();
//intSelInfoRight = mr.Right;
/*AutoItX3 auto = new AutoItX3();
auto.AutoItSetOption("MouseCoordMode", 2);
auto.MouseClick("", intSelInfoLeft, intSelInfoTop, 1, 80);*/
MessageBox.Show("Coordinates: " + intSelInfoLeft + ", " + intSelInfoTop, "Coordinates", MessageBoxButtons.OK);
}
//string textResult = modiTextSel.Text;
//MessageBox.Show(textResult, "Search Results", MessageBoxButtons.OK);
// Close this dialog.
Application.Exit();
}
I am using the same program to find the location.
int centerwidth = (intSelInfoRight - intSelInfoLeft)/2;
centerwidth = intSelInfoLeft + centerwidth;
int centerheight = (intSelInfoBottom - intSelInfoTop)/2;
centerheight = centerheight + intSelInfoTop;
u can find the exact middle point of the text using it.
But this programs always gives the location of the 1st occurence of the word and not for the next occurences. Please let me know how to find the location of the text at all occurences.
I'm not familiar with the tools presented, but from what I read the GetSelectRects function returns a bounding rectangle, that is the smallest rectangle that contains the whole selection, in this case the word you searched for. I believe what happens is that you're clicking the corner of the bounding rectangle instead of in the middle, where the word is.
Calculate the co-ordinates for the center of the rectangle and try clicking that:
int height = mr.Bottom - mr.Top;
int width = mr.Right - mr.Left;
AutoItX3 auto = new AutoItX3();
auto.AutoItSetOption("MouseCoordMode", 2);
auto.MouseClick("", width/2, height/2, 1, 80);
MODI.Document modiDoc = null;
MODI.MiDocSearch modiSearch = null;
MODI.IMiSelectableItem modiTextSel = null;
MODI.MiSelectRects modiSelectRects = null;
MODI.MiSelectRect modiSelectRect = null;
MODI.MiRects modiRects = null;
int intSelInfoPN;
int intSelInfoTop;
int intSelInfoBottom;
int intSelInfoLeft;
int intSelInfoRight;
// Load an existing image file.
modiDoc = new MODI.Document();
modiDoc.Create(#"C:\Users\h117953\Desktop\OCR\1.jpg");
// Perform OCR.
//modiDoc.Images[0].OCR();
//MODI.Image image = (MODI.Image)modiDoc.Images[0];
modiDoc.OCR(MiLANGUAGES.miLANG_ENGLISH);
MODI.Image modiImage = (modiDoc.Images[0] as MODI.Image);
//string ocrtext = #"C:\Users\h117953\Desktop\OCR\Sample.txt";
//File.WriteAllText(ocrtext, modiImage.Layout.Text);
// Search for the selected word.
//int wordindex
modiSearch = new MODI.MiDocSearch();
//date to search
modiSearch.Initialize(modiDoc, "Deer", 0, 2, false, false);
modiSearch.Search(null, ref modiTextSel);
if (modiTextSel == null)
{
Response.Write("\nText not found \n");
}
else
{
Response.Write("\nText is found \n");
try
{
modiSelectRects = modiTextSel.GetSelectRects();
}
catch (Exception)
{
Response.Write("Me thinks that the OCR didn't work right!");
}
int centerwidth = 0;
int centerheight = 0;
foreach (MODI.MiSelectRect mr in modiSelectRects)
{
//intSelInfoPN = mr.PageNumber.ToString();
intSelInfoTop = mr.Top;
intSelInfoBottom = mr.Bottom;
intSelInfoLeft = mr.Left;
intSelInfoRight = mr.Right;
// MessageBox.Show("Coordinates: " + intSelInfoLeft + ", " + intSelInfoTop, "Coordinates", MessageBoxButtons.OK);
// MessageBox.Show("Coordinates: " + intSelInfoRight + ", " + intSelInfoBottom, "Coordinates", MessageBoxButtons.OK);
centerwidth = (intSelInfoRight - intSelInfoLeft) / 2;
centerwidth = intSelInfoLeft + centerwidth;
centerwidth = (intSelInfoBottom - intSelInfoTop) / 2;
centerheight = centerheight + intSelInfoTop;
//MessageBox.Show("Coordinates: " + centerwidth + ", " + centerheight, "Coordinates", MessageBoxButtons.OK);
Response.Write("the Widht and Height co-ordinates are (Width,Height)= ({0},{1})" + centerwidth + ","+ centerheight);
}

Categories

Resources