Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: GUI и Java FX приложения > Зависание GUI


Автор: shutffl 29.6.2011, 17:11
Доброго времени суток!!

Жизнь заставила после 10 лет отбрыкивания от Java таки сесть за её изучение. И сразу же решил написать проект по работе дабы в боевых действиях познать прелести языка. Суть проблемы сводится к подвисанию GUI. Сразу привожу код.

mWindow.java:
Код

package su.shadowmoon.testcv;



import java.awt.BorderLayout;

import java.awt.EventQueue;



import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.border.EmptyBorder;

import java.awt.GridLayout;

import javax.swing.JButton;

import su.shadowmoon.canvas.Canvas;

import java.awt.event.ActionListener;

import java.awt.event.ActionEvent;



public class mWindow extends JFrame {



    private JPanel contentPane;



    /**

     * Launch the application.

     */

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            public void run() {

                try {

                    mWindow frame = new mWindow();

                    frame.setVisible(true);

                } catch (Exception e) {

                    e.printStackTrace();

                }

            }

        });

    }



    /**

     * Create the frame.

     */

    public mWindow() {

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setBounds(100, 100, 450, 300);

        contentPane = new JPanel();

        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));

        setContentPane(contentPane);

        contentPane.setLayout(new GridLayout(1, 0, 0, 0));

        

        Canvas panel = new Canvas();

        contentPane.add(panel);

        

        final JButton button = new JButton("0");

        button.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {

                button.setText("" + (Integer.parseInt(button.getText()) + 1));

            }

        });

        contentPane.add(button);

    }



}


Canvas.java:
Код

package su.shadowmoon.canvas;



import java.awt.*;

import java.util.Random;



import javax.swing.*;



public class Canvas extends JPanel {

    

    public void Canvas () {

        

        setSize(300, 300);

        repaint();

        

    }

    

    public void paintComponent(Graphics g) {

        super.paintComponent(g);        

        Graphics2D g2 = (Graphics2D) g;

        

        g2.drawRect(0, 0, 300, 300);

        

        for (int i=0; i<1000000; i++) {

            g2.drawLine(

                    (int)Math.round(-300 + 600*Math.random()),

                    (int)Math.round(-300 + 600*Math.random()),

                    (int)Math.round(-300 + 600*Math.random()),

                    (int)Math.round(-300 + 600*Math.random())

                    );

        }

    }

    

}



Прошу сильно не бить, ибо язык почти не знаю - могут быть серьезные огрехи, но логика есть логика. В данном примере, как это видно из кода, JPanel рисует 1000000 количество линий в области 300*300 пикселей. Это сделано для задержки.

Как сделать, чтобы GUI реагировал во время рисования линий? То есть, чтобы работала нажатие на кнопку, считающую количество нажатий на ней. Это элементарный кусок кода, переписаный аналогчно (по принципу работы GUI) реальному приложению, которое должно обрабатывать в реальном времени изображение с камеры и делать фотки по нажатию на кнопку. Но и там, и там GUI просто висит. Знаю, что ответ где-то рядом. Подозреваю, что копать надо в сторону "Is Java threadsafely?". Но с потоками на Java пока туго, ибо куда их тут впихнуть - я хз.

Заранее спасибо.

Автор: jk1 29.6.2011, 18:07
Цитата

Как сделать, чтобы GUI реагировал во время рисования линий?


Рисовать линии в отдельном потоке на off-screen изображении и затем выводить на экран. Хотите видеть промежуточные результаты  - отрисовывайте полуготовое изображение.

Цитата

Is Java threadsafely?


Про сам язык так говорить не слишком корректно. А вот про Swing, который Вы используете, можно сказать однозначно - небезопасен. Смотрите в сторону класса SwingWorker, который как раз предназначен для выноса тяжелых задач из EDT (основной поток Swing'а).
Публикацию промежуточных результатов SwingWorker также поддерживает. 

http://developers.sun.ru/content/view/159/89/

Автор: shutffl 30.6.2011, 08:45
Спасибо, почитаю.

Автор: shutffl 30.6.2011, 10:31
Хорошо, а если вместо заглушки вставить код на вывод потокового видео или аудио? Например, используя JavaCV или VLC Java binding? 

Автор: jk1 30.6.2011, 11:35
Цитирую документацию
Цитата

JavaCV also comes with hardware accelerated full-screen image display (CanvasFrame),


так что здесь все уже cделано за Вас, этот компонент сам будет все отрисовывать.

Автор: aleksandy 1.7.2011, 07:17
Думаю не лишним будет ознакомиться вот с этим: http://www.skipy.ru/technics/gui_int.html, http://www.skipy.ru/technics/gui_sync.html.

Автор: shutffl 1.7.2011, 11:09
Спасибо, сейчас приступаю к ознакомлению. До этого писал нечто подобно на связке Qt + libvlc, но там был другой проект - радио. На Gtk+ так и не получилось адекватно реализовать потоки, чтобы GUI не подвисал. А тут решил под Android ПО пописать системное, а офф язык раработки только Java (C/C++ и python слишком коряво реализованы плюс в GUI все равно JavaScript-функция "биндится" для стороннего кода).

Автор: shutffl 1.7.2011, 11:45
Цитата(jk1 @  30.6.2011,  12:35 Найти цитируемый пост)
так что здесь все уже cделано за Вас, этот компонент сам будет все отрисовывать.

Да, но от требует NamedWindow, что для моих целей не годится. Грубо говоря, мне надо, чтобы было две компоненты 300*300 (реально 320*240) в горизонтальном layout, где в одном идет потоковое изображение с камеры, а во второй попадает изображение (фото) по нажатию кнопки. После этого фото можно сохранить.

Автор: jk1 1.7.2011, 13:40
Цитата

Да, но от требует NamedWindow, что для моих целей не годится. Грубо говоря, мне надо, чтобы было две компоненты 300*300 (реально 320*240) в горизонтальном layout, где в одном идет потоковое изображение с камеры, а во второй попадает изображение (фото) по нажатию кнопки. После этого фото можно сохранить.

Не прошло и недели, а мы уже выяснили в чем же состоит задача.  smile 

А если серьезно, то вот что я могу Вам предложить. Запускаете отдельный SwingWorker, который читает с камеры и получает BufferedImage.
Код

IplImage img = grabber.grab();
...
BufferedImage image = img.getBUfferedImage();

Прочитанные изображения публикуются как промежуточный результат (возможно пачками). Реализация метода process() в вашем SwingWorker() передает изображение панели и вызывает её принудительную перерисовку.

А дальше надо будет смотреть, что выйдет. Если будут задержки и тормоза возможно придется прикрутить промежутчный буфер для изображений, а панель перерисовывать по таймеру. 

Автор: shutffl 1.7.2011, 14:21
Спасибо. Про BufferedImage уже знал, так как само изображение получаю. Проблема была в том, что поток буфферизируется и изображения по клику мышки получаются не текущие (которых даже не видно), а следующие в потоке.

Автор: shutffl 1.7.2011, 14:54
На C++ (с GUI на Qt) уже все написал. Но хочу на Java.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)