I have an application that acquires temperature data from the serial port every time that the temperature changes. The value get stored in a variable and is shown in a text box.
I would like to show the temperature versus time in a graph.
I have setup the graph as following but I don't know where to start to get the temperature line updated versus time every second.
InitializeComponent();
//Chart
chart1.ChartAreas.Add("areas");
chart1.ChartAreas["areas"].AxisX.Minimum = 0;
chart1.ChartAreas["areas"].AxisX.Interval = 1;
chart1.ChartAreas["areas"].AxisY.Minimum = 0;
chart1.ChartAreas["areas"].AxisY.Maximum = 250;
chart1.ChartAreas["areas"].AxisY.Interval = 10;
chart1.ChartAreas["areas"].AxisX.Title = "Time [s]";
chart1.ChartAreas["areas"].AxisY.Title = "Temperature [°C]";
chart1.Series.Add("Temperature");
chart1.Series["Temperature"].Color = Color.Red;
chart1.Series["Temperature"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
chart1.Titles.Add("Extruder Temperature");
//this two lines are only to see something in the graph
chart1.Series["Temperature"].Points.AddXY(0, 20);
chart1.Series["Temperature"].Points.AddXY(1, 50);
}
I think I need to update the "point" and the "value" (0, 20) by replacing the values with the variables but if I do so, I have only one point in the graph and I cannot show the previous values.
Being a beginner I would appreciate some help.
An alternative way (to refresh every second) is to use a BackgroundWorker. This question give an example. Essentially, you start a background thread that updates the view every set amount of time.
private BackgroundWorker _bw;
....
InitializeComponent();
_bw = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bw.DoWork += bw_DoWork;
_bw.ProgressChanged += bw_ProgressChanged;
_bw.RunWorkerCompleted += bw_RunWorkerCompleted;
_bw.RunWorkerAsync ("Hello to worker");
if (_bw.IsBusy) _bw.CancelAsync();
}
private void bw_DoWork (object sender, DoWorkEventArgs e)
{
if (_bw.CancellationPending) { e.Cancel = true; return; }
var dataPoints = YourConnectToSerialPortAndGetDataFunction();
_bw.ReportProgress (dataPoints);
Thread.Sleep (1000);
e.Result = dataPoints; // This gets passed to RunWorkerCompleted
}
private void bw_RunWorkerCompleted (object sender,
RunWorkerCompletedEventArgs e)
{
}
private void bw_ProgressChanged (object sender,
ProgressChangedEventArgs e)
{
//UPDATE YOUR LABEL
chart1.Series["Temperature"].Points.AddXY(0, 20);
chart1.Series["Temperature"].Points.AddXY(1, 50);
}
After you acquired new data, simply add another Datapoint:
Just to give you an idea (as you haven't posted your data receive method):
OnSerialReceive()
{
var temp = serialport.read();
var time = (int)stopwatch.Elapsed.TotalSeconds;
chart1.Series["Temperature"].Points.AddXY(time, temp);
}
And yes, as soon as you get real data into your diagram, you will want to remove your fake data. Fake data is confusing.
Related
I have a site that is extremely basic and will only ever consist of a single integer.
However the integer will actively change, I want to add onto my existing application to display what this integer is in real time.
-I've tried using a Timer and WebClient however if I put the code under InitializeComponent() the form will never load.
-Also if I put the code in Form1_Load the form will never load.
-I was successful in getting the number to display in real time by putting the code under a button_click event, but I want this code to begin as soon as the form load.
-Also when the button was first clicked the first timer sequence the label would display lat (unsure what this means)
-After the button was pressed and the timer loop began the app breaks, the number will update properly, but you cannot use any other functionality of the app, you can not move the window, you cannot close the app, etc..
private void timer_Tick(object sender, EventArgs e)
{
Timer timer = (Timer)sender;
this.Visible = false;
timer.Stop();
this.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
int c = 5;
while (c == 5)
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 5000;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
}
Please help me no clue what I am doing wrong here
I managed to fix my issue by using the following code if anyone ever has a similar question:
private void test()
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
}
}
int i = 1;
private void timer1_Tick(object sender, EventArgs e)
{
i += 1;
if (i >= 199)
{
i = 1;
timer1.Stop();
timer1.Start();
}
test();
}
timer1 was added to the winform from the toolbox, and is set to enabled with an interval of 200
Before all. I'm not so good at this and hopefully you will understand it anyway.
Im making a function in my program where it checks to see if a row in a rtb is highlighted. If not, it highlights it.
For this to work I had to use different threads to be able to access the rtb from different places. My problem is that it creates a new "delegate"/instance/thread every time the timer refreshes. I would like to remove the old thread/delegate or replace it with the new.
Because now the program crashes after a while. It's a very small program but after 40 sec i reach over 3gb ram usage.
Thanks in advance!
Haris.
Code:
private void Timer()//Timer for color refresh
{
aTimer = new System.Timers.Timer(300);
aTimer.Elapsed += new ElapsedEventHandler(Form1_Load);
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private void RefreshColor()//Refreshing the color of selected row
{
this.Invoke((MethodInvoker)delegate
{
if (richTextBox1.SelectionBackColor != Color.PaleTurquoise)
{
HighlightCurrentLine();
}
});
}
private void Form1_Load(object sender, EventArgs e)
{
Timer();
RefreshColor();
What is happening if i am not mistaken is that you are creating and starting new timers exponentially. So your forms loads, Form1_Load method is called. Form1_Load creates a new timer that when elapsed, will call Form1_Load again. As the old timer not being disposed 2 timers are running now that will both create 2 new timers. 4 timers create 4 new so there are 8, 16, 32 and so on...
Basically what you have to do is call other method on timer elapsed:
private void Timer()//Timer for color refresh
{
aTimer = new System.Timers.Timer(300);
aTimer.Elapsed += ATimer_Elapsed;//new ElapsedEventHandler(Form1_Load);
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private void ATimer_Elapsed(object sender, ElapsedEventArgs e)
{
RefreshColor();
}
private void RefreshColor()//Refreshing the color of selected row
{
this.Invoke((MethodInvoker)delegate
{
if (richTextBox1.SelectionBackColor != Color.PaleTurquoise)
{
HighlightCurrentLine();
}
});
}
private void Form1_Load(object sender, EventArgs e)
{
Timer();
RefreshColor();
Timer(); is only called ones thus creating only one timer.
I have a Windows Forms App written in C#. The idea is, that it draws a chart for 10 numbers after clicking a button. This works fine. I click the button, and I get a nice chart. However I also want to include a sort of "auto refresh" mode, where the chart is refreshed every few seconds. This would be enabled via Checkbox. Here's my code:
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
foreach (var series in ekran.Series) //clear previous values
{
series.Points.Clear();
}
for (int i = 0; i < 10; i++) //draw a chart from ten new values
{
value = rdn1.Next(0, 10); //for testing purpouses the value will be a random number a random number
ekran.Series["seria1"].Points.AddXY(i, value);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while(checkBox1.Checked) //click the chartButton every one second, when the checkbox is checked
{
//rysuj.PerformClick();
chartButton.PerformClick();
Thread.Sleep(1000);
}
}
And now for my problem. When I check the Checkbox, I will not get a chart until it finishes every iteration of the while loop. Since it's an infinite loop, I will never get my chart. If I rewrite the code to make only five iterations when the Checkbox is checked, I only get the chart for the fifth one (and after five seconds, as to be expected).
So my question is: how can I force this to draw a chart every time the button is clicked via chartButton.PerformClick()? When I click the button manually, everything works fine, it's just when I try to do it automatically, I get my problem.
EDIT
First of all,thank you for the replies. However, I'm still experiencing the same problem when using a timer. This is how my code looks now:
namespace ChartTest
{
public partial class Form1 : Form
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 1000;
}
void timer_Tick(object sender, EventArgs e)
{
timer.Enabled = false;
chartButton.PerformClick();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while (checkBox1.Checked)
{
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
}
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
ekran.Series.Clear();
var series2 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "Series2",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
this.ekran.Series.Add(series2);
for (int i = 0; i < 100; i++)
{
value = rdn1.Next(0, 10);
series2.Points.AddXY(i, value);
}
}
}
}
Sorry for being a total noob, but I have no idea, what am I doing wrong this time.
This is exactly what a Timer is for. Have the checkbox start/stop or enable/disable the timer, and handle the Timer.Tick event to redraw your chart. In your case, the event handler could simply call chartButton.PerformClick(), or insert whatever code the PerformClick() does.
ETA: If the chart refresh is not instant, you will probably want to push it off to a separate thread. If it's instant, there's not really any need to deal with the threading though.
I would go the route of using a thread with combination of checkbox's checkChange() event. Essentially this will allow your application to keep running while the update code will execute periodically. The refresh is determined by the sleep time, not your manual click or any other value.. Example below on how I to do this:
Thread refreshThread = null;
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (refreshThread == null) //No thread running, assume it starts this way
{
refreshThread = new Thread(chartRefresh);
refreshThread.Start();
}
else //Thread is running, must terminate
{
refreshThread.Abort();
refreshThread = null;
}
}
private void chartRefresh()
{
while (true)
{
//code to refresh chart
Thread.Sleep(10000);
}
}
I am trying to make a very simple logic game. The idea is to see a matrix with a certain number of colored squares(buttons) then to hide them and the player has to click the colored squares. So I need a 2 second delay between the painting the squares/buttons and returning the original colors. All the code is implemented in a button_click event.
private void button10_Click(object sender, EventArgs e)
{
int[,] tempMatrix = new int[3, 3];
tempMatrix = MakeMatrix();
tempMatrix = SetDifferentValues(tempMatrix);
SetButtonColor(tempMatrix, 8);
if (true)
{
Thread.Sleep(1000);
// ReturnButtonsDefaultColor();
}
ReturnButtonsDefaultColor();
Thread.Sleep(2000);
tempMatrix = ResetTempMatrix(tempMatrix);
}
This is the whole code, but what I need is to have some delay between calling SetButtonColor() and ReturnButtonsDefaultColor(). All my experiments with Thread.Sleep() meet no success till now. I get a delay at some point, but the colored squares/buttons are never shown.
You don't see the buttons change color because the Sleep call prevents messages from being processed.
Probably the easiest way to handle this is with a timer. Initialize the timer with a 2 second delay and make sure that it's disabled by default. Then, your button click code enables the timer. Like this:
private void button10_Click(object sender, EventArgs e)
{
// do stuff here
SetButtonColor(...);
timer1.Enabled = true; // enables the timer. The Elapsed event will occur in 2 seconds
}
And your timer's Elapsed event handler:
private void timer1_TIck(object sender, EventArgs e)
{
timer1.Enabled = false;
ResetButtonsDefaultColor();
}
You can always use TPL, its the simplest solution, because you don't need to handle thread contexts or fragment your code.
private async void button10_Click(object sender, EventArgs e)
{
int[,] tempMatrix = new int[3, 3];
tempMatrix = MakeMatrix();
tempMatrix = SetDifferentValues(tempMatrix);
SetButtonColor(tempMatrix, 8);
if (true)
{
await Task.Delay(2000);
// ReturnButtonsDefaultColor();
}
ReturnButtonsDefaultColor();
await Task.Delay(2000);
tempMatrix = ResetTempMatrix(tempMatrix);
}
Use a Timer. Instead of your Thread.Sleep Start() the timer, in the tick event call ReturnButtonsDefaultColor(). You could use a second timer instead of the second Thread.Sleep or save some sort of state and use it in the tick event.
You can use Tasks:
private void button10_Click(object sender, EventArgs e)
{
int[,] tempMatrix = new int[3, 3];
tempMatrix = MakeMatrix();
tempMatrix = SetDifferentValues(tempMatrix);
SetButtonColor(tempMatrix, 8);
Task.Factory.StartNew(
() =>
{
if (true)
{
Thread.Sleep(1000);
// ReturnButtonsDefaultColor();
}
ReturnButtonsDefaultColor(); //Need to dispatch that to the UI thread
Thread.Sleep(2000);
tempMatrix = ResetTempMatrix(tempMatrix); //Probably that as well
});
}
Dispatching in WPF is different from Winforms, it should be easy enough to google ;)
i am developing a project (WPF) and i have a Datagrid the load more than 5000 records from the database so i used a BackgroundWorker to advice the user the data is loading but it is so slow , i need to wait almost 2 minutes to load the data from the database,instead if i don't use the BackgroundWorker i need to wait just 3 second to load the data in the Datagrid.
Here i write down the code snippet that i use for the BackgroundWorker :
private void RunWorker()
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker senderWorker = sender as BackgroundWorker;
dc = new DataClasses1DataContext();
var query = from c in dc.Contact_DDBB_Xavis
select
new
{
c.ContactID,
c.Continent,
c.Country,
c.City,
c.PostalCode,
c.CompanyName,
c.UserCreated,
c.DateCreated,
c.UserModified,
c.DateModified
};
if (query.Count() > 0)
{
for (int i = 0; i < query.Count(); i++)
{
int progressInPercent = (int)(((decimal)(i + 1) / (decimal)query.Count()) * 100);
worker.ReportProgress(progressInPercent, i);
System.Threading.Thread.Sleep(10);
e.Result = query.ToList();
}
}
if (senderWorker.CancellationPending)
{
e.Cancel = true;
}
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
this.dataGrid.DataContext = e.Result;
backGround.Visibility = Visibility.Collapsed;
duracel.Visibility = Visibility.Collapsed;
txtBackWORK.Visibility = Visibility.Collapsed;
}
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
for (double i = 0.0; i < e.ProgressPercentage; i++)
{
duracel.pthFiller.Width = 0;
double max = 312;
max = (double)e.ProgressPercentage;
duracel.pthFiller.Width = e.ProgressPercentage * 3.12;
duracel.txtStatus.Text = e.ProgressPercentage + " %";
txtBackWORK.Text = String.Format("Loading " + e.ProgressPercentage + " %");
}
}
now i don't know if there is something wrong in my code so i ask you some advice to how load faster the data from database without wait so long time.
Thanks for your attention.
Have a good time.
Cheers
Each time you call query.Count(), you're running another SQL query.
You should call Count() once, and store it in a local variable.
Also, you should only call `ReportProgress if the progress actually changed. (There's no point in calling it 1,000 times)
Umm... you are calling System.Threading.Thread.Sleep() in your code, in a loop, which appears to be calling the query multiple times.
Your update code also seems to be needlessly looping from 0 to the current percentage... not sure why?
Get rid of the whole progress indicator block as it exists. You are iterating over every single record, pausing your thread, re-calling the query and assigning it to e.Result and generally incurring overhead 5,000 times after your data has already loaded. This is useless.
If the data loads in a matter of seconds, show a marquee progressbar on the UI thread and everyone will be happy.