Can't Get Multiple Charts (Google Visualization) on Multiple Tabs (GWT TabPanel) - gwt

I need to display an unknown quantity of tabs each with an unknown quantity of graphs (Google Visualizations). I have created "Tab" and "Graph" classes and Tab contains an ArrayList.
TabWrappers extends FlexTable and is currently empty. It's a place holder at the moment, but the behavior does not change if I use FlexTable rather than TabWrapper.
The code below, minus the section that adds Tab2 works perfectly for creating 1 tab populated with graphs. When adding the 2nd tab both tabs are displayed and named correctly but neither have graphs.
public class SomeClass {
...
DataTable data = response.getDataTable();
DataView result;
Options options = createOptions();
ArrayList<Tab> displayTab = new ArrayList<Tab>();
Tab t;
ArrayList<Graph> graphList = new ArrayList<Graph>();
Graph g;
t = new Tab();
g = new Graph();
result = DataView.create(data);
result.setRows(new int[]{0, 2, 4, 6});
g.setGraphType(new PieChart(result, options));
graphList.add(g);
g = new Graph();
result = DataView.create(data);
result.setRows(new int[]{1, 3, 5, 7});
g.setGraphType(new PieChart(result, options));
graphList.add(g);
g = new Graph();
result = DataView.create(data);
g.setGraphType(new PieChart(result, options));
graphList.add(g);
t.setTabName("Tab1");
t.setGraphs(graphList);
displayTab.add(t);
// Add a 2nd tab
t = new Tab();
t.setTabName("Tab2");
t.setGraphs(graphList);
displayTab.add(t);
TabWrapper tabWrapper;
for (Tab tX : displayTab){
int row = 0, col = 0, maxCol = 2;
tabWrapper = new TabWrapper();
for (Graph gX : tX.getGraphs()) {
col = tX.getGraphs().indexOf(gX) - (row * maxCol);
tabWrapper.setWidget(row, col, gX.getGraphType().asWidget());
if (++col == maxCol) {
row++;
}
}
tabPanel.add(tabWrapper, tX.getTabName());
}
...
}

When you use tabs in GWT, the panels held in each tab seem to be lazy loaded and aren't completely set up until the user clicks on each tab.
In particular, the tab containers will have zero widths and heights (the error logs will probably be giving an error about containers having zero width) so the graph drawing will fail and leave an empty space.
What you need to do (and is probably good practice anyway) is to lazy load the contents of the tabs too so that the graph is loaded only when the tab is fully set up. This can be done by removing the call the t.setGraphs(...) and adding a selection handler that does it instead:
tabPanel.addSelectionHandler(new SelectionHandler<Integer> () {
public void onSelection(SelectionEvent<Integer> event) {
// Pseudocode:
// n = event.getSelectedItem()
// t = displayTab[n]
// g = graphList[n]
// t.setGraphs(g)
}
});
so that graphs are added and drawn only when the tab is selected.
You'll probably also want to call
tabPanel.selectTab(0);
to force a selection of the first tab after the selection handler is in place.
I had the same issues that you describe and this resolved it.

Related

Is there way to auto resize percentage columns when column group is collapsed/expaned in NatTable

I found ResizeColumnHideShowLayer class at nattable version 1.6.
(about https://bugs.eclipse.org/bugs/show_bug.cgi?id=521486)
That is work fine for normal column headers only.
But, if I collapse a column group, no adjust size to fit window. (no increasing column size)
How can I solve the problem?
Is there way to resize other columns to fit window automatically increase?
Thank you.
Currently not because the ColumnGroupExpandCollapseLayer is taking care of hiding collapsed columns.
I found solution by myself!
It works fine very well. :-)
I was run based on NatTable v1.6 version.(downloaded yesterday)
I think this is a basic feature, so I hope this feature will be included in the next NatTable version.
In narrow tables, behavior that collapsing column group means that may be someone want to view other column data more widely.
Overview (Problem screen and solved screen)
I explain using two application(before, after) screen shot.
Refer to bottom image if you want understand my issue easily at once.
Problem screen
enter image description here
Improved screen
enter image description here
Solution summary :
Add event listener to ColumnGroupExpandCollapseLayer.
      (HideColumnPositionsEvent, ShowColumnPositionsEvent)
Handle above events.
      Get column indices which is hidden by collapsed
      Execute MultiColumnHideCommand with the indices
Layer structure of my test code
↑ ViewportLayer (top layer)
| SelectionLayer
| ColumnGroupExpandCollapseLayer
| ResizeColumnHideShowLayer
| ColumnGroupReorderLayer
| ColumnReorderLayer
| DataLayer (base layer)
Implementation code is below:
void method() {
...
columnGroupExpandCollapseLayer.addLayerListener(new ILayerListener() {
#Override
public void handleLayerEvent(ILayerEvent event) {
boolean doRedraw = false;
//It works for HideColumnPositionsEvent and ShowColumnPositionsEvent
// triggered by ColumnGroupExpandCollapseCommandHandler
if (event instanceof HideColumnPositionsEvent) {
HideColumnPositionsEvent hideEvent = (HideColumnPositionsEvent)event;
Collection<Range> columnPositionRanges = hideEvent.getColumnPositionRanges();
Collection<Integer> convertIntegerCollection = convertIntegerCollection(columnPositionRanges);
int[] positions = convertIntPrimitiveArray(convertIntegerCollection);
//Execute command to hide columns that was hidden by collapsed column group.
MultiColumnHideCommand multiColumnHideCommand = new MultiColumnHideCommand(resizeColumnHideShowLayer, positions);
resizeColumnHideShowLayer.doCommand(multiColumnHideCommand);
doRedraw = true;
}else if (event instanceof ShowColumnPositionsEvent) {//called by ColumnGroupCollapsedCollapseCommandHandler
ShowColumnPositionsEvent showEvent = (ShowColumnPositionsEvent)event;
Collection<Range> columnPositionRanges = showEvent.getColumnPositionRanges();
Collection<Integer> positions = convertIntegerCollection(columnPositionRanges);
//Execute command to show columns that was hidden by expanded column group.
MultiColumnShowCommand multiColumnShowCommand = new MultiColumnShowCommand(positions);
resizeColumnHideShowLayer.doCommand(multiColumnShowCommand);
//Set whether or not to redraw table
doRedraw = true;
}
if (doRedraw) {
natTable.redraw();
}
}
/**
* Merge position values within several Ranges to Integer collection
*/
private Collection<Integer> convertIntegerCollection(Collection<Range> rangeCollection) {
Iterator<Range> rangeIterator = rangeCollection.iterator();
Set<Integer> mergedPositionSet = new HashSet<Integer>();
while (rangeIterator.hasNext()) {
Range range = rangeIterator.next();
mergedPositionSet.addAll(range.getMembers());
}
return mergedPositionSet;
}
/**
* Convert Integer wrapper object to primitive value
*/
private int [] convertIntPrimitiveArray(Collection<Integer> integerCollection) {
Integer [] integers = (Integer [])integerCollection.toArray(new Integer[integerCollection.size()]);
int [] positionPrimitives = new int[integers.length];
for (int i = 0 ; i < integers.length ; i++) {
positionPrimitives[i] = integers[i].intValue();
}
return positionPrimitives;
}
});
}

Change value of widget inside GWT flex table when other widget's value changes?

Ok, so I have a pretty specific and to me quite complicated issue, as I'm a GWT newbie.
I have a GWT flex table, which I use to dynamically add rows, whose cells contain GWT widgets. The row number changes, but the number of columns in static, always 6. Each row contains a cell with a remove button and five cells each with their own textbox.
What I need to do is somehow code a kind of relationship between the textbox in cell 6 of one row and the textbox in cell 5 in the next row (and vice versa).
To illustrate: when something changes in the textbox at [1,6] the content of textbox at [2,5] needs to be overwritten with the same value. If the textbox at [2,5] changes the textbox at [1,6] needs to change as well. I cannot use a button to commit the changes, it needs to happen via onValueChange or Blur or something similar, which doesn't require the user to perform a specific action.
My problem stems mostly from trying to figure out how to address specific cells in the flex table and their content. For the remove button the solution was easy enough with a click event handler, but for this issue I just can't seem to be able to come up with a solution.
Sadly I also cannot provide any of the code which I have up until now, since it's a business secret. I can only give a broad description of the problem like the one above.
EDIT:
Actually, it's probably more a problem of not having much code in terms of this specific problem.
What I have is a flex table, which has initially only the header row. Upon clicking a button below this table the addNewField() method is called, which just contains the creation, setting of default values and adding of the text fields into a new row.
addNewField() {
int rows = flextable.getRowCount();
Button removeBtn = new Button("x");
removeBtn.getElement().setId(Integer.toString(rows));
//then the button's event handler
TextBox name = new TextBox();
name.setText("something");
flextable.setWidget(rows, 0, "name");
//repeat 4 more times with incrementing columns for the other widgets
}
This way I add entire rows of editable TextBoxes. What I need is a way to influence the values of the 6th column TextBox of a chosen row and the 5th column TextBox of chosen row + 1.
EDIT2: I've tried the dirty option just to see how it would go and somehow the compare inside the if breaks the app. The compiler detects a nullpointerexception and I can't even debug it with breakpoints because it fails to compile and won't start. I can't figure out why though. I threw the code directly into the event for testing purposes, so pardon the ugliness.
TextBox bis = new TextBox();
bis.setText(rows + ":10:00");
subs.setWidget(rows, 5, bis);
bis.addValueChangeHandler(new ValueChangeHandler<String>()
{
#Override
public void onValueChange(ValueChangeEvent<String> event)
{
allRows: for (int i = 0; i < subs.getRowCount(); i++)
{
for (int j = 0; j < subs.getCellCount(i); j++)
{
if ( subs.getWidget(i, j) == bis )
{
TextBox widgetAtColumnSix = ((TextBox) subs.getWidget(i, 5));
String text = widgetAtColumnSix.getText();
TextBox widgetAtColumnFiveRowPlusOne = ((TextBox) subs.getWidget(i + 1, 4));
widgetAtColumnFiveRowPlusOne.setText(text);
break allRows;
}
}
}
}
});
EDIT: Since you edited your question and you dont want to use EventBus you could iterate over your FlexTable and set your TextBox value depending on your current rowIndex and cellIndex... Its not nice but it should work:
public class CellWidget extends Composite {
private TextBox nameBox;
public CellWidget() {
FlowPanel flowPanel = new FlowPanel();
Button deleteButton = new Button("x");
nameBox = new TextBox();
nameBox.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
notifiyTextBox(CellWidget.this, event.getValue());
}
});
flowPanel.add(nameBox);
flowPanel.add(deleteButton);
initWidget(flowPanel);
}
public void setText(String text) {
nameBox.setText(text);
}
}
public void notifiyTextBox(CellWidget source, String string) {
rows: for (int i = 0; i < flextable.getRowCount(); i++) {
columns: for (int j = 0; j < flextable.getCellCount(i); j++) {
if (flextable.getWidget(i, j) == source) {
CellWidget widgetAtColumnSix = ((CellWidget) flextable.getWidget(i, 5));
widgetAtColumnSix.setText(string);
CellWidget widgetAtColumnFiveRowPlusOne = ((CellWidget) flextable.getWidget(i + 1, 4));
widgetAtColumnFiveRowPlusOne.setText(string);
break rows;
}
}
}
}
I still would recommend using an eventbus. To make it even more convenient there is the GWT Event Binder lib, which makes using events a breeze.
So when you change a value in your textbox[2,5] it also fires your CustomEvent. All Widgets, that need to change their textbox value just need to catch...

Automatic update of Button text on change

I'm currently learning scala, and making an encryption program with a basic scala swing UI.
I added 2 swing buttons which text is held by 2 var.
The code looks like this :
var encText = "Encrypt"
var decText = "Decrypt"
def top = new MainFrame {
title = "Data Guardian"
minimumSize = new Dimension(500, 200)
contents = new GridPanel(2, 2) {
hGap = 3; vGap = 3
contents += new Button {
text = encText
reactions += {
case ButtonClicked(_) => Main.startEnc
}
}
contents += new Button {
text = decText
reactions += {
case ButtonClicked(_) => Main.startDec
}
}
}
size = new Dimension(150, 40)
}
Those "text" var will be changed often during the encryption/decryption process by various methods, but when they do change, the text displayed on the buttons doesn't.
I'd like to know a way to make the displayed text of the buttons automatically change when the var that holds that text changes.
Thanks a lot for your insight :)
Make the strings private and write getters/setters that change the button text as a side-effect.
You'll need to give the buttons names, rather than having anonymous instances as you do above.

Add new labels on button listener GWT

I'm trying to add new labels to a panel and this is when a button is clicked, in fact the number of labels is unknowing because my application consists in extracting some informations from a file and then display each information in a label so i have to upload the file and then extract the informations, i created an uploadfile and i'm able to extract the informations but i face a problem to display each information in its label, i can't create many labels and then with label.settext() make each information in its label beacuase the number of labels/informations is variable.
So can you advice/help me so i can make this working.
Best regards.
If you get the result from an Array for example you can do like this:
String[] data; //You can add you data here
addButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
for (String s : data) {
RootPanel.get().add(new Label(s));
}
}
});
That way you can add as much Labels as you want
You can create a variable number of Labels with a LinkedList.
int count = x ; //Quantity of labels you need;
LinkedList<Label> labelList = new LinkedList<Label>();
for (int i = 0; i < count ;i++)
{
Label tmpLabel = new Label();
tmpLabel.setText(STUFF) //Here you have to set your content
labelList.add(tmpLabel);
}
// Now we add the Labels to the Panel
for (int ind = 0; ind < labelList.size() ;ind++)
{
panel.add(labelList.get(ind)); //panel is the panel you show
}
If you don't have to access the labels later, you don't need the LinkedList and could add them direct to your panel.
You didnt say how you exactly attach the Labels, but if you use a Grid you have to set the size of it depending on your information.

SWT - TableViewer - Refreshing Selection

I have a button that runs a method. The method gets the selected Rows in the table and adds them to an arraylist. This works well the first time executed. But if the user selected the wrong row, they will be able to re select a different row and add that selection data to the arraylist.
But with my current the code, it does not matter what row the user selects the second time the first selected data is always added to the arraylist. It is like the selection needs to be reset or refreshed before the user selects the new row.
Button Code
Button pdfButton = new Button(composite, SWT.PUSH);
pdfButton.setText("Get Plotter List");
pdfButton.setEnabled(true);
pdfButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
getPlotterSelection();
}
});
Method Code
public void getPlotterSelection() {
selectedPlotters.clear(); <-- Clearing the ArrayList
int[] row = viewer.getTable().getSelectionIndices(); <-- Getting Current Selections
Arrays.sort(row);
if (row.length > 0) {
for(int i = row.length-1; i >= 0; i--){
PrinterProfile pp = new PrinterProfile(aa.get(i).getPrinterName(), aa.get(i).getProfileName());
selectedPlotters.add(pp);
}
}
viewer.getTable().deselectAll();
}
As I am writing this, I think maybe the problem is in the getSelectionIndices(). It seems to be getting the number of rows selected, but not the actual row number
Edit
The problem was in my logic. I was getting the correct Indices but using i varaible in the for loop to get the value.
for(int i = row.length-1; i >= 0; i--){
PrinterProfile pp = new PrinterProfile(aa.get(i).getPrinterName(), aa.get(i).getProfileName());
changed it to
aa.get(row[i].getPrinterName(), etc...
and it works like I thought it would
Since you are already using a TableViewer, why not get the selection from it?
IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
YourObject[] array = (YourObject[])selection.toArray();
Then you can iterate over the array and add them to your ArrayList.