this code below creates a checkbox which I want to turn synchronized scrolling on my two panels on/off. Initially when the program runs the scrollbars are independent. When the checkbox is checked and the "if" statement is reached, scroller2's scrollbar is set to the model of scroller1's scrollbar and they are connected, i.e. they move together.
However the problem is when the checkbox is unchecked, meaning I want the scrollbars to be independent again. Nothing I put in the else statement seems to work.
JCheckBox check = new JCheckBox("Global");
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
AbstractButton abstractButton = (AbstractButton) actionEvent.getSource();
boolean selected = abstractButton.getModel().isSelected();
System.out.println(selected);
if (selected){
scroller2.getHorizontalScrollBar().setModel(scroller.getHorizontalScrollBar().getModel());
} else {
scroller = new JScrollPane(plotPanel);
scroller2 = new JScrollPane(plotPanel2);
}
}
};
check.addActionListener(actionListener);
This:
scroller = new JScrollPane(plotPanel);
scroller2 = new JScrollPane(plotPanel2);
creates two new JScrollPanes but you do nothing with them. Understand that changing the object a variable refers to, here the scroller and scroller2 vairables will have no effect on the object that is displayed, and this issue gets to the core distinction of the difference between a reference variable and a reference or object. If you wanted to change the displayed JScrollPane you'd have to remove the old one, and add the new one to the GUI.
But you don't want to do this. Simply give one of the horizontal JScrollBars a new model, specifically a new DefaultBoundedRangeModel. I would use the constructor that takes 4 ints, and would scrape the value, extent, min, max parameters from the current model.
e.g., something like:
else {
JScrollBar horizBar = scroller2.getHorizontalScrollBar();
DefaultBoundedRangeModel currentModel = (DefaultBoundedRangeModel) horizBar.getModel();
int extent = currentModel.getExtent();
int min = currentModel.getMin();
int max = currentModel.getMax();
int value = currentModel.getValue();
DefaultBoundedRangeModel newModel = new DefaultBoundedRangeModel(value, extent, min, max);
horizBar.setModel(newModel);
}
Note code not tested
Related
I currently have a simple Java AWT/Swing code that creates a simple GUI that takes multiple String User-input and stores and displays it in the Intellij terminal like so:
import javax.swing.*;
import java.awt.*; // Using AWT container and component classes
import java.awt.event.*; // Using AWT event classes and listener interfaces
import java.util.ArrayList;
import java.util.Scanner;
// An AWT program inherits from the top-level container java.awt.Frame
public class DateTime extends JFrame implements ActionListener {
private Label lblCount, lblsource, lbldate1, lbldate2; // Declare a Label component
private JTextField tfCount, date1, date2; // Declare a TextField component
private Button btnCount; // Declare a Button component
private int count = 0; // Counter's value
static String type = null;
private JCheckBox source1, source2;
boolean a = false;
boolean b= false;
static String source, datedefined1, datedefined2;
ArrayList<String> texts = new ArrayList<String>();
// Constructor to setup GUI components and event handlers
public DateTime () {
setLayout(new FlowLayout());
// "super" Frame, which is a Container, sets its layout to FlowLayout to arrange
// the components from left-to-right, and flow to next row from top-to-bottom.
lblCount = new Label("Enter the type of report you want generated; Hourly/ Daily/ Weekly/ EventComparison:"); // construct the Label component
add(lblCount); // "super" Frame container adds Label component
tfCount = new JTextField("", 20); // construct the TextField component
tfCount.setEditable(true); // set to read-only
// "super" Frame container adds TextField component
tfCount.setBounds(10,50,200,40);
add(tfCount);
tfCount.addActionListener(this);
lblsource = new Label("Now choose the source type:");
add(lblsource);
source1 = new JCheckBox("Drivetest", a);
source1.setBounds(10,100,50,30);
add(source1);
source2 = new JCheckBox("Ookla Dump",b);
add(source2);
source1.addActionListener(this);
source2.addActionListener(this);
lbldate1 = new Label("Please enter the Start DATETIME of the chosen duration(YYYY-MM-DD HH:MM:SS) :");
add(lbldate1);
date1 = new JTextField("", 30); // construct the TextField component
date1.setEditable(true);
add(date1);
date1.addActionListener(this);
lbldate2 = new Label("Please enter the end DATETIME of the chosen duration(YYYY-MM-DD HH:MM:SS): ");
add(lbldate2);
date2 = new JTextField("",30);
date2.setEditable(true);
add(date2);
date2.addActionListener(this);
// set to read-only
// "super" Frame container adds TextField component
// "btnCount" is the source object that fires an ActionEvent when clicked.
// The source add "this" instance as an ActionEvent listener, which provides
// an ActionEvent handler called actionPerformed().
// Clicking "btnCount" invokes actionPerformed().
setTitle("Report Generator"); // "super" Frame sets its title
setSize(800, 700); // "super" Frame sets its initial window size
// For inspecting the Container/Components objects
// System.out.println(this);
// System.out.println(lblCount);
// System.out.println(tfCount);
// System.out.println(btnCount);
setVisible(true); // "super" Frame shows
// System.out.println(this);
// System.out.println(lblCount);
// System.out.println(tfCount);
// System.out.println(btnCount);
}
// The entry main() method
public static void main(String[] args) {
// Invoke the constructor to setup the GUI, by allocating an instance
DateTime app = new DateTime();
// or simply "new AWTCounter();" for an anonymous instance
}
// ActionEvent handler - Called back upon button-click.
#Override
public void actionPerformed(ActionEvent evt) {
Object actionsource = evt.getSource();
if(actionsource instanceof JTextField){
JTextField dateget1 = (JTextField) evt.getSource();
JTextField dateget2 = (JTextField) evt.getSource();
if (dateget1 == date1){
datedefined1 = date1.getText();
System.out.println(datedefined1);}
else if(dateget2 == date2){
datedefined2 = date2.getText();
System.out.println(datedefined2);}
else{
type = tfCount.getText();
System.out.println(type);
}
}
else if(actionsource instanceof JCheckBox){
JCheckBox cb = (JCheckBox) evt.getSource();
if(cb == source1){
source = "Drivetest";
System.out.println(source);
}
else if(cb == source2){
source = "Ookla Data Dump";
System.out.println(source);
}
}
}
}
The thing is, my main program needs to take in and store multiple string variables(ie. type, source, date1 AND date2) before it should execute.
My code for a normal terminal-style running of the program would look like this:
System.out.println("Enter the report type you would like: DailyComparison or HourlyComparison or WeeklyComparison or EventComparison; Type the exact words!");
type = scan.next();
System.out.println("Now enter the type of data you would like analysed: OOKLA or ManualTest: ");
source = scan.next();
if("DailyComparison".equals(type) || "HourlyComparison".equals(type) || "WeeklyComparison".equals(type) ){
Scanner scan2 = new Scanner((System.in));
System.out.println("Now enter the lower bound of the DateTime range(FORMAT YYYY-MM-DD HH:00:00):");
date1 = scan2.nextLine();
System.out.println("Now enter the upper bound of the DateTime range(FORMAT YYYY-MM-DD HH:00:00):");
date2 = scan2.nextLine();
}
Where user-input is taken through the terminal as normal.
The user input is then used to run the rest of the program, calling the methods in other classes i have defined:
Report.report(date1, date2, type, filename, source);// Creates the excel .xlsx file report
MailSender.MailSender(filename, type); // Send a email containing the attached report xlsx file
So my question is: How do i extend this GUI code's functionality so that the user-input string variables can ALL be gathered first then used to run the rest of the program?
EDIT:
Thanks for the advice guys.
I sort of got it to work, but I'm not sure if the structure is sound. What was happening previously was that since each component was handling a different variable and i wanted to store all the variables first before calling the main method classes that would process these variables.
So i created an additional button called "Generate Report" and under the actionlistener condition+action for this button, I placed the class.methods like so. Where basically I key in all my variables in the respective components(checkboxes, button etc) THEN press "generate report"
if (evt.getActionCommand() == "Generate Report") {
if ("DailyComparison".equals(type)) {
filename = "\\Users\\User\\Documents\\Reports\\" + " Daily SpeedTest Telco Comparison Report";
datedefined3 = null;
datedefined4 = null;
datedefined5 = null;
datedefined6 = null;
} else if ("WeeklyComparison".equals(type)) {
filename = "\\Users\\User\\Documents\\Reports\\" + " Weekly Telco Comparison Report";
datedefined3 = null;
datedefined4 = null;
datedefined5 = null;
datedefined6 = null;
} else if ("HourlyComparison".equals(type)) {
filename = "\\Users\\User\\Documents\\Reports\\" + "Hourly Telco Comparison Report";
datedefined3 = null;
datedefined4 = null;
datedefined5 = null;
datedefined6 = null;
}
if("HourlyComparison".equals(type)|"DailyComparison".equals(type)|"WeeklyComparison".equals(type)) {
Report.report(datedefined1, datedefined2, datedefined3, datedefined4, datedefined5, datedefined6, type, filename, source);// Creates the base excel .xlsx file report
LinechartGenerator.chartgen(0, "upload", datedefined1, datedefined2, datedefined3, datedefined4, datedefined5, datedefined6, source, type, filename);
LinechartGenerator.chartgen(0, "download", datedefined1, datedefined2, datedefined3, datedefined4, datedefined5, datedefined6, source, type, filename);
LinechartGenerator.chartgen(0, "latency", datedefined1, datedefined2, datedefined3, datedefined4, datedefined5, datedefined6, source, type, filename);
}
}
Although the code has its limitations where I cannot press generate report first or the program will just throw an error seeing as no variables were stored.
I also faced a roadblock where I'm trying to find the Swing-equivalent of the Flush Scanner function to allow the user to generate multiple reports in the same program instance.
This would invoke some basic principles:
Model-View-Controller - where the "data" is separated from the view and mechanisms used to collect it
Observer pattern - which is used to generate notifications when some state changes so interested parties can take action.
The observer pattern is used extensively in most UI frameworks, which tend to be event driven (something happens, you respond to it), rather then procedural or linear driven.
Typically, you would create a "form" which included the fields which would capture the data you need and some kind of "button", which when pressed, would initiate the next step - validating the data, building the model and generating the notification that the form has been completed.
The observer to this would then take the model and process it as required.
These are just some of the basic concepts used in UI development. Have a look at:
Creating a GUI With JFC/Swing
How to use buttons
How to write Action Listeners
for some more details
I noticed that you set this as the action listener for all the check boxes and textfields. What I think you really want, is to only process the user input when the user clicks the button, right?
Remove lines like these:
tfCount.addActionListener(this);
so that this only handles the click of the button.
Now, you can access all the inputs in the actionPerformed method:
dateDefined1 = date1.getText();
dateDefined2 = date2.getText();
type = tfCount.getText();
if (source1.isChecked()) {
source = "Drivertest";
} else {
source = "Ookla Data Dump"
}
// now you can use dateDefined1, dateDefined2, type and source!
Also, why use check boxes but not radio buttons? For this kind of "select one from the below option" usage, radio buttons are better suited.
You may break down this in two different jars:
Read the input and write that in a temp file.
Once the input has been read and written to the file, invoke your GUI jar application this can read contents from the temp file.
You may refer to this question on how to invoke jar from java application.
I have a JTable and I can't manage to make it update its content, I am in IntelliJ IDEA.
I'll share some code first
I have all the objects declared in the main class, which extends a JFrame
private Object[][] transactionsData;
private String[] transactionsColumnNames = new String[] {"uid","data","ora","product","ini","amount","final"};
private DefaultTableModel transactionsModel;
private JTable transactionsTable;
Then in the createUIComponents() method, that I managed to understand, is called at the very beginning, when the UI needs to be created, I initialize the table like this:
transactionsData = Main.db.getTransactions();
transactionsModel = new DefaultTableModel(transactionsData,transactionsColumnNames);
transactionsTable = new JTable(transactionsData,transactionsColumnNames){
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component component = super.prepareRenderer(renderer, row, column);
int rendererWidth = component.getPreferredSize().width;
TableColumn tableColumn = getColumnModel().getColumn(column);
tableColumn.setPreferredWidth(Math.max(rendererWidth + getIntercellSpacing().width, tableColumn.getPreferredWidth()));
return component;
}
};
transactionsTable.setRowSelectionAllowed(true);
transactionsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
transactionsTable.setDefaultEditor(Object.class, transactionsEditor);
transactionsTable.getTableHeader().setFont(new Font("Segoe",Font.PLAIN,16));
sorter = new TableRowSorter<>(transactionsTable.getModel());
transactionsTable.setRowSorter(sorter);
transactionsTable.getSelectionModel().addListSelectionListener(e -> {
if ( transactionsTable.getSelectedRows().length > 0 ) {
logUIDField.setText(transactionsTable.getValueAt(transactionsTable.getSelectedRow(), 0).toString());
transactionsTable.clearSelection();
uidFilter();
}
});
Now, each time I click a button i switch the content of the frame and the table is displayed, I need it to update its content before being displayed, because its content is taken from an online database, and of course, its content may have changed, so I do this:
storicoButton.addActionListener(e -> {
if( Main.db.hasTag() )
logUIDField.setText(Main.db.tag.getUid());
else
logUIDField.setText("");
transactionsData = Main.db.getTransactions();
transactionsModel = new DefaultTableModel(transactionsData, transactionsColumnNames);
transactionsTable.setModel(transactionsModel);
uidFilter();
CardLayout cardLayout = (CardLayout) rootPanel.getLayout();
cardLayout.show(rootPanel, "Card4");
});
But I can't manage to make it update its content, I tried everything I could find on the internet, and on this site, but everyone says to just use setModel, but it doesn't work, and I can't find what I'm doing wrong.
I tried to read the sizes of transactionsData, transactionsModel and transactionsTable before and after I click the button, transactionsData and transactionsModel actually update, since their size changes accordingly ( I checked looking at the database while I ran the program ) but transactionsTable doesn't change at all.
I tried repaint(), various methods that looks like "updateSomething", I tried with local variables, global variables, initializing new model, updating the model, initializing the table with the model or with data and columns, nothing. I'm desperate.
I hope someone here can help me.
At the moment here is late, and tomorrow I need to wake up early, after work I'll try starting clean, rewriting everything from scratch, maybe I'll find the solution myself, in the mean time I hope someone here could push me in the right direction at least.
Il post the code of that uidFilter() which I found online, it's the first thing I'm going to remove tomorrow, because I suspect it can change something, but I don't have the time to do that now
private void uidFilter() {
RowFilter<TableModel, Object> rf = null;
//If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter(logUIDField.getText(), 0);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
I'm having difficulty changing unselected options in a JList table to set them to setEnable(false). The method that is receiving the values is an ActionListener button method that, once pressed, receives the selected values from the JList. Here is the method and the buildEnemySelectionPanel() method is creating the JList with the appropriate JPanel for later placement:
private String[] enemies = {"Goblin", "Skeleton"};
private void buildEnemySelectionPanel()
{
enemyPanel = new JPanel();
enemyListPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
enemyListPanel.setPreferredSize(new Dimension(180, 85));
enemyListPanel.setBackground(Color.WHITE);
enemyPanel.setLayout(new BoxLayout(enemyPanel, BoxLayout.Y_AXIS));
enemyList = new JList(enemies);
enemyList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
enemyList.addListSelectionListener(new EnemyListListener());
enemyListPanel.add(enemyList);
enemyPanel.add(enemyListPanel);
}
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals("Select Enemy"))
{
indexEnemy = enemyList.getSelectedIndex();
indexEnemyWeapon = weaponList.getSelectedIndex();
/*
here is where I'm having problems
*/
}
}
So far I've tried to take all of the items from the JList and, matching them with the references from the original String[] list items that I sent to the JList, parsed the indexes and if they didn't match set to false. Unfortunately as you are all probably well aware, compilation errors came up as result due to the fact that the JList is not actually a list. Here is a sample of my for loop that I tried to use in my method above:
for(int x = 1; x < enemyList.length(); x++)
{
if (!(enemies[x] == indexEnemy))
{
enemyList[x].setEnable(false);
}
}
I've read the http://docs.oracle.com/javase/8/docs/api/ , (tried to link 'setEnable') among some examples but don't seem to be making the connection.
Ideally, what I wish to happen is that when the ActionEvent of my button is triggered, all non-selected options in my JList will be disabled. I understand that the end-user will still be able to change his/her mind and make a different selection. But I'd like to still receive some help on how I can set the non-selected items in my JList to false if they are not the indexEnemy from my method above.
I have the following class ReviewPanel in which i have created a JList and trying to populate it with Vector passed to it. the vector already has its values filled but when i add it to JList it doesn't shows anything.
following is my class
public class ReviewPanel extends JPanel
{
private Vector bookList;
private JList jl;
private JRadioButton poor,fair,avg,good,exlnt;
private JButton jb;
public ReviewPanel(Vector bookList)
{
this.bookList = bookList;
jl = new JList<String>(this.bookList);
String tmp=null;
for(int i=0;i<bookList.size();i++){
tmp = tmp + bookList.get(i) + "\n";
System.out.println(tmp); //here it is showing the values in booklist vector
}
add(jl);
ButtonGroup bg =new ButtonGroup();
poor = new JRadioButton("1 Poor");
fair = new JRadioButton("2 Fair");
avg = new JRadioButton("3 Average");
good = new JRadioButton("4 Good");
exlnt = new JRadioButton("5 Excellent");
bg.add(poor);
bg.add(fair);
bg.add(avg);
bg.add(good);
bg.add(exlnt);
add(poor);
add(fair);
add(avg);
add(good);
add(exlnt);
jb = new JButton("Submit Review");
add(jb);
}
//more code
}
below is the pic for what it looks like. even though i have added the Jlist already. it doesn't show anything
not savvy to jlist and vectors, any help appreciated.
In the past I have had simular issues with the FlowLayout, which is the default layout for a JPanel.
It sometimes does not show one of the items added (in my case it was often the last).
You can test if the use of your layout manager is your problem by starting you function with setLayoutManager(null). This turns is off, which will place the elements on their default locations and sizes. If the JList does show this is your problem.
A very good example for your program is the Button Demo. This also shows the use of SwingUtils to make sure you create your program on the 'Event Dispatch Thread', which is required for all interaction with Swing objects.
I have several components, all inheriting JCheckBox without overwriting anything from it, stored in a vector, which is then traversed and each of the components is added to a dialog
CreateLists(); // initialises the checkbox vector
for(int i = 0; i < checkBoxes.size() ; i++){
myPanel.add(checkBoxes.elementAt(i));
}
Some of these checkboxes are already selected.
My problem now is: When I open the dialog and select or unselect any checkbox, the value of the checkboxes in the vector doesn't change. Selected stays selected and unselected stays unselected.
I also tryed to get the new values by using JPanels getCompoents(), but the values of these are wrong, too.
An ItemListener in the checkbox inheritors confirmes that the changes do happen, but whenever I try to get the new values, there just the same as those with which the checkboxes were initialised.
Here is a console output I used to keep track of the changes:
create lists
print values:
checkBox1 = true
checkBox2 = true
checkBox3 = false
checkBox2 clicked new value = false
checkBox3 clicked new value = true
print values:
checkBox1 = true
checkBox2 = true
checkBox3 = false
Here is some more code and information:
CreateList() compares a watchlist with the watchable things, creates the checkboxes accordingly (true = watched etc) and adds them to the new initalised vector.
To read the values i use this:
Component[] components = pnlGesamt.getComponents();
for(int i = 0; i < components.length; i++){
if(components[i] instanceof WLElementCheckBox){
WLElementCheckBox cb = (WLElementCheckBox) components[i];
System.out.println(cb.WlElement().Name() + " = " + cb.isSelected());
}
}
The JCheckBox inheritor:
private WatchListElement wlElement;
public WLElementCheckBox (WatchListElement wl, boolean selected)
{
super();
WlElement(wl);
setSelected(selected);
setText(wlElement.Name());
addItemListener(new MIL());
}
public WatchListElement WlElement ()
{
return wlElement;
}
public void WlElement (WatchListElement wlElement)
{
this.wlElement = wlElement;
}
public class MIL implements ItemListener{
public void itemStateChanged(ItemEvent arg0) {
System.out.println("Ckb " + wlElement.Name() +" geklickt Item neu = " + isSelected());
}
}
There is a small possibility that the changes to your checkboxes are not visible because you're querying them from another thread (such as main) instead of the Event Dispatch Thread (EDT). Swing is not thread-safe. When you check the checkboxes, the values are set on the EDT, but other threads might have the old state saved in memory and don't check to see if it's been updated. So, the first thing you need to do is ensure that when you're checking the states of your checkboxes, you're doing it from the EDT, using a call to SwingUtilities.invokeAndWait() or SwingUtilities.invokeLater().
For more information on why this happens, search for Thread Safety in Java.
(Also, don't be tempted to use synchronization to solve the problem or you might end up with a deadlock in your GUI).
One final pro tip: using new Java for-loop syntax makes code easier to read and write, as well as more efficient in some cases. Your code...
for(int i = 0; i < checkBoxes.size() ; i++){
myPanel.add(checkBoxes.elementAt(i));
}
... can turn into...
for(JCheckBox c : checkBoxes) {
myPanel.add(c);
}
This works on arrays and anything that is Iterable (which includes Collection).