I am currently working on a Swing Applet, and am having issues referencing my custom AWT Canvas component (very simple extended class) in other class functions, such as with any other component (i.e. button) normally created with Netbean (7.0)'s designer.
My custom canvas element I add here, I was sure this would be the appropriate place (especially after all other generated components were just created in the same area)
public void init() {
try {
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
CustomCanvas myCan = new CustomCanvas();
myCan.setBounds(100, 100, 200, 200);
getContentPane().add(myCan);
...
However, unlike the generated components, I cannot access them by name and cannot seem to access them through other means (this.myCan) either. I have set up a sample function that will handle a (generated) button on the Swing form to manipulate the previously instantiated myCan component:
private void btnManipCanvasActionPerformed(java.awt.event.ActionEvent evt) {
//Essentially Was wanting to call something here such as myCan.getGraphics().setRect...
}
Do you know of a way to access myCan there? Am I supposed to place custom initializations of components in a different area so they can be publicly accessed?
Just make the canvas an instance variable, as (I guess) all the other components created by the Netbeans designer:
private CustomCanvas myCan;
public void init() {
try {
java.awt.EventQueue.invokeAndWait(new Runnable() {
public void run() {
initComponents();
myCan = new CustomCanvas();
myCan.setBounds(100, 100, 200, 200);
getContentPane().add(myCan);
// ...
there are possible issues or painting lacks because you probably mixing ATW Components with Swing JComponets,
if there nothing special that came from OpenGL, then look for JPanel instead of AWT Canvas and for all panting in Swing redirect everythigns to JLabel
please read how LayoutManagers works to avoids setBounds(int, int, int, int);
Related
In my program I would like the user to be able to change the colour scheme of the program. I have a method which passes in a colour to set the background of all the panels using UIManager.
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(this);
}
However the issue I'm running into is that it is not changing the colour of the panels. This method is located in the class for the JFrame.
Copied directly from Swing API Docs.
public static void updateComponentTreeUI(Component c)
A simple minded look and feel change: ask each node in the tree
to updateUI() -- that is, to initialize its UI property with the
current look and feel.
Notice the emphasize. Your code doesn't work because you are passing this as the argument to updateComponentTreeUI(). Since you are passing your panel as the argument, only the components inside the panel and the panel itself will get their UI updated. You have to pass the container that holds all other panels in your program, that is your JFrame
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(frame); //instace of your frame
}
simply because the UIDefaults changes is only effective for the newly created components
for your feature to work you have to make the app restart then before starting the gui change the background in the defaults in the UIManager , or you will have to do it manually (panel by panel).
if you do it manually recursion will help a lot ,like this
private static void loopForPanel(Container c, Color col) {
synchronized (c.getTreeLock()) {
for (Component com : c.getComponents()) {
if (com instanceof JPanel) {
com.setBackground(col);
}
if (com instanceof Container) {
loopForPanel((Container) com,col);
}
}
}
}
then in your changeColourScheme method pass the frame it self for the loopForPanel method with your desired color.
I've just starting using Java Swing and I have a issue.
I tried to do a simple menuBar and a menuItem 'Exit', but before linking the button to the action the menuItem appeared, now that I've linked the button to a System.exit(0) action it disappeared. Help?
The code is the following:
in MainPanel (the autogenerated code from swing is excluded):
public void init() {
initComponents();
initActions();
setLocationRelativeTo(null);
pack();
setVisible(true);
}
private void initActions() {
this.menuItemExit.setAction(Application.getInstance().getPanelControl().getActionExit());
}
In PanelControl:
public class PanelControl {
private Action actionExit;
public Action getActionExit() {
return actionExit;
}
public class ActionExit extends AbstractAction{
public ActionExit(){
putValue(Action.NAME, "Exit");
putValue(Action.SHORT_DESCRIPTION, "Exit from the application");
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ctrl e"));
putValue(Action.MNEMONIC_KEY, KeyEvent.VK_E);
}
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
In Application:
private void init() {
viewMainPanel = new MainPanel();
controlPanel = new ControlPanel();
viewMainPanel.init();
}
i think the problem is somewhere in here but i can't figure out where. any help?
(there's other code but i just put the more relevant part, also i translated the code from italian so i'm sorry if there are any problems or a few names dont match up)
private Action actionExit;
public Action getActionExit() {
return actionExit;
}
Your actionExit variable is null.
Nowhere in your code do you create an instance of your ActionExit class.
Somewhere you need:
actionExit = new ActionExit();
Your design seems a bit complicated, I have no idea why you have a panel just to create an instance of the ActionExit class.
I would suggest you just create the ActionExit instance in your main class and get rid of the PanelControl class.
Instead of using an IDE to generate confusing code you should consider learning how to write the code yourself so you can better structure your classes. Read the section from the Swing tutorial on How to Use Menus for a working example to get you started.
A menu item has to be added to a Native Java Swing component. You have to add it to a JFrame. You can't add a MenuItem to a Panel. The Parent 'root' container in any Java Swing application is 'native' and a JFrame. Everything else in that container is 'drawn' into the rectangle using the look and feel of your choosing.
Then you CREATE a MenuItem using your TAbstractAction item. That object CAN be used to create a JButton, JMenuItem or ToolBar button. Keeping a reference to your TAbstractAction in your code, you can enable/disable the object and it implements an 'observable' pattern where it will enable/disable ALL UI controls you used to build with it. I actually wrote a Java Swing framework for doing Java Applications. It used to be on the Sun Open Source web site. If you wish I can put it up on GitLab for you to play with. Java Swing is nice but JavaFX should be the long term goal for UI on a JVM.
In your JFrame object you need to do this:
_menuBar = new JMenuBar();
// add controls to the frame
setJMenuBar(_menuBar);
Then you need to add your 'exitMenuItem' to your _MenuBar control.
Cheers
How can I embed a PApplet into a JApplet ?
I wanted to add it to a JPanel inside the applet, but I couldn't.
If any of you know how I can do this. ??
As of Processing 3, you can no longer do this. PApplet no longer extends Applet, so it can't be treated as a component.
For 95% of users, this is okay. Applets are dead anyway, so you really shouldn't be using them. If at all possible, you should try deploying with Processing.js.
If you need to execute a Processing sketch from Java code, then you should use the PApplet.main("YourSketchNameHere"); function to launch it. Processing will take care of the window for you.
If you really need to treat a PApplet as a component, then you're going to have to go through its PSurface. The PSurface class contains a getNative() function that returns an object that can be treated as a component. But that's overkill for most Processing users.
Prior to Processing 3, this code should have worked for you as #Kevin has explained. So, if your question is directed towards understanding a legacy code here is what you will need to know:
import javax.swing.JFrame;
import javax.swing.JPanel;
class MyPApplet extends PApplet implements ActionListener{
#Override
public void setup() {
super.setup();
// setup
}
#Override
public void draw() {
// my draw code
}
}
public class PAppletDemo {
public static void main(String[] args) {
final JFrame frame = new JFrame("PApplet in Java Application");
JPanel panel = new JPanel();
//create an instance of your processing applet
final MyPApplet applet = new MyPApplet();
applet.init();
panel.add(applet); // From processing 3, this will give you error that applet is not a Component
frame.add(panel);
frame.setSize(applet.getSize().width, applet.getSize().height +200);
frame.setVisible(true);
}
}
To circumvent this, you will need to use PSurface getNative() function. Please refer to the example and discussion given on this link.
I have a weird Problem with my Java GUI.
I can access the Methods in the Main Class from another Class but i cannot access the Swing Components.
Let me show you how i built the whole thing
Main Class:
public class GUI extends JFrame {
static Code c = new Code();
static Draw panel = new Draw();
JTextArea codelog;
JLabel lblFile;
...
...
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI frame = new GUI();
frame.create();
}
});
}
public void create() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1280,720);
...
...
contentPane = new JPanel();
setContentPane(contentPane);
contentPane.setBackground(Color.DARK_GRAY);
GridBagLayout gbl_contentPane = new GridBagLayout();
setResizable(false);
...
...
panel.setBackground(Color.BLACK);
gbc_panel.fill = GridBagConstraints.BOTH;
gbc_panel.gridx = 1;
gbc_panel.gridy = 1;
contentPane.add(panel, gbc_panel);
codelog = new JTextArea();
codelog.setEditable(true);
JScrollPane scrollPane_1 = new JScrollPane(codelog);
codelog.setLineWrap(true);
scrollPane_1.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
codelog.setVisible(true);
scrollPane_1.setVisible(true);
GridBagConstraints gbc_scrollPane_1 = new GridBagConstraints();
gbc_scrollPane_1.gridheight = 2;
gbc_scrollPane_1.gridwidth = 4;
gbc_scrollPane_1.fill = GridBagConstraints.BOTH;
gbc_scrollPane_1.gridx = 8;
gbc_scrollPane_1.gridy = 1;
contentPane.add(scrollPane_1, gbc_scrollPane_1);
...
...
}
public void refresh(){
panel.repaint();
}
}
I am using static Code c and static Draw panel to avoid multiple instances as i also have to create Objects of the Main class in other classes.
The other Class named Code
public class Code {
...
...
static GUI g = new GUI();
String test;
...
...
public void hpgl(){
g.codelog.append(test); // gives me nullPointerException !!
g.refresh // works
...
...
}
}
The Problem is that i can access the Methods of the Main Class (GUI) from other classes (such as Code) but i cannot access the Components (such as JTextArea).
The refresh() Method in the Main Class proves it. I can access the Method and in the Main Class the repaint() works. But if i try to repaint from another class using GUI.panel.repaint() it won't work because i would in that case access the panel directly from Code Class.
The Same goes for the JTextArea. I am trying to append codelog from Code but it won't let me do it. If i create a Method in Main Class which appends the Textarea and then call the Method from Code Class it works. But using g.codelog.append(test) gives me a Java null pointer exception
So i can access the Methods but i cannot access the Swing Components.
Can you guys please help me. I don't want to have to write an extra Method in the Main Class for every single Swing Component i want to modify.
Thank You
The UI which is visible on the screen is not the same UI you have created in your Code class. If you want Code to be able to access the UI properties, you will need to pass a reference of the GUI to it.
Having said that, I would be apposed to exposing the UI components directly to any class an instead provide getters and setters (where applicable) to provide access to the information been managed. This prevents rouge classes from making changes to the UI which it should be allowed to do (like remove components)
Depending on what you are doing, an Observer Pattern might be a better choice, where by Code is notified by GUI when something it might be interested in changes. If done through the use of interfaces, this will reduce the coupling between your classes and make it more flexible
Beware static is not a mechanism for providing cross object communication and should be avoid if at all possible, especially in something as dynamic as a GUI.
I was able to solve the Problem following MadProgrammer's Suggestion.
This is what i changed.
I have 3 Classes:
Main Class
Draw
Code
Main Class
public class GUI extends JFrame {
Draw panel = new Draw(this);
Code c = new Code(this);
...
...
}
Code Class
public class Code {
private GUI g;
private Draw b;
public Code(GUI g){
this.g = g;
}
...
...
}
Draw Class
public class Draw extends JPanel{
private GUI x;
private Code c;
public Draw(GUI x){
this.x = x;
}
...
...
}
I removed all the Static declarations. It is now working. I can access the Swing Components in the Main Class now.
Is this the Professional way to do it? or is there still room for improvement. This is the first time i used the passing reference way to do it. Until now i always used static Objects.
Thank You
Im using a JPanel with propertyChangeListener and want it to rerender itself based on whenever a particular variable model changes. My code for the same is as follows --
public class LabelMacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private LabelMacroModel model;
public LabelMacroEditor(LabelMacroModel bean) {
this.model = bean;
model.addPropertyChangeListener(this);
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
JScrollPane macroModelScroller = new JScrollPane(allButtons);
macroModelScroller.setPreferredSize(new Dimension(300, 200));
for(MacroModel macroModel : model.getMacroModelList())
{
LabelMacroEditorEditableEntity macroEditorEntity = new LabelMacroEditorEditableEntity(macroModel);
Box entityBox = Box.createHorizontalBox();
entityBox.add(macroEditorEntity.getUpButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getMacroDetailsButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getDownButton());
allButtons.add(entityBox);
}
add(macroModelScroller);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
When i use the debug mode in eclipse i can see that whenever there is a change to model it triggers off the call propertyChange and it also runs over revalidate and repaint but only the JPanel display remains the same. It does not seem to be rerendering itself.
Anything fundamental that I'm missing here ?
EDIT :
An example snippet of a property im changing is as follows --
labelMacroModel.addMacroModel(addedMacroModel);
where labelMacroModel is of the type LabelMacroModel and addedMacroModel is of the type Macro
Now the relevant part of LabelMacroModel class that fires off the property change is as follows --
private List<MacroModel> macroModelList;// this is the list of all MacroModels
public void addMacroModel(MacroModel macroModel) {
macroModelList.add(macroModel);
pcs.fireIndexedPropertyChange("LabelMacroModel", macroModelList.size(), null, macroModel);
}
Its not clear how you are changing the components in the panel. If panel is not updated then repaint/revalidate will have no effect. I think you should not need revalidate/repaint to be called explicitly if you are not modifying the way components are laid out. JButton.setText should for example change the label of the button without need of calling repaint.
To expand on the answer by AKJ above, I think you should be reconstructing your components on property change. So doing a remove all then readding is one way to do this. Once you get this working you could be more selective about pushing the model update into the GUI eg if a new entry has been added then just add a new component to reflect this. The remove all / readd is fine for a lot of cases though. HTH.