Hi
I am working in windows 7 phone based app using silverlight. I have following methods in one of my UI classes, GameScreen.xaml. I am calling startTimer method in the constructor GameScreen. Problem is that when the updateTime method is called and
timeLabel.Text = "Time left: 00 : " + time;
line is executed, the program throws UnauthorizedAccessException on time variable.
private void startTimer()
{
timeThread = new Thread(new ThreadStart(startThread));
timeThread.Start();
}
public void startThread()
{
while (timeLeft > 0)
{
Thread.Sleep(1000);
updateTime();
if (timePassed % 10 == 0)
{
findNextBGResource();
changeBackgroundScene();
}
}
}
private void updateTime()
{
// update the view
String time = timeLeft.ToString();
if (timeLeft < 10)
{
time = "0" + time;
}
if (doUpdateTime && timeLeft >= 0)
{
timeLabel.Text = "Time left: 00 : " + time;
}
}
Can anyone please help me in this regard?
Best Regards...
Basically you can't modify the UI from anything other than the dispatcher thread. Two options:
Use Dispatcher.BeginInvoke to execute your ui-modifying code in the dispatcher thread
Use DispatcherTimer instead of starting a new thread and sleeping - that way the "tick" will occur in the UI thread already.
Are you sure it's on the time variable, not on timeLabel?
You can't usually edit the UI from other threads than the one that handles the UI.
Related
I’ve run into a performance obstacle and I’m uncertain of the cause, all of this is running under VS2022 & Net6.0. As this is my 1st time using this combination of a modal windows form, and progress bar, with the work running on a background thread and two Progress objects updating the UI, the progress bar, and a text label, I don’t know where to attack the problem. Prior to placing the workload on a background thread, everything was snappy, searching a thousand files with about 600 lines of text in each, in about a minute. Naturally, the windows form was frozen during this, which is why the workload was placed on a background thread.
After doing so, the workload will be 25-50% complete before the UI starts displaying the values from the Progress objects, and overall, the entire process now takes 10x as long to complete. Progress objects aren’t skipping over any values sent to them, the UI thread just seems slow in getting the information. Likewise, if I try to drag the modal form to a new spot on the desktop it’s unresponsive for 20—30 seconds before it finally moves. One more thing, I can step through the code on the background thread and see it calling the Progress updaters, but the UI thread is just very slow in responding to them.
I could use some suggestions on how to uncover the problem or if clearly evident, point out where the likely problem could be. Here are the essential controls and methods used.
public class SearchProgressForm : Form
{
private System.Windows.Forms.Button btnSearch = new Button();
private System.Windows.Forms.TextBox txtTextSearch = new TextBox();
private System.Windows.Forms.Label lblSearchFile = new Label();
private System.Windows.Forms.ProgressBar SearchProgressBar = new ProgressBar();
public event LogSearchEventHandler SearchSucceededEvent;
protected void OnSearchSucceeded(LogSearchEventArguments p_eventArguments)
{
LogSearchEventHandler handler = SearchSucceededEvent;
if (handler != null)
{
handler(this, p_eventArguments);
}
}
private void InitializeComponent()
{
this.btnSearch.Name = "btnSearch";
this.btnSearch.Text = "Search";
this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click);
this.lblSearchFile.Text = "Searching File: ";
this.txtTextSearch.Text = "search string";
}
public SearchProgressForm() { }
private void btnSearch_Click(object sender, EventArgs e)
{
this.SearchByText(this.txtTextSearch.Text);
}
private void SearchByText(string p_searchParameter)
{
// Setup a progress report for thr ProgressBar
var _progressBarUpdate = new Progress<int>(value =>
{
this.SearchProgressBar.Value = value;
this.SearchProgressBar.Refresh();
});
var _progressFileNameUpdate = new Progress<string>(value =>
{
this.lblSearchFile.Text = "Searching File For : " + value;
this.lblSearchFile.Refresh();
});
// Start search on a backgroud thread and report progress as it occurs
Task.Run(async () => await this.SearchByStringAsync(p_searchParameter, _progressBarUpdate, _progressFileNameUpdate));
}
private async Task SearchByStringAsync(string p_searchParameter, IProgress<int> p_progressBar, IProgress<string> p_progressFileName)
{
await Task.Delay(1);
TextFileReader textFileReader = new TextFileReader();
LogSearchEventArguments logSearchEventArguments = null;
long _sessionloopCount = 0;
long _totalTextLinesCount = this.GetTotalSearchCount(p_searchParameter, SearchType.TextString);
// Get file names from SQL table
var _logFiles = DataOperations.LogFileSortableList(null);
foreach (var log in _logFiles)
{
// Format a file name to be read from the file system
string _fileName = log.Directory + "\\" + log.FileName;
p_progressFileName.Report(log.FileName);
// If we've raised an event for this file, then stop iterating over remaning text
if (logSearchEventArguments != null)
{
logSearchEventArguments = null;
break;
}
// Read in file contents from file system
List<string> _fileContents = textFileReader.ReadAndReturnStringList(_fileName);
long _fileTotalRecordCount = _fileContents.Count;
long _fileRecordCount = 0;
foreach (var _line in _fileContents)
{
if (_line.ToUpper().Contains(p_searchParameter.ToUpper()))
{
// Raise an event so search parameter and file name can be captured in another form
logSearchEventArguments =
new LogSearchEventArguments
(
"TextSearch", p_searchParameter, SearchType.TextString, true, log,
new DateTime(
Convert.ToInt32("20" + log.FileName.Substring(14, 2)),
Convert.ToInt32(log.FileName.Substring(16, 2)),
Convert.ToInt32(log.FileName.Substring(18, 2)))
);
// We found a match, so no further searching is needed in this log file,
// and it's been flagged in the DB, so raise the event to save search parameter and file name
// then break out of this loop to get the next file to search in.
this.OnSearchSucceeded(logSearchEventArguments);
break;
}
// These calcs are based on actual searches performed
_fileRecordCount++;
_sessionloopCount++;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
// Because we exit a search as soon as the 1st match is made, need to resynch all counts
// and update the progress bar accordingly
if (_fileRecordCount < _fileTotalRecordCount)
{
long _countDifference = _fileTotalRecordCount - _fileRecordCount;
// Add count difference to sessionLoopCount and update progress bar
_sessionloopCount += _countDifference;
p_progressBar.Report(Convert.ToInt32((_sessionloopCount * 100) / _totalTextLinesCount));
}
}
//Search is complete set Progress to 100% and report before exiting
p_progressBar.Report(100);
// Close the modal SearchForm and exit
this.Close();
}
}
I solved this problem but I'm still not certain of what caused it. I eliminated the method "private void SearchByText(string p_searchParameter)" and moved the code there into the btnSearch_Click event handler so I could call my background worker "SearchByStringAsync" directly from the button click event handler.
I also updated the EFCore NuGet Packages, which were version Net6.0 to version 6.0.4, because of single line of code in my Async background method, "var _logFiles = DataOperations.LogFileSortableList(null)".
That call returned a Sortable BindingList, using BindingList <T>. Between the NuGet updates and a minor change on a custom comparer method in my BindingList <T> class, the windows modal form now updates the ProgressBar and Label text as expected, and the form now responds immediately to user interaction.
I have coded myself into a corner here. In my FXML file, I declare a progress bar and an upload progress label (for a batch upload):
Program_view.fxml
<Label fx:id="uploadProgressLabel" layoutX="384" layoutY="579" text="Upload Progress">
<font>
<Font size="18" />
</font>
</Label>
...
<ProgressBar fx:id="uploadProgressBar" layoutX="185" layoutY="606" prefHeight="18" prefWidth="534" progress="0" />
Then I have a UI controller where I import all my elements from FXML:
UI_Controller.java
#FXML Label uploadProgressLabel;
#FXML ProgressBar uploadProgressBar;
Later in the UI Controller, there is a button whose action is to update the "upload" progress bar, and it doesn't. I've tried a few different thread / task strategies and none of them seem to work while they are running.
UI_Controller.java
#FXML
protected void distributeSetButtonClick() {
//In a previous version of this project using swing, I tossed this whole function into a new thread and that made the progress bar happy
//new Thread(() -> {
final boolean[] done = {false};
if (logTextArea.getText().equalsIgnoreCase("This is your console log, logs relating to uploading your set will appear here")) {
logTextArea.setText("");
}
//Upload each file and iterate label + counter
for (int i = 1; i <= uploadImages.size(); i++) {
System.out.println("Test: " + i);
uploadProgressLabel.setText("Uploading image " + i + "/" + uploadImages.size());
File f = uploadImages.get(i - 1);
mediaIds.add(UploadFile.uploadFile(f, logTextArea, i - 1, uploadImages.size()));
double currentProgress = (1.0 / uploadImages.size()) * i;
uploadProgressBar.setProgress(currentProgress);
}
uploadProgressLabel.setText("Completed uploading: " + uploadImages.size() + " images");
String areaUpdate = filesSelectedTextArea.getText();
if (mediaIds.size() == uploadImages.size()) {
areaUpdate += "\r\n\r\n All Images uploaded successfully";
} else {
areaUpdate += "\r\n\r\n One or more files had an error while uploading";
}
filesSelectedTextArea.setText(areaUpdate);
}
...
My question is, how can I get the progress bar / label to update while they are on the main thread? When I try moving them off the main thread I get an error about them not being on the JavaFX thread. I've also tried moving the logic over into a task, which looked like this (and then had a run of the task on the main thread) also to no avail:
Tasker.java
public static Task<Void> updateProgressBar(ProgressBar p, double value) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
p.setProgress(value);
return null;
}
};
p.progressProperty().bind(task.progressProperty());
return task;
}
Some guidance would be appreciated.
Like most other UI toolkits, JavaFX is single threaded, with the FX Application Thread being responsible for processing user events and rendering the UI. This means:
You must not update UI controls from a background thread. JavaFX will throw IllegalStateExceptions in many (though not all) cases if you do this. (Note that Swing doesn't throw exceptions; it just leaves you vulnerable to arbitrary failure at some indeterminate point.)
Long-running processes (such as your file upload) must not be run on the FX Application Thread. Since this thread is responsible for rendering the UI and processing user events, no UI updates (such as updating the label and progress bar) will be possible until the long-running process is complete. Additionally, the UI will be unresponsive during this time.
You should use a Task to implement the long running process, and run the task on a background thread. The Task has thread-safe update methods which will update its properties (such as progress and message) on the FX Application Thread, so you can safely bind properties of UI elements to these properties. It also has onSucceeded and onFailed handlers, which are also executed on the FX Application thread. The onSucceeded handler can access any return value from the task.
So your code should look something like:
#FXML
protected void distributeSetButtonClick(){
//In a previous version of this project using swing, I tossed this whole function into a new thread and that made the progress bar happy
Task<String> task = new Task<>() {
#Override
protected String call() throws Exception {
final boolean[] done = {false};
//Upload each file and iterate label + counter
for (int i = 1; i <= uploadImages.size(); i++) {
System.out.println("Test: " + i);
updateMessage("Uploading image " + i + "/" + uploadImages.size());
File f = uploadImages.get(i - 1);
mediaIds.add(UploadFile.uploadFile(f, logTextArea, i - 1, uploadImages.size()));
updateProgress(i, uploadImages.size());
}
if (mediaIds.size() == uploadImages.size()) {
return "All Images uploaded successfully";
} else {
return "One or more files had an error while uploading";
}
}
};
if (logTextArea.getText().equalsIgnoreCase("This is your console log, logs relating to uploading your set will appear here")) {
logTextArea.setText("");
}
task.setOnSucceeded(event -> {
filesSelectedTextArea.append("\n\n"+task.getValue());
uploadProgressLabel.setText("Completed uploading: " + uploadImages.size() + " images");
});
uploadProgressLabel.textProperty().unbind()
uploadProgressLabel.textProperty().bind(task.messageProperty());
uploadProgressBar.progressProperty().unbind();
uploadProgressBar.progressProperty().bind(task.progressProperty());
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
I'm developing an app for Windows in Visual C++ which would get inputs from a web API. Some of the API calls require me to request response in some specified time delay and during this time I would like to display a spinner.
I'm all good with the displaying spinner, disabling the buttons etc for the time I need to wait before proceeding to the request, but don't know how to pause the process within the app. Obviously, if I use the _sleep function, the app becomes unresposive.
Here's more or less what I need to achieve (pseudo-code)
void doSomething()
{
ui->button1->setEnable(false);
SendAPIReuqest1();
while (APIRequest1_success)
{
requestAPIRequest1_response();
//wait 10s if false and retry, this can take up to 5mins
}
SendAPIRequest2();
//wait 30s - here I know I can start the job on my end exactly after 30s
doSometing2();
ui->button1->setEnable(true);
}
What would be the correct approach to achieve what I need?
You'll most likely just want to poll for a response from the web API, alternatively you can start a secondary thread to check for a response from the web API;
After seeing some minimal code, something similar to this may work.
//takes an initial start time, calculates elapsed time, compares elapsed time to count
bool ready(std::chrono::time_point<std::chrono::system_clock>&start, const double& count) {
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> diff = end-start;
if (diff.count() >= count) {
start = end;
return true;
}
return false;
}
void doSomething()
{
static std::chrono::time_point<std::chrono::system_clock> start;
static int state = 0;
switch(state) {
case (0):
ui->button1->setEnable(false);
SendAPIRequest1();
if (APIRequest1_success) {
start = std::chrono::system_clock::now();
++state;
}
break;
case (1):
if (ready(start, 10.0) && requestAPIRequest1_response()) ++state;
break;
case(2):
SendAPIRequest2();
start = std::chrono::system_clock::now();
++state;
break;
case(3):
if (ready(start, 30.0)) {
doSomething2();
ui->button1->setEnable(true);
state = 0;
}
break;
}
}
This way you can call the function and it will either attempt one of the requests or return to do other tasks.
or with threads it could be as simple as
void worker_func(std::promise<bool>&& result) {
using namespace std::chrono_literals;
SendAPIRequest1();
while (!requestAPIRequest1_response()) {
std::this_thread::sleep_for(10s);
}
SendAPIRequest2();
std::this_thread::sleep_for(30s);
doSomething2();
result.set_value(true);
}
void doSomething() {
static std::future<bool> finished;
static bool flag = true;
if (flag) {
std::promise<bool> _finished;
finished = _finished.get_future();
ui.button1.setEnable(false);
std::thread worker(worker_func, std::move(_finished));
flag = false;
} else if (finished.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
//finished.get();
worker.join();
ui.button1.setEnable(true);
flag = true;
}
}
This way your main thread can keep running the ui, while the worker thread waits for the web API response, as long as your requests aren't handling any QT ui components I believe this should work.
Edit:
Since I have never used QT and it was never mentioned originally that QT was being used the above answers may or may not be usefull, however it looks like QT has some functions to handle things like this. maybe you can just start and stop a Qtimer
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &foo::update);
timer->start(1000);
would run the function foo::update() every second, the time interval can be changed accordingly. Here is a reference to QTimer and a reference to another class which may be of interest QTimerEvent not to mention, QT has an entire event handling system The Event System
I have a java thread which is running a path-finding algorithm in a constant while loop. Then, every so often I want to retrieve the most updated path from the thread. However, I am unsure how to do this, and think I might be doing it wrong.
My thread consists of the following code:
public class BotThread extends Thread {
Bot bot;
AStar pathFinder;
Player targetPlayer;
public List<boolean[]> plan;
public BotThread(Bot bot) {
this.bot = bot;
this.plan = new ArrayList<>();
pathFinder = new AStar(bot, bot.getLevelHandler());
}
public void run() {
while (true) {
System.out.println("THREAD RUNNING");
targetPlayer = bot.targetPlayer;
plan = pathFinder.optimise(targetPlayer);
}
}
public boolean[] getNextAction() {
return plan.remove(0);
}
}
I then create an object of BotThread, and call start(). Then when I call getNextAction() on the thread, I seem to receive a null pointer. Is this because I am not able to call another method on the thread whilst it is in the main loop? How should I do this properly?
This is because you are not giving enough time to thread to initialise plan Arraylist. You need to add sleeping time to the threads. Something like this while calling BotThread class from main:
int num_threads = 8;
BotThread myt[] = new BotThread[num_threads];
for (int i = 0; i < num_threads; ++i) {
myt[i] = new BotThread();
myt[i].start();
Thread.sleep(1000);
myt[i].getNextAction();
}
i want to use keyPressed function in canvas class. but i do not want immediately call this function.
i try to use wait function but it cause an error ( i think it hasn't any use for this). what should i do?
keyPressed is called by the AMS (Application Management Software) when the user clicks a key. You cannot delay that.
But you can of course call keyPressed yourself as you want. If you want to call keyPressed 10 seconds from now, you should create a Thread with a timer and a loop that asks if 10 seconds has gone by now.
Something like this: (not tested)
class keyPressedAfterSeconds implemments Runnable {
MyCanvasObject myCanvas = null;
int seconds = 10; // Default
long startTime;
public keyPressedAfterSeconds(MyCanvasObject myCanvas, int seconds) {
this.myCanvas = myCanvas;
this.seconds = seconds;
new Thread(this).start();
}
public run() {
startTime = System.currentTimeMillis();
while(System.currentTimeMillis()-startTime<seconds*1000) {
try { // Wait 100 ms and ask again
Thread.sleep(100);
} catch (Exception e) {}
}
myCanvas.keyPressed(someKeycode);
}
}