0

So I have written a Java application, that provides a transparent Heads up display at the top of the screen, it works perfectly on windows, but on my kubuntu 16.04 machine it does not clear the old label when you change the labels text, you end up with a ton of overlapping mess.

because a picture is worth a thousand words, the top is how it looks in windows, the bottom is how it looks under kubuntu:

https://s23.postimg.org/yra0vvlvf/rawr.png

here is the code:

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.net.URL;
import javax.swing.*;
import java.io.*;

public class spob extends JFrame implements WindowFocusListener
{
    public spob()
    {
        if (!SystemTray.isSupported()) {
            System.out.println("SystemTray is not supported");
            return;
        }
        final TrayIcon trayIcon = new TrayIcon((new ImageIcon("icon.png", "trayicon")).getImage());
        final SystemTray tray = SystemTray.getSystemTray();
        trayIcon.setImageAutoSize(true);
        trayIcon.setToolTip("spO2 pr monitor");        
        try {
            tray.add(trayIcon);
        } catch (AWTException e) {
            System.out.println("TrayIcon could not be added.");
            return;
        }
        setType(javax.swing.JFrame.Type.UTILITY);
        setUndecorated(true);
        getContentPane().setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
        setBackground(new Color(1.0f,1.0f,1.0f,0.0f));
        setSize(400, 35);
        JLabel label = new JLabel("Loading...");
        label.setFont(new Font("Tahoma", Font.BOLD, 28));
        label.setForeground(Color.GREEN);
        add(label);
        setLocation(800, 0);
        addWindowFocusListener(this);
        setAlwaysOnTop( true );
        this.setFocusable(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setVisible(true);
        URL url = null;
        BufferedReader in = null;
        String[] anArray = new String[10];
        anArray[0] = "<html><font color=green>- spO2:91  pr:65</font></html>";
        anArray[1] = "<html><font color=red>+ spO2:85  pr:77</font></html>";
        anArray[2] = "<html><font color=green>- spO2:90  pr:68</font></html>";
        anArray[3] = "<html><font color=orange>+ spO2:89  pr:76</font></html>";
        anArray[4] = "<html><font color=orange>- spO2:89  pr:72</font></html>";
        anArray[5] = "<html><font color=orange>+ spO2:88  pr:73</font></html>";
        anArray[6] = "<html><font color=red>- spO2:87  pr:78</font></html>";
        anArray[7] = "<html><font color=red>+ spO2:86  pr:73</font></html>";
        anArray[8] = "<html><font color=green>- spO2:92  pr:74</font></html>";
        anArray[9] = "<html><font color=green>+ spO2:90  pr:71</font></html>";
        while (true){
            try {
                Thread.sleep(200);
                //url = new URL("http://192.168.1.153/stat.php");
                //in = new BufferedReader(new InputStreamReader(url.openStream()));
                //label.setText(in.readLine().toString());
                Random randomno = new Random();
                label.setText(anArray[randomno.nextInt(9 - 1) + 1]);
            } catch (Exception ex) {
            } finally {
                //try {
                //  in.close();
                //} catch (IOException e) {
                //}
            }
        }
    }

    public void windowGainedFocus(WindowEvent e){}
    public void windowLostFocus(WindowEvent e)
    {
        if(e.getNewState()!=e.WINDOW_CLOSED){
            setAlwaysOnTop(false);
            setAlwaysOnTop(true);
        }

    }

    public static void main(String[] args) 
    {
    new spob();
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
Jieiku
  • 489
  • 4
  • 15
  • getContentPane().setBackground(new Color(1.0f,1.0f,1.0f,0.0f)) is your problem, Swing doesn't know how to deal with an opaque component with a aloha based color (JFrame is a special case). In your case, simply make a new instance of JPanel, set its opaque state to false and apply it as the content panel to the frame (and update the layout manager if needed) – MadProgrammer Jan 24 '17 at 09:32
  • Swing is also not thread safe, your while loop is either going to block the ETD or violate the singe thread rules if Swing, in either case, you should be using a Swing Timer – MadProgrammer Jan 24 '17 at 09:35
  • do you mean instead of updating the label with label.setText, I would instead be updating the entire jpanel?(to make it work on kubuntu) ... interesting, much of the code was put together using examples and documentation available on the internet. what happens when the ETD is blocked? so far its been running reliably the last couple weeks on windows, I will look into the swing timers. – Jieiku Jan 24 '17 at 09:43
  • I tried your suggestion, but now its lost its transparency in windows, and did not fix the problem in kubuntu: http://pastebin.com/ZhiSAH8P picture: https://s28.postimg.org/j16gcgmzx/help2.png in picture top is original java app in windows, buttom is in windows with the edits.(lost its transparency.) – Jieiku Jan 24 '17 at 10:24

1 Answers1

1

So, a number of issues

  • You're violating the single threaded rules of Swing, essentially, updating the UI from outside the context of the EDT, this can cause issues if the system is trying to paint something while you're trying to update it
  • getContentPane().setBackground(new Color(1.0f,1.0f,1.0f,0.0f)); - Swing doesn't know how to deal with opaque components which have an alpha based color, it tends to not to update the any of the components beneath it.

Transparent windows are ... fun ... they tend to introduce their own issues beyond what we would normally expect.

On my Mac system I was able to reproduce the issue, but inconsistently. This was especially apparent, because the Mac OS keeps rendering a shadow around the text.

The first thing I got rid of was setType(javax.swing.JFrame.Type.UTILITY);, I also added a repaint request of the label's parent container which seems to have solved the symptoms of the problem, but again, I was able to execute the code without at times.

If you want to update the UI periodically, you should use a Swing Timer, see How to use Swing Timers for more details. If you need to do something in the background and then update the UI, you should use a SwingWorker, have a look Worker Threads and SwingWorker for more details

Translucent example

(wow is me, it doesn't like my animated gif :()

The example deliberately uses a translucent background, it's intended to show the frame. Change pane.setAlpha(0.5f); to pane.setAlpha(0.0f); to make it fully transparent (I've tested that as well).

If you have issues, uncomment the line label.getParent().repaint(); in the Timer and see if that helps

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    private JLabel label;
    private String[] anArray = {
        "<html><font color=green>- spO2:91  pr:65</font></html>",
        "<html><font color=red>+ spO2:85  pr:77</font></html>",
        "<html><font color=green>- spO2:90  pr:68</font></html>",
        "<html><font color=orange>+ spO2:89  pr:76</font></html>",
        "<html><font color=orange>- spO2:89  pr:72</font></html>",
        "<html><font color=orange>+ spO2:88  pr:73</font></html>",
        "<html><font color=red>- spO2:87  pr:78</font></html>",
        "<html><font color=red>+ spO2:86  pr:73</font></html>",
        "<html><font color=green>- spO2:92  pr:74</font></html>",
        "<html><font color=green>+ spO2:90  pr:71</font></html>"
    };
    private Random randomno = new Random();

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setUndecorated(true);
                frame.setAlwaysOnTop(true);
                // Transparent window...
                frame.setBackground(new Color(255, 255, 255, 0));
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                BackgroundPane pane = new BackgroundPane();
                // Set this to 0.0f to make it fully transparent
                pane.setAlpha(0.5f);
                pane.setLayout(new BorderLayout());

                pane.setBorder(new EmptyBorder(10, 10, 10, 10));

                frame.setContentPane(pane);

                label = new JLabel("Loading...");
                label.setFont(new Font("Tahoma", Font.BOLD, 28));
                label.setForeground(Color.GREEN);
                frame.add(label);

                frame.pack();
                Dimension size = frame.getSize();
                size.width = 400;
                frame.setSize(size);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Timer timer = new Timer(200, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        label.setText(anArray[randomno.nextInt(9 - 1) + 1]);
//                      label.getParent().repaint();
                    }
                });
                timer.start();
            }
        });
    }

    public class BackgroundPane extends JPanel {

        private float alpha;

        public BackgroundPane() {
            setOpaque(false);
        }

        public void setAlpha(float alpha) {
            this.alpha = alpha;
            repaint();
        }

        public float getAlpha() {
            return alpha;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(getBackground());
            g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
            g2d.fillRect(0, 0, getWidth(), getHeight());
            g2d.dispose();
        }

    }

}

nb I'm not using openJDK, I'm using Java 8, that might make a difference

Testing for capabilities

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;

public class Test {

    public static void main(String[] args) {
        GraphicsEnvironment ge
                = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();

        boolean isUniformTranslucencySupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.TRANSLUCENT);
        boolean isPerPixelTranslucencySupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT);
        boolean isShapedWindowSupported
                = gd.isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSPARENT);

        System.out.println("isUniformTranslucencySupported = " + isUniformTranslucencySupported);
        System.out.println("isPerPixelTranslucencySupported = " + isPerPixelTranslucencySupported);
        System.out.println("isShapedWindowSupported = " + isShapedWindowSupported);
    }

}
Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • offtop: how is your daughter? she might be almost 5yo now, right? :) – SeniorJD Jan 24 '17 at 15:50
  • just was at http://stackoverflow.com/questions/11696833 where you mentioned her :) – SeniorJD Jan 24 '17 at 15:56
  • @SeniorJD 4.5 and testing every boundary :P – MadProgrammer Jan 24 '17 at 18:37
  • Thank you so much for writing the example, it functions with full transparency, and solves all the issues you pointed out. The only issue that is still unresolved is it still does not have transparency on Kubuntu, which ships stock with openJDK, I am thinking the transparency and overlapping garbled mess error on Kubuntu is something specific to openJDK, what I may do is see about installing Java 8 instead of openJDK on my kubuntu machine. – Jieiku Jan 25 '17 at 02:29
  • It could also be that it doesn't support per pixel alphering, I'll see if I can find an example of how to test for it – MadProgrammer Jan 25 '17 at 02:37
  • I just installed Java 8 on Kubuntu, and it still has the problem, so maybe the problem is not Java vs OpenJDK, I am now thinking maybe its a Kubuntu problem, or what you said about per pixel alphering. ALSO, setting the label to fully transparent, pane.setAlpha(0.0f); makes the issue more pronounced on Kubuntu, because with partially transparency, the older layers slowly get hidden, kinda like they are fading out. – Jieiku Jan 25 '17 at 02:40
  • So I just want to re-iterate, your example is working perfectly fine on windows, its only on Kubuntu that it does not work, and I have tried both openJDK and Java 8, problem is the same. Also fully transparent makes the issue way more obvious on Kubuntu. – Jieiku Jan 25 '17 at 02:44
  • Did you try uncommenting label.getParent().repaint(); ! I've found that sometimes makes a difference – MadProgrammer Jan 25 '17 at 02:49
  • yes, I did, it seems to have zero effect on kubuntu, I have tried on a physical kubuntu install as well as a virtual machine kubuntu install. I am running Kubuntu 16.04.1 – Jieiku Jan 25 '17 at 02:51
  • Have a look at [Determining a Platform's Capabilities](https://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html#capability) for the APIs you can use to determine the if transparent/translucentcy is supported. If these are successful, then I'd argue it's a bug – MadProgrammer Jan 25 '17 at 02:55
  • ok I figured out why I didnt have transparency, on kubuntu you have to make sure the compositor is enabled, I have transparency on kubuntu now, but I cannot figure out why it still makes a garbled mess showing old label texts all overlapped, it does it on kubuntu with both openjdk or java, heres a picture: https://s24.postimg.org/jzz9lirvp/kubuntu.png pastebin: http://pastebin.com/AjAtP3XN – Jieiku Jan 25 '17 at 14:22
  • I had a similar issue on the Mac which is why I use `label.getParent().repaint()` which seemed to fix it. You might consider using a very low alpha (0.01 - 0.05) and see if it makes a difference – MadProgrammer Jan 25 '17 at 22:30
  • Just tried 0.1f, also made sure the repaint line was not commented out, it wasn't. its good to know that its working on Mac, are there any other elements that I could have it try repainting on update, that might possibly fix the problem for kubuntu? since repainting the label fixed it for you on mac. Also by using the low opacity, I noticed it slowly forms a black box around the text, like maybe the transparency doesn't last. – Jieiku Jan 25 '17 at 23:57
  • Off the top of my head, the only thing you have left is to try and trigger a repaint in the frame itself – MadProgrammer Jan 26 '17 at 00:09
  • I am trying to use the code from Determining a platforms capabilities, but its not compiling, I gotta run an errand but I am going to try to figure out why it wont compile when I get home, the "GraphicsEnvironment ge = " and "GraphicsDevice gd = " lines say error class, interface, or enum expected. – Jieiku Jan 26 '17 at 00:14
  • I did the Determining a Platform's Capabilities, using this code pastebin: http://pastebin.com/vDs0yfXh on windows all 3 return true, but on my kubuntu one of them is False TRANSLUCENT picture: https://s28.postimg.org/lu4l0x1nh/kubuntu_result.png Both of the PERPIXEL ones returned true. – Jieiku Jan 26 '17 at 03:52
  • http://qubanshi.cc/questions/3513628/java-window-translucency-on-linux that post says he needed to use a NON VM machine, with dedicated graphics card drivers. My kubuntu machine uses intel cpu for integrated graphics, its an intel i5-3570k, wondering if maybe the graphics driver is to blame, lspci | grep VGA shows: 00:02.0 VGA compatible controller: Intel Corporation Xeon E3-1200 v2/3rd Gen Core processor Graphics Controller (rev 09) – Jieiku Jan 26 '17 at 04:37
  • since I have the perpixel transparent, and the program starts out looking good and gets progressively worse, i'm thinking maybe trying to repaint the frame itself might work, I am not really sure how to accomplish that. – Jieiku Jan 26 '17 at 05:03
  • [`SwingUtilities#getWindowAncestor`](https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#getWindowAncestor(java.awt.Component)) – MadProgrammer Jan 26 '17 at 06:15
  • 1
    Thank you for all the help MadProgrammer, I have a strong suspicion that it has to do with the graphics drive that kubuntu is using for my integrated intel graphics. I am going to round up a system with an nvidia card and retest, i will let ya know what I find. – Jieiku Jan 26 '17 at 07:00