I'm currently reading data from 7 sensors from bluetooth and displaying them with 7 lines in the chart. I managed to get the app working, for each data coming in, I implement the addEntry function below to update the chart and move the window along the x-axis to show the most updated data.
However, data transmission is at 100Hz, and the app will run for more than an hour, I found that the app freeze and might not be able to deal with this sometimes after running the app for a period of time, say 20 minutes. Given the large amount of data to deal with, I would like to just keep data for 10-15 seconds in the chart(i.e. even I scroll back to the beginning of the chart, it will just show 10-15s data). After some research, I just found the way
mChart.getData().notifyDataChanged(); to update data, which won't allow me to just display a selected part of the arraylist. Could any advice?
Thanks in advance.
private void addEntry(){
Log.d(TAG, "add Entry called");
LineDataSet[] sensorSet = new LineDataSet[7];;
if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0){
for (int k = 0; k<7; k++) {
sensorSet[k] = (LineDataSet) mChart.getData().getDataSetByIndex(k);
sensorSet[k].setValues(SensorData.getSensor()[k]);
}
mChart.getData().notifyDataChanged();
Log.d(TAG, "notified");
mChart.notifyDataSetChanged();
mChart.setVisibleXRange(0,5000);
int temp = mChart.getData().getEntryCount();
Log.d(TAG, "Entry count: " + mChart.getData().getEntryCount());
mChart.moveViewToX(mChart.getLineData().getXMax()-1000);
}else{
for (int k = 0; k<7; k++) {
sensorSet[k] = createSet(k);
}
LineData data = new LineData(sensorSet);
mChart.setData(data);
}
}
Related
Using JavaFX 8.0 and JRE 1.8.0 u45 in IntelliJ, I created a basic pixel editor app. I have two windows(stages). One is a 32x128 matrix of Circle Objects placed in a Grid Pane and the other, a Tools widow; I have one Controller.java. Using the mouse, the Tools window provides tools to draw lines, rectangles, etc. and a menu for Setup, Files and Playlists. The Files menu presents the user with Open, SaveAs and Delete. To view a saved pixel art file, the user clicks Open and via the JFileChooser, the selected file is opened and each Circle’s assigned color is displayed using the following method:
public static void openFile( String pathName) throws IOException {
path = Paths.get(pathName);
pixelByteArray = Files.readAllBytes(path);
int cnt = 0;
for (int r = 0; r < row; r++) {
for (int c = 0; c < col; c++) {
String hexRGB = String.format("#%02X%02X%02X",
pixelByteArray[cnt++], //red
pixelByteArray[cnt++], //green
pixelByteArray[cnt++]); //blue
Color color = Color.valueOf(hexRGB);
pixelArray[r][c].setFill(color);
}
}
String fileName = path.getFileName().toString();
window.setTitle(MessageFormat.format("Pixel Array {0} x {1} File: {2}", Integer.toString(row), Integer.toString(col), fileName));
} // openFile
The Circle’s color is updated with for loops using setFill(color).
Next I created a class to test a basic slide show. I would use JFileChooser to select multiple files, put them in an ArrayList<file> and use the openFile(pathname) in a for loop to display each, pausing 5 secs between each openFile(pathname) as shown below:
public class PlayPlaylist {
public static void playPlaylist() throws IOException, InterruptedException {
FileChooser fileChooser = new FileChooser();
fileChooser.setInitialDirectory(new File("C:\\ProgramData\\L1 Art Files\\"));
fileChooser.setTitle("Play One or More Pixel Art Files");
java.util.List<File> selectedFiles = fileChooser.showOpenMultipleDialog(null);
for (File selectedFile : selectedFiles) {
openFile(selectedFile.getPath());
sleep(5000);
}
} //Method playPlaylist()
I first used wait(5000) but couldn’t compile because I have a static method, which I need. The wait() method won’t run in a static method. I then tried sleep(5000) which compiles but to my confusion only the last file in the ArrayList<file> will be displayed. All the pixel art files will display the setTitle() correctly and pause for about 5 secs but the Circle’s will not change to the assigned color except for the last file, after the 5 secs have passed. The for loop, setFill(color) process in openFile() seems to be paused even though the setTitle() is executed correctly, which is after the setFill(color) process?
I’m new to Java and JavaFX and have never created any threads directly in this program or any other but researching possible problems implies the problem may be related to threads.
Is there a classic fix to this problem? Thanks for any help
After studying the references and suggested solutions below, I am unable to fix the problem. I tried two code changes. The first was to the for loop code above:
for (File selectedFile : selectedFiles) {
Platform.runLater(() -> {
try {
openFile(selectedFile.getPath());
try {
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
});
The second change was to the openFile(pathname) above:
final int finalR = r;
final int finalC = c;
Platform.runLater(() -> pixelArray[finalR][finalC].setFill(color));
I get the same results as described above with only the last file being displayed. If I simply select one file in the Arraylist<File> it will display, i.e.:
if (selectedFiles != null) {
File playFile = selectedFiles.get(0);
openFile(playFile.getPath());
System.out.println("\n File 1 = " + playFile.getPath());
}
Now if I add Sleep(5000):
if (selectedFiles != null) {
File playFile = selectedFiles.get(0);
openFile(playFile.getPath());
System.out.println("\n File 1 = " + playFile.getPath());
sleep(5000);
}
It will display the pixel art file after 5 secs. What I don't understand is why it appears to "skip" over the pixelArray[r][c].setFill(color)); which sets all the Circle's to the saved colors but does execute the following and last statement in the openFile(pathname) which is setTitle(...)? It's like sleep(5000) blocks the pixelArray[r][c].setFill(color));but nothing else?
Why can I openFile(pathname) for just one file with proper results but not two or more in sequence?
I also have the two UI windows posted on the web at: Virtual Art. I think the pixel art shown in the Pixel Array window clarifies the goal of creating a user-defined slide show.
***This question was answered elegantly by jewelsea at:
How to avoid Thread.sleep() in a for loop from interrupting the UI Thread?***
You have two problems here, your understanding of wait(), and a GUI multithreading issue.
About the GUI multithreading... You will find more informations here about Platform.runLater(...):
Platform.runLater and Task in JavaFX
Now about Wait... It will pause your Thread until a Notify event is received through its monitor. To be able to call notify() you need to synchronize on the same object (the monitor).
synchronized (someObject) {
someObject.wait();
}
/* different thread / object */
synchronized (someObject) {
someObject.notify();
}
If you want to use Wait, you will need another Thread (a Timer?), to wakeup/notify you. Sleep should work fine in your case, as long as you use Platform.runlater(...)
I'm developing a traffic application with using nutiteq map. There is over 500 traffic lines, lot of markers about traffic. I drew traffic lines with this way;
public void drawlines(){
ArrayList<MapPos> arr_lat_long1 = new ArrayList<MapPos>();
for(int i = 0; i < arr_lat_long1.size(); i ++){
MapPos lineMapPos = new MapPos(arr_lat_long1.get(i).x,arr_lat_long1.get(i).y);
arr_lat_long1.add(lineMapPos);
geoLayer = new GeometryLayer(new EPSG4326());
mapView.getLayers().addLayer(geoLayer);
LineStyle lineStyle = null;
lineStyle =LineStyle.builder().setWidth(0.14f).setColor(Color.RED).build();
//Label label = new DefaultLabel("Line", "Here is a line");
Line line = new Line(arr_lat_long1, null, lineStyle, null);
line.setVertexList(arr_lat_long1);
geoLayer.add(line);
lines.add(line);
}
and i add markers same way;
Bitmap pointMarker = UnscaledBitmapLoader.decodeResource(activity.getResources(), R.drawable.marker3);
MarkerStyle markerStyle = MarkerStyle.builder().setBitmap(pointMarker).setSize(0.5f).setColor(Color.WHITE).build();
Label markerLabel = new DefaultLabel("Here", "Blabla");
MapPos markerLocation = MainActivity.mapLayer.getProjection().fromWgs84(log, lat);
marker = new Marker(markerLocation, markerLabel, markerStyle, null);
markerLayer.add(marker);
MainActivity.mapView.getLayers().addLayer(markerLayer);
there is no problem for drawing. When i want to delete lines or markers, firstly deleted item but when i want to slide map, all items come back and shown on mapview. I'm deleting items iteratively. My deleting code is here:
for(int i = 0; i <lines.size(); i++){
geoLayer.remove(lines.get(i));
geoLayer.clear();
}
and also i tried this again:
geoLayer.removeAll(lines);
How can i delete all my map items properly on Nutiteq?? Is there any way to clear or remove?
From the code above it seems you are creating a new layer for each line and you are 'forgetting' the references to these layers. Simply move geoLayer construction outside of the loop. For removing all the lines, you can call either geoLayer.clear() or geoLayer.removeAll(lines). Both should work. No need to use the for-cycle loop.
I have been using this example for my project, and it works really nice.
My question: Is it possible to offset the hovered node such that it does not overlay the underlying data point. The example centers the hovered node right over the "normal" node. It kind of gets in the way on a chart with a lot of data points.
A simple solution is to set a custom translation to the displayed Label. The following code is extracted from the example.
private Label createDataThresholdLabel(int priorValue, int value)
{
final Label label = new Label(value + "");
label.setTranslateY(-25); //Move label 25 pixels up
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0)
{
label.setTextFill(Color.DARKGRAY);
}
else if (value > priorValue)
{
label.setTextFill(Color.FORESTGREEN);
}
else
{
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
I have an application in which double clicking over an image view area changes the layout of the image view. Also on single click a dot will be placed on the image.
My problem is, both functionality is working when double clicked.
Of course I know that, when a double click occurs the control first goes to LButtonDown. I don't want the dot functionality to work when double click occurs. I have been working around this for more than a week. Please help.
The easiest way to solve this is to build a finite-state machine for handling mouse clicks.
Basically, this will be a singleton object, which takes input from the mouse click events you're currently using.
It's output will be SingleClickDetected, DoubleClickDetected, ....
Red arrows indicate events which you are reporting to the rest of your application.
Parentheses indicate what event you are reporting.
Of course, this state machine will have to be modified if you have to deal directly with MouseDown and MouseUp events, instead of MouseClick events.
It will be slightly larger, but the idea is basically the same.
EDIT:
From the comments, it looks like Windows doesn't cleanly report single- vs double-clicks, and you need to separate them.
A state-machine for this scenario:
This is probably overkill for what you're trying to do, especially since most, if not all GUI-based programs in the history of everything have never ever used a double-click drag.
It does show the basic idea, and shows how you can extend your state machine to handle different types of button clicks.
Furthermore, if you wanted to, you could handle double-right-clicks, a drag that involves both left and right buttons, or any other scenario you could think of and incorporate into your UI.
I wrote the following code and it works.
UINT TimerId;
int clicks;
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
KillTimer(NULL, TimerId);
if (clicks < 2 && !double_click){
MessageBox(hWnd, L"Show Widget", L"Widget", MB_OK);
}
clicks = 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
UINT uID;
UINT uMouseMsg;
uID = (UINT)wParam;
uMouseMsg = (UINT)lParam;
if (uMouseMsg == WM_LBUTTONDBLCLK){
double_click = true;
MessageBox(hWnd, L"Double click", L"CAPT", MB_OK);
return 0;
}
if (uMouseMsg == WM_LBUTTONDOWN){
double_click = false;
clicks++;
//single click opens context menu
if (clicks == 1){
TimerId = SetTimer(NULL, 0, 500, &TimerProc);
}
return 0;
}
,...
}
Try storing the timestamp of the last LButtonDown; if the time difference between the last timestamp and the timestamp produced in the current event is too short, you can just cancel your operation (but still store the new LButtonDown timestamp)
The only thing you can do is wait for a short amount of time each time you receive click event, and test if in the meantime the equivalent of a double click event doesn't occur before performing the single click response. This may be source to new bugs and unresponsive UI. Maybe try to change the user interaction to toss the problem away.
EDIT: The fact that you are working around this for more than a week is a symptom of bad user interaction design. A "double click" still means two clicks occurs, which means the application naturally should perform the operation of a single click. Check the ui of the apps installed on your desktop to verify this. Have you considered using a different user medium to trigger the UI response? For instance, you can use the right button to put the dot on the image.
#E.T's answer is spot-on. To implement something like that, you really need a timer running along with your message loop.
In my application I wanted to be able to distinguish mouse down/up from double click because I want a double click to not undo a drag operation (imagine selection box with left button drag and double click to zoom to fit).
The following code does this using PreTranslateMessage. I did not add the timer out of laziness. So the UI behaves a bit "funny" if you don't immediately move the mouse after the left button is held down. In my case, this is a minor issue.
BOOL MyWindow::PreTranslateMessage(MSG *pMsg)
{
//From https://msdn.microsoft.com/en-us/library/windows/desktop/ms645606(v=vs.85).aspx
//Double-clicking the left mouse button actually generates a sequence of four messages:
//WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
//So here's the problem. If an button up message arrives, we can't just
//take it, because it may be followed by a double click message. So we check
//for this.
//Ideally you need a timer - what happens if you don't get any messages
//after the button up or down? But here we get lazy and assume a message
//will come "soon enough".
static bool upMessageStored = false;
static bool dnMessageStored = false;
static MSG upMessage, dnMessage;
static bool processDnNow = false, processUpNow = false;
//This logic sequence absorbs all button up and down messages, storing them to be sent
//if something other than a double click message immediately follows.
if (!(pMsg->message == WM_LBUTTONDBLCLK ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_LBUTTONUP) &&
(upMessageStored || dnMessageStored))
{
//If we receive any message other than double click and we've stored the messages,
//then post them.
Output::Message(L"Stored messages posted.\n");
if (upMessageStored)
{
processUpNow = true;
upMessageStored = false;
this->PostMessage(upMessage.message, upMessage.wParam, upMessage.lParam);
}
if (dnMessageStored)
{
processDnNow = true;
dnMessageStored = false;
this->PostMessage(dnMessage.message, dnMessage.wParam, dnMessage.lParam);
}
return TitlelessWindow::PreTranslateMessage(pMsg);
}
if (pMsg->message == WM_LBUTTONDOWN && !processDnNow)
{
Output::Message(L"WM_LBUTTONDOWN absorbed; message stored\n");
dnMessage = *pMsg;
dnMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONUP && !processUpNow)
{
Output::Message(L"WM_LBUTTONUP absorbed; message stored\n");
upMessage = *pMsg;
upMessageStored = true;
return TRUE; //swallow this message.
}
else if (pMsg->message == WM_LBUTTONDBLCLK)
{
Output::Message(L"WM_LBUTTONDBLCLK; stored message discarded\n");
upMessageStored = false;
dnMessageStored = false;
processUpNow = false;
processDnNow = false;
}
//If we get here, we are processing messages normally. Be sure we clear the flags
//for up and down.
processDnNow = false;
processUpNow = false;
return ParentClass::PreTranslateMessage(pMsg);
}
I really liked the finite state machine answer BUT there is a flaw in it.
There is no such thing as "single click time" which you can exceed.
If you look at a the working of your mouse closely you will see that:-
single click not one event but = WM_LBUTTONDOWN, WM_LBUTTONUP which is independent of time in between, the appropriate action will take place anyway
Double-clicking the left mouse button actually generates a sequence of four messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, and WM_LBUTTONUP
so you should use the 3rd flag to your advantage
BTW, I am also working on something like this. Thanks!
I'm trying to add a pop-up message function in my project.And I make it run in a subthread since I need a realtime notification.But I find if the notification dialog is created in my subthread(started by AfxBeginThread),all the elements(buttons,urls....) of the dialog are not shown.The message box is just a blank dialog. If I extract the function and start it by a button then it works fine.Here's the function,it's quite simple.
UINT sendNotification(LPVOID pParam)
{
int x = GetSystemMetrics(SM_CXSCREEN);
int y = GetSystemMetrics(SM_CYSCREEN);
testPopDlg *testPop = new testPopDlg;
testPop->Create(IDD_TEST,0);
CRect lprect(0,0,0,0);
testPop->GetWindowRect(lprect);
int w = lprect.Width();
int h = lprect.Height();
testPop->web.Navigate("http://www.google.com",NULL,NULL,NULL,NULL);
testPop->ShowWindow(SW_SHOW);
for(int k=0;k<20;k++) //slide out
{
testPop->MoveWindow(x-w,y-h*k/20,w,y-h*k/20,1);
Sleep(20);
}
Sleep(5000); //will close after 5 sec
return 0;
}
Why this function doesn't work right in subthread?
To use a dialog on a different thread in MFC you should create it on a UI thread. To create a UI thread you basically have to create a class derived from CWinThread. See also this code project article.