вторник, 21 августа 2012 г.

Управляемая сцена openGl

В прошлый раз я обещал управляемую сцену и сферу из примитивов -  Принимайте :-)

За подробностями реализации прошу пожаловать под кат.


Первое, что мы сделаем - Управляемую сцену. Охота, чтобы по нашему миру можно было путешествовать, как в компьютерных играх: с помощью мышки и клавиатуры WASD. Вспомним наш прошлый класс SceneGl 

class SceneGL : public QGLWidget


Он наследуется от QGLWidget, а QGLWidget наследуется от QWidget, QWidget может принимать и обрабатывать события клавиатуры и мыши. Воспользуемся этим - для этого дополним наш класс SceneGl такими функциями:


    void mousePressEvent(QMouseEvent* pe);
    void mouseMoveEvent(QMouseEvent* pe);
    void mouseReleaseEvent(QMouseEvent* pe);
    void wheelEvent(QWheelEvent* pe);
    void keyPressEvent(QKeyEvent* pe);

На данном этапе нам стоит отдалиться от кода и подумать о концепции необходимого нам функционала. Что мы хотим видеть в итоге?
  • Клавиши W,S - движение вперёд и назад соответственно;
  • Движение мыши, при зажатой левой кнопке - вращение камеры. 
  • Клавиши со стрелками - движение камеры.
  • Пробел - возврат на исходную позицию.
  • Esc - выход из программы.
  • Вращение колёсика мыши- изменение чувствительности скорости вращения к движению мыши.
Для обеспечения данного функционала мне понадобятся такие переменные:

    QPoint ptrMousePosition;


    GLfloat xRot;//поворот камеры относительно X
    GLfloat yRot;//поворот камеры относительно Y
    GLfloat zRot;//поворот камеры относительно Z
    GLfloat zTra;// перемещение камеры по оси Z( происходит перд поворотом)
    GLfloat nSca;// чувствительность мыши



Обрабатывать нажатие кнопки будем в функции keyPressEvent и там, с помощью case определять нажатую клавишу, и изменять параметры. Для удобоваримого чтения кода, и ради хорошего стиля( стиль - не пустой звук, так правда удобней), выделим на каждое изменение параметра по функции:



    void scale_plus();
    void scale_minus();
    void rotate_up();
    void rotate_down();
    void rotate_left();
    void rotate_right();
    void translate_down();
    void translate_up();
    void defaultScene();


Их названия говорят сами за себя. 

Заканчиваем с концепцией, пора перейти к реализации. Дабы не путать функциональный код и тривиальный код управления, я выделил отдельный файл controlescene.cpp.
Вот его содержимое:
#include "scenegl.h"
#include <QtGui>
#include <math.h>

void SceneGL::mousePressEvent(QMouseEvent* pe)
{
   ptrMousePosition = pe->pos();
}

void SceneGL::mouseReleaseEvent(QMouseEvent* pe)
{

}

void SceneGL::mouseMoveEvent(QMouseEvent* pe)
{

   xRot  = 180/nSca*(GLfloat)(pe->y()-ptrMousePosition.y())/height();
   zRot  = 180/nSca*(GLfloat)(pe->x()-ptrMousePosition.x())/width();

   ptrMousePosition = pe->pos();

   updateGL();
}

void SceneGL::wheelEvent(QWheelEvent* pe)
{
   if ((pe->delta())>0) scale_plus(); else if ((pe->delta())<0) scale_minus();

   updateGL();
}

void SceneGL::keyPressEvent(QKeyEvent* pe)
{
   switch (pe->key())
   {
      case Qt::Key_Plus:
         scale_plus();
      break;

      case Qt::Key_Equal:
         scale_plus();
      break;

      case Qt::Key_Minus:
         scale_minus();
      break;

      case Qt::Key_Up:
         rotate_up();
      break;

      case Qt::Key_Down:
         rotate_down();
      break;

      case Qt::Key_Left:
        rotate_left();
      break;

      case Qt::Key_Right:
         rotate_right();
      break;

      case Qt::Key_S:
         translate_down();
      break;

      case Qt::Key_W:
         translate_up();
      break;

      case Qt::Key_Space:
         defaultScene();
      break;

      case Qt::Key_Escape:
         this->close();
      break;
   }

   updateGL();
}

void SceneGL::scale_plus()
{
   nSca = nSca*1.1;
}

void SceneGL::scale_minus()
{
   nSca = nSca/1.1;
}

void SceneGL::rotate_up()
{
   xRot  = 1.0;
}

void SceneGL::rotate_down()
{
   xRot -= 1.0;
}

void SceneGL::rotate_left()
{
   zRot  = 1.0;
}

void SceneGL::rotate_right()
{
   zRot -= 1.0;
}

void SceneGL::translate_down()
{
   zTra -= 0.05;
}

void SceneGL::translate_up()
{
   zTra  = 0.05;
}

void SceneGL::defaultScene()
{
   xRot=0; yRot=0; zRot=0; zTra=0; nSca=1;
}





Перейдём к изменению scenegl.cpp.Добавьте в конструктор класса вызов функции обнуления переменных defaultScene(); и измените paintGL так:

void SceneGL::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0f, 0.0, -4.0f);
    glTranslatef(0.0f, 0.0, zTra);
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    glRotatef(zRot, 0.0f, 0.0f, 1.0f);и
    ...
Теперь про перемещение. 
Обратите внимание на то, что сначала я перемещаюсь, а потом поворачиваю оси, тем самым я поворачиваю из вокруг уже смещённого положения. В пространстве порядок действий очень важен. На этом рисунке показаны обе последовательности и результаты, к которым они приводят.


Теперь я хочу поговорить про актёров на нашей сцене. Всё, что мы хотим нарисовать должно вызываться в функции repaint. Я хочу дать возможность помещать объекты на сцену динамически. Для этого я придумал такую технологию :
Я создаю абстрактный класс GLobject, в нём создаю виртуальную функцию paint, на сцене создаю вектор объектов типа GLobject и в функции repaint вызываю функцию paint для каждого объекта из моего вектора. Создать объект GLobject мы не можем(класс абстрактный),но мы можем от него наследовать и в классе-наследнике переопределить функцию paint, и в ней рисовать то, что хотим


Ближе к делу. Вот класс GLobject
class ObjectGL : public QObject
{
    Q_OBJECT
public:
    explicit ObjectGL(QObject *parent = 0);
    virtual void paint()=0;
};

Я хочу, чтоб объекты жили своей жизнью во время работы программы. Для этого я делаю дополнительный слой абстракции -  AliveObjectGL. В создадим переменную QTimer, и ещё одну виртуальную функцию-слот live(). Эта функция будет вызываться таймером, в ней потомки будут меня параметры характеризующие их состояние. Вот код классов GLobject и AliveObjectGL :
#ifndef OBJECTGL_H
#define OBJECTGL_H

#include <QGLWidget>
#include <GL/glu.h>
#include <QObject>
#include <QTimer>
class ObjectGL : public QObject
{
    Q_OBJECT
public:
    explicit ObjectGL(QObject *parent = 0);
    virtual void paint()=0;
};



class AliveObjectGL : public ObjectGL
{
    Q_OBJECT
public:
    QTimer *timer;
    explicit AliveObjectGL(QObject *parent = 0,int delay = 10);
    void setdelay(int delay);
private:
        int delay;
public slots:
     virtual  void live()=0;

};


#endif // OBJECTGL_H


И реализация:
#include "objectgl.h"

ObjectGL::ObjectGL(QObject *parent) :
    QObject(parent)
{
}

AliveObjectGL::AliveObjectGL(QObject *parent,int delay) :
    ObjectGL(parent)
{
    this->delay = delay;
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(live()));
    timer->start(delay);
}

void AliveObjectGL::setdelay(int delay)
{
    this->delay = delay;
    timer->stop();
    timer->start(delay);
}


В private SceneGL создаю вектор указателей  QVector<AliveObjectGL*> actors;

После прорисовки осей рисую все объекты.
void SceneGL::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glTranslatef(0.0f, 0.0, -4.0f);
    glTranslatef(0.0f, 0.0, zTra);
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    glRotatef(zRot, 0.0f, 0.0f, 1.0f);

    drawAxis();
    foreach (AliveObjectGL* actor, actors){
        glPushMatrix();
        actor->paint();
        glPopMatrix();
    }

}






Теперь создадим класс реального объекта. Я обещал сферу? Получайте:
#ifndef SPHEREGL_H
#define SPHEREGL_H
#include "objectgl.h"
#include "math.h"
class SphereGL : public AliveObjectGL
{
    Q_OBJECT
public:
    explicit SphereGL(QObject *parent = 0);


    float radius; /радиус сферы
    GLfloat rotz;   // её вращение вокгруг оси z ( меняется таймерм)
    int init();
    void paint();
    QColor*color;
public slots:
    void live();

};

#endif // SPHEREGL_H



А вот то, как она реализована.
#include "spheregl.h"
#include <QtCore>
SphereGL::SphereGL(QObject *parent) :
    AliveObjectGL(parent)
{
    init();
}

int SphereGL::init()
{
    rotz=0;radius = 1;
    color = new QColor(255,255,255,255);
}

void SphereGL::paint()
{

    glRotatef(20,1,0,0);
    glRotatef(rotz,0,0,1);
    float x,y,z;
    float X=-M_PI,Y=0;
    float Z=0;
    float accuracy = 0.1;
      glColor4f(color->redF(),color->greenF(),color->blueF(),color->alphaF());
    glBegin(GL_TRIANGLE_STRIP);
              while(X<M_PI)
              {
               while(Y<2*M_PI)
               {
               x=radius*cos(X)*cos(Y);
               y=radius*cos(X)*sin(Y);
               z=radius*sin(X);
              // qDebug()<<x<<y<<z;

               glVertex3f(x,y,z);

               x=radius*cos(X)*cos(Y);
               y=radius*cos(X accuracy)*sin(Y);
               z=radius*sin(X);
             //  qDebug()<<x<<y<<z;

               glVertex3f(x,y,z);

               x=radius*cos(X accuracy)*cos(Y);
               y=radius*cos(X)*sin(Y);
               z=radius*sin(X accuracy);
              // qDebug()<<x<<y<<z;

               glVertex3f(x,y,z);
               Y =accuracy;
                 }
                 Y=0;
                 X =accuracy;
               }
              glEnd();

}

void SphereGL::live()
{

    rotz =1.0f;
}


Это простая реализация параметрического уравнения сферы. Перед каждой точкой я добавил цвет по 3 координатам - это делает сферу разноцветной.

Теперь, при инициализации сцены добавим в наш вектор новую сферу. Как видите, она крутится, и мы можем мышкой перемещать её!



Это конечно интересно, но я хочу планету, а у планеты есть спутники, они движутся по орбитам. Давайте посмотрим на небо, как там обстоят дела: есть чёрная дыра, вокруг которой все движется, включая наше солнышко, вокруг солнышка движется планета, вокруг планеты луна и вокруг луны мы можем запустить спутник, и если кто-то выкинет мусор из окошка спутника, у этого спутника то-же появится спутник. Исходя из всего этого я постулирую: у каждого объекта может быть один или несколько спутников, которые движутся по эллиптической орбите вокруг него, и каждый объект может являться спутником для другого объекта, при этом объект не может стать своим собственным спутником. Самостоятельный объект я назову планетой,  а для организации спутников, я создам объект "Орбита", который не имеет права существовать сам по себе, он должен иметь планету, которая по ней движется и вокруг которого она движется. Любую планету можно прилепить к любой орбите, любую орбиту можно прилепить к любой планете, кроме случая, когда планета-центр и планета -спутник совпадают. Орбита должна характеризоваться параметрами эллипса, ещё, кроме вращения планеты по орбите я сделал вращение орбиты относительно планеты, этот случай не имеет места в реальности(на сколько я знаю), но это красиво и интересно.Для всего этого составим диаграмму классов:

Вот такое применение ООП( обожаю эту технологию). Внизу приведён код. Обратите внимание, что из главного окна вызывается прорисовка одной единственной планеты, а она в сою очередь вызывает прорисовки всех орбит которые на ней, а орбиты вызывают прорисовки планет, которые по этим орбитам крутятся. Красота да и только! После реализации всего этого у нас вот такой результат:




Код для запуска и просмотра реализации кольца, и движения по его траектории окружности можете посмотреть тут:
https://dl.dropbox.com/u/95314428/ForBlog/ControledScene.zip
Спасибо за внимание :-) В следующий раз хочу вам рассказать про замечательную функцию- gluLookAt. С её помощью мы сможем перемещаться по миру не смотря всё-время в центр координат, как это происходит в вышеприведённом видео.

Читать дальше......

суббота, 4 августа 2012 г.

Помещение актёров на сцену OpenGl Qt

В прошлом посте я рассказал про создание OpenGl окна. Сегодня я расскажу , как туда поместить объект. Сначала создадим окно , как описано тут http://alexkutsan.blogspot.com/2012/04/opengl-qt.html .

Всё не так сложно.
Изменять я буду  функцию paintGL(). В ней происходит отрисовка.Вызывается она всякий раз, когда окно перерисовывается. Мы нарисуем  8 угольник. Определим его вершины:


Да, мы можем заставить компьютер считать все эти косинусы и синусы, но зачем? Компьютер будет раскладывать в ряд каждое выражение. Лучше попросим любого 7-миклассника посчитать нам эти значения. Он нам с первого взгляда всё выведет (стыдно, да?)


Ближе к коду.


void SceneGL::paintGL(){
  glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
  glBegin(GL_POLYGON);
      glVertex3f( 0.5f, 1.0f,0.0f);
      glVertex3f(1.0f,0.5f,  0.0f);
      glVertex3f( 1.0f,-0.5f,0.0f);

      glVertex3f(0.5f,-1.0f,0.0f);
      glVertex3f(-0.5f,-1.0f,0.0f);
      glVertex3f(-1.0f,-0.5f,0.0f);

      glVertex3f(-1.0f,0.5f,0.0f);
      glVertex3f(-0.5f,1.0f,0.0f);
   glEnd();
}



Немного описания.

Мы работаем уже в матричной модели. Изменяем ведь саму модель - glMatrixMode(GL_MODELVIEW);

Мы очищаем буфер цветов -  glClear(GL_COLOR_BUFFER_BIT); , ведь у нас появляются здесь цвета( пока-что только белый)

Загружаем единичную матрицу -  glLoadIdentity(); . Никаких трансформаций над матрицей мы не делаем, поэтому этот шаг не обязателен,но делать его принято. Попробуйте вставить
...
glRotated(30,0,0,1);
glBegin(GL_POLYGON);
...
Вы  увидите, что матрица повернётся относительно единичной. Теперь уберите  glLoadIdentity();. Видите? Матрица поворачивается всякий раз при перерисовке окна относительно прошлого её состояния. Это потому, что функция glRotated(30,0,0,1); не поворачивает объект, это мы с вами думаем, что она поворачивает объект. glRotated умножает текущую матрицу на матрицу поворота, параметры которой мы задаём.

Всё серо и грустно.
Давайте добавим радости - добавим цвета.
Перед каждой точкой будем задавать цвет функцией glColor3f. Её параметры - RGB (0.0f-1.0f);
  glBegin(GL_POLYGON);
       glColor3f(1,0,0);
      glVertex3f( 0.5f, 1.0f,-1.0f);
       glColor3f(0,1,0);
      glVertex3f(1.0f,0.5f,  -1.0f);
       glColor3f(0,0,1);
      glVertex3f( 1.0f,-0.5f,-1.0f);

       glColor3f(1,0,1);
      glVertex3f(0.5f,-1.0f,-1.0f);
       glColor3f(1,1,0);
      glVertex3f(-0.5f,-1.0f,-1.0f);
       glColor3f(1,1,1);
      glVertex3f(-1.0f,-0.5f,-1.0f);

       glColor3f(0.5,0.5,0.5);
      glVertex3f(-1.0f,0.5f,-1.0f);
       glColor3f(0,0.5,1);
      glVertex3f(-0.5f,1.0f,-1.0f);
      glEnd();



 
И результат:



Хочу, чтоб всё вертелось!
Когда мы смотрим на объект, мы смотрим на него сверху.
Для простоты , сейчас мы повертим его относительно оси Z.
Если вы посмотрите код выше, то увидите, что наш 6-угольник находился в плоскости Z на уровне  -  -1. Давайте это изменим, и поставим его на уровень 0. И воспользуемся функцией смещения glTranslated(0,0,-3); Тем самым, мы отведём от глаз шестиугольник на 3 шага.

Для того, чтоб повернуть этот шестиугольник , используется функция   glRotatef. Она принимает 4 параметра:
angel - угол на который нужно повернуть объект.
x - поворачивать относительно Ох?
y - поворачивать относительно Оу?
z - поворачивать относительно Oz?

Давайте создадим private переменную  int rot; И создадим public slot void update();
Внутри update напишем.
void SceneGL::update()
{
    rot++;
    updateGL();
}

Теперь, изменим код initializeGL
void SceneGL::initializeGL(){
    glClearColor(0.0f,0.0f,0.0f,1.0f);
    glEnable(GL_DEPTH_TEST);
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(10);
    
}

Как видите, мы запускаем таймер, и через каждые 10 миллисекунд будет выполняться update(), тем самым увеличивая переменную rot и перерисовывая сцену. Стоит изменить функцию paintGL
void SceneGL::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslated(0,0,-4);
    glRotatef(rot,0,0,1);
    ...
После загрузки единичной матрицы я поворачиваю сцену на rot градусов, а rot постоянно меняется таймером. Запускайте, любуйтесь.

Теперь, давайте повертим наш шести угольник не только относительно Z, а ещё относительно X и Y. Для этого изменим glRotatef(rot,0,0,1); на glRotatef(rot,1,1,1);. Для большей наглядности нарисуем Оси координат. Создайте private функцию

void SceneGL::drawAxis()
{
    glLineWidth(3.0f);
    // до вызова команды ширина равна 9 пикселю по умолчанию
    // устанавливаем цвет последующих примитивов
    glColor4f(1.00f, 0.00f, 0.00f, 1.0f);
    // ось x кvoid Scene3D::drawAxis()
    {
        glLineWidth(3.0f);
        // до вызова команды ширина равна 9 пикселю по умолчанию

        // устанавливаем цвет последующих примитивов
        glColor4f(1.00f, 0.00f, 0.00f, 1.0f);
        // ось x красного цвета
        glBegin(GL_LINES); // построение линии
        glVertex3f( 9.0f,  0.0f,  0.0f); // первая точка
        glVertex3f(-9.0f,  0.0f,  0.0f); // вторая точка
        glEnd();

        QColor halfGreen(0, 128, 0, 255);
        qglColor(halfGreen);
        glBegin(GL_LINES);
        // ось y зеленого цвета
        glVertex3f( 0.0f,  9.0f,  0.0f);
        glVertex3f( 0.0f, -9.0f,  0.0f);

        glColor4f(0.00f, 0.00f, 1.00f, 9.0f);
        // ось z синего цвета
        glVertex3f( 0.0f,  0.0f,  9.0f);
        glVertex3f( 0.0f,  0.0f, -9.0f);
        glEnd();
    }

}

Теперь изменим функцию paintGL вот так:
    ...
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslated(0,0,-4);
    glRotatef(30,1,1,0);
    drawAxis();
    glRotatef(rot,1,1,1);
    ...
и функцию initializeGL
void SceneGL::initializeGL(){
    glClearColor(0.0f,0.0f,0.0f,1.0f);
    glEnable(GL_DEPTH_TEST);
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(10);

}
Обратите внимание на glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); и glEnable(GL_DEPTH_TEST);. Я включил тест глубины, если его не включить, то openGl не будет учитывать дальность объектов. Виден будет объект, которые нарисуется первым. С включённым тестом глубины будет виден тот объект, который ближе к наблюдателю(к нам). Ещё я поместил сюда второй glRotatef, который поворачивает сцену на 30 градусов по X и Y - это даст нам удобный угол просмотра. После этого я рисую оси и поворачиваю сцену на rot градусов по всем осям, и рисую 6-угольник. Благодоря этой последовательности прорисовки, оси у меня остаются всегда в 30 градусах, а объект крутится.
Итог получается вот такой:
 
 В следующий раз я расскажу как сделать управляемую сцену, чтоб мы в ней могли перемещаться динамически. Ещё я хочу рассказать, как с помощью примитивов нарисовать сферу. На сегодня всё.Спасибо всем, кто это прочёл. Исходники можно получить здесь : Arctors.zip
Читать дальше......

среда, 1 августа 2012 г.

Gnome3 Linux Mint

Что-то мне надоели постоянные проблемы с системой. Охота сидеть и писать код. Решил поставить Linux Mint,но меня ждали плохие новости. Как известно, в Mint используется оконное окружение - mate. Мне оно не нравится. Я хочу gnome3. При установке пакетов gdm, gnome и gnome-shell, исталятор валится с указанием на существование файла /usr/share/pixmaps/nobody.png. Его удаление не помогает. Информации об этом в интернете нету.Оказалось, что Mintовский mdm(mint display manager)- это будто-бы gdm, но mdm.Для корректной работы gdm, mdm надо удалить. Удаляете и ставите спокойно gdm и gnome. Лично мне - нравится mdm. Я удалил mdm, поставил gdm,gnome,gnome-shell, удалил gdm, поставил mdm. Всё... Работает как надо :-)
Читать дальше......

пятница, 1 июня 2012 г.

Больше гибкости!!! И прыгающие шарики.

Прошла пара дней с тех пор,как я выпустил свою первую библиотеку. И вот, я выпускаю первое к ней обновление, и програмку “Скачущие шарики”, иллюстрирующую её применение . Если интересно, прошу под кат.
Если вы посмотрите в исходный код, то теперь там найдёте новый класс ExeptionMiscarriage.
public class ExeptionMiscarriage extends Throwable{
    private String info ="Object didn't ready";
    public ExeptionMiscarriage(String info){
        this.info =info;
    }

    public ExeptionMiscarriage() {
       
    }
    /**
     * @return the info
     */
    public String getInfo() {
        return info;
    }

    /**
     * @param info the info to set
     */
    public void setInfo(String info) {
        this.info = info;
    }
    public void addInfo(String info) {
        this.info =  this.info   info;
    }   
}
Не сложно догадаться, что это Исключение “выкидыша”. Что это значит? Теперь, если пользователь не заполнил все необходимые поля в панели создания объекта и нажал на кнопочку “ОК”, вы можете сформировать это исключение, и наполнить его информаицией. И пользователю будет выдано модальное окошко ошибки, в котором будет ваша информация.
Диалог не прекратится, а продолжит действовать, пока пользователь не введёт всю необходимую информацию для формирования вашего объекта, или же не нажмёт “Отмена”. Второе улучшение библиотеки касается расширяемости.  По ходу написания программы “Скачущие шарики”, мне понадобилось использовать мою библиотеку не совсем в стандартной ситуации. Я хотел сделать диалог выбора цвета. Панель выбора цвета существует - JColorChooser. И вот ведь незадача: я не могу сообщить её в конструктор своего диалога, так как она не является наследником FactoryPanel. Вот он, камень преткновения и проектировочный тупик. Наследовать от 2-х классов в Java нельзя. Даже, если я наследую от JColorChooser и реализую интерфейс ObjectFactory. То это не сможет явно привести к FactoryPanel во -первых :потому, что так делать нельзя. Если у нас есть:
 
class A{}
interface I{}
class B extends A,implements I{}
class С extends A,implements I{}
Мы не можем Объект класса C явно привести к B. Но, даже, если бы это было возможно-в конкретной ситуации этого сделать было бы нельзя, так как у JColorChooser нет в потомках JPanel. Но зато, у него есть JComponent. JComponent является наследником всех Swing UI элементов. Поэтому, я пересмотрел свою библиотеку и в свой диалог я буду принимать JComponent. Конечно, в целях обратной совместимости, я оставил FactoryPanel, но переименовал её в FactoryComponent. Это не вызовет проблем при разработке панели в визуальном редакторе. Вы по-прежнему, после разработки переопределяете наследника с JPanel на FactoryComponent. Но из за мухи в супе, описанной выше, такой фокус не пройдёт с JColorChooser. Поэтому, я решил, сделать ещё один конструктор в ComplexDialog. ComplexDialog(JFrame parent, Object mainpanel, String title, boolean modal); Как видите - Тип главной панели-настройщика – Object. То есть , я могу сообщить туда всё, что душе угодно. Но в самом конструкторе я проверяю:
 
        settingscomponent = (JComponent)mainpanel;
        objectFactory = (ObjectFactory)mainpanel;
Если вы скормили моему диалогу какое-то непотребство - вывалится исключение о несоответствии типов.  Вернёмся к задаче: Нам надо скормить JColorChooser в ComplexDialog. Если скормим без приправы, ComplexDialog- выплюнет исключение, как я и описывал выше. Поэтому, мы наследуем от JColorChooser и при этом реализуем интерфейс ObjectFactory.
 
    public class MyColorChoser extends JColorChooser implements complexdialog.ObjectFactory{

        @Override
        public Object getResult() throws ExeptionMiscarriage {
           return getColor();
   }
Видите, как классно! Теперь мы можем создать объект типа MyСolorСhoser и успешно скормить его моему диалогу в качестве Object. Так как JColorChooser потомок JComponent, а MyСolorСhoser ещё и реализовывает ObjectFactory- значит диалог скушает этот объект с большим удовольствием. Здесь, в папке examples/example2 ,лежит программа “Скачущие мячики” ,в которой реализованы 2 диалоговых окна моей библиотеки. Первое окно (добавления мячика) показывает, как работают исключения-выкидыши. Второе- показывает, как скормить чужие компоненты моей библиотеке. Дабы вам не было сильно скучно - в самой програмке скачут шарики, как в реальной жизни: с ускорением, вязкостью воздуха, коэффициентом упругости и прочими вкусностями. Для добавления шарика-нажмите кнопочку “Add”,она вызовет мой диалог, и вы не сможете нажать OK, пока не заполните все поля и не выберете цвет. Если вы читаете эту статью, наверно вы - программист. А ,значит , вас не испугает перспектива залезть в код и посмотреть, как добавляются шарики. Я специально, сразу кинул в цикле 20 шариков с разной начальной скоростью и прочим. Поэкспериментируйте с разным количеством шариков, динамической начальной скоростью(очень интересны получаются функциональные зависимости), меняющимися цветами, динамическим коэффициентом упругости.Попробуйте изменить вязкость среды, время обновления рассчётов, временной коэффициент- я специально создал его, чтобы при очень больших масштабах( я сбрасывал мячик с 10 километров на начальной скорости 100 метров в секунду) всё происходило немного быстрей, чем в реальной жизни. Ибо скучно. Только осторожно! Меня эта штука затянула на целый вечер, поэтому, я и пишу статью сегодня, а не вчера :-) Всем спасибо. Постараюсь радовать вас новыми интересными идеями, библиотеками, програмками и прочим. И ,конечно, до бесконечности совершенствовать библиотеку ComplexDialog.


За редакцию хочется выразить благодарность,Алисе Брыль. 
Читать дальше......

понедельник, 28 мая 2012 г.

Моя первая библиотека, или Сложные Диалоги в Java (SWING)

Добрый вечер. Сегодня я хочу предоставить на ваш суд свою первую библиотеку. Прошу не судить строго, но буду рад конструктивной критике и предоложениям по развитию. Если же кто-то захочет помочь в модификации, буду только счастлив. Теперь про библиотеку. Она позволяет вам создавать сложные диалоговые окна. Что это? Зачем? Стандартная библиотека позволяет создавать простые диалоговые окна(выбор Да\Нет, ввод строки, выдор варианта из списка). А что, если вам требуется диалоговое окно, результатом котого будет целый объект. Например, Диалоговое окно выбора места на карте, а результатом будет объект адреса, на котоый кликнул пользователь. Вот, с помошью моей библиотеки, вы сможете это сделать быстро и просто.
Скачать сие творение можно от сюда ComplexDialog . Там же можно присоедениться к разработке.

Теперь про то, как ей пользоваться. Подключив библиотеку(Есть много способов это сделать. Зависит от среды разработки и ваших вкусовых предпочтений) ,вы получаете 2 класса и 1 интерфейс.

Интрфейс ObjectFactory имеет один единственный метод, который возврашает значение указаного вами типа.

Первый класс - ComplexDialog. Это, непосредственно ,класс диалога. Он насделуется от JDialog .Для работы с ним, вы должны указать тип который диалог обязан вернуть. После вызова его конструктора и вызова метода show\setVisible(true) ,можно смело вызывать метод getReturnedStatus(), который вернёт сформированный пользователем объект. Программа приостановится, пока пользователь не нажмёт на OK или Canel. Если Пользователь жмёт Canel, getReturnedStatus возвращает нулевой указатель.

Для того, чтоб детальнее понять ComplexDialog, стоит изучить FactoryPanel. Это абстрактный класс. Он абсолютно пустой. Наследуется от JPanel и реализует интерфейс ObjectFactory. От него вам стоит наслелдовать панель, в которой будет вестись настройка вашего объекта. Обратите внимание, класс нетипизирован и дать ему тип вы должны такой же, как и в ComplexDialog. Потому что ComplexDialog использует метод getResult() для возвращения значения. На самом деле, вы можете создать панельку и в визуальном редакторе, но потом подредактировать код, изменив extends высшей панельки на FactoryPanel, и реализовать метод getResult(),как того требует интерфейс ObjectFactory.

Для создания ComplexDialog нужно:
JFrame parent - окно -родитель(осталось от JDialog);
FactoryPanel mainpanel - панелька, в которой происходит настройка необходимого вам типа; 
String title - надпись на окошке (осталось от JDialog) ; 
boolean modal - модальность окна (осталось от JDialog); 


Для более наглядного объяснения, хочу привести пример практического использования этой библиотеки. Одним из самых простых и достаточно часто необхидимых примеров ,наверное, будет DatePicker, то есть Диалог, в котором пользователь выберет какую- либо дату. 


Наш "ХелоВорлд" будет просить пользователя ввести дату его рождения и подсчитает количество дней прожитых пользователем. Для начала, Создадим формочку, которая наследуется от FactoryPanel. Для подтверждения своих слов, я создал её в визуальном редакторе, позже реализовал внутри необходимый функционал и реазиловал метод getResult() ,который возвращает объект типа java.util.Date, сформированный пользователем. FactoryPanel:
package alexkutsan_blogspot;

import complexdialog.FactoryPanel;
import java.util.Calendar;
import java.util.Date;
import javax.swing.DefaultComboBoxModel;
/**
 *
 * @author alex
 */

public class DatePickerPanel extends FactoryPanel<Date>{

    /**
     * Creates new form DatePickerPanel
     */
    public DatePickerPanel() {
        super();
        initComponents();
        myinitUI();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        YearBox = new javax.swing.JComboBox();
        MonthBox = new javax.swing.JComboBox();
        DayBox = new javax.swing.JComboBox();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();

        YearBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        YearBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                YearBoxActionPerformed(evt);
            }
        });

        MonthBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        MonthBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                MonthBoxActionPerformed(evt);
            }
        });

        DayBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));

        jLabel1.setText("%u0413%u043E%u0434");

        jLabel2.setText("%u041C%u0435%u0441%u044F%u0446");

        jLabel3.setText("%u0414%u0435%u043D%u044C");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(YearBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(6, 6, 6)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(MonthBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(DayBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(27, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(25, 25, 25)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(YearBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(MonthBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(DayBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2)
                    .addComponent(jLabel3))
                .addContainerGap(35, Short.MAX_VALUE))
        );
    }// </editor-fold>

    private void YearBoxActionPerformed(java.awt.event.ActionEvent evt) {                                        
       int m = MonthBox.getSelectedIndex()   1;
                    int y = YearBox.getSelectedIndex()   (Calendar.getInstance().get(Calendar.YEAR) - CountYears);
                    int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
                    Integer[] Days = new Integer[n];
                    for (int i = 1; i < n   1; i  ) {
                        Days[i - 1] = i;
                    }
                    DayBox.setModel(new DefaultComboBoxModel(Days));
    }                                       

    private void MonthBoxActionPerformed(java.awt.event.ActionEvent evt) {                                         
                    int m = MonthBox.getSelectedIndex()   1;
                    int y = YearBox.getSelectedIndex()   (Calendar.getInstance().get(Calendar.YEAR) - CountYears);
                    int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
                    Integer[] Days = new Integer[n];
                    for (int i = 1; i < n   1; i  ) {
                        Days[i - 1] = i;
                    }
                    DayBox.setModel(new DefaultComboBoxModel(Days));
    }                                        

    // Variables declaration - do not modify
    private javax.swing.JComboBox DayBox;
    private javax.swing.JComboBox MonthBox;
    private javax.swing.JComboBox YearBox;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    // End of variables declaration

    private void myinitUI() {
     Date cur = new Date(System.currentTimeMillis());
            int curentYear = Calendar.getInstance().get(Calendar.YEAR);
            Integer[] Years = new Integer[CountYears];
            for (int i = curentYear - CountYears; i < curentYear; i  ) {
                Years[i - (curentYear - CountYears)] = i;
            }
            Integer[] Month = new Integer[12];
            for (int i = 1; i <= 12; i  ) {
                Month[i - 1] = i;
            }
            Integer[] Data31 = new Integer[31];
            Integer[] Data30 = new Integer[30];
            Integer[] Data29 = new Integer[29];
            Integer[] Data28 = new Integer[28];
            for (int i = 1; i < 32; i  ) {
                if (i < 31) {
                    Data30[i - 1] = i;
                }
                if (i < 30) {
                    Data29[i - 1] = i;
                }
                if (i < 29) {
                    Data28[i - 1] = i;
                }
                Data31[i - 1] = i;
            }
            int m = Month[0];
            int y =Years[0];
            int n = m != 2 ? m > 7 ? 30   m % 2 == 1 ? 0 : 1 : 30   m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
            Integer[] Days = new Integer[n];
            for (int i = 1; i < n   1; i  ) {
                Days[i - 1] = i;
            }
            YearBox.setModel(new DefaultComboBoxModel(Years));
            MonthBox.setModel(new DefaultComboBoxModel(Month));
            DayBox.setModel(new DefaultComboBoxModel(Days));
    }
    int CountYears = 100;

    @Override
    public Date getResult() {
            Integer y =(Integer) YearBox.getSelectedItem();
            Integer m =(Integer) MonthBox.getSelectedItem();
            Integer d =(Integer) DayBox.getSelectedItem();
            Date date = new Date(y-1900, m-1, d);
            return date;
    }
}
Вот такая панелька
У неё незамысловатый функционал .Внимания заслуживает лишь моя функция рассчёта количества дней в месяце.
 int n = m != 2 ? m > 7 ? 30 m % 2 == 1 ? 0 : 1 : 30 m % 2 : y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28; где 
m - месяц(1..12)
y - год.

 Как видите, в функции getResult() формируется и возвращается Дата, введённая пользователем. Теперь главный класс:
 


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package alexkutsan_blogspot;
import complexdialog.ComplexDialog;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
/**
 *
 * @author alex
 */
public class Alexkutsan_blogspot {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws ParseException, IOException, URISyntaxException {


        // Date today = new Date(System.currentTimeMillis());
        Date today = new Date(System.currentTimeMillis());
        DatePickerPanel p = new DatePickerPanel();
        ComplexDialog<Date> dp = new ComplexDialog<Date>(null,p,"%u0412%u0432%u0435%u0434%u0438%u0442%u0435 %u0434%u0430%u0442%u0443 %u0432%u0430%u0448%u0435%u0433%u043E %u0440%u043E%u0436%u0434%u0435%u043D%u0438%u044F", true);
        dp.setVisible(true);
        Date born = dp.getReturnedStatus();
        if (born != null) {
            long difference = today.getTime() - born.getTime();
            long daycount = TimeUnit.MILLISECONDS.toDays(difference);
            JOptionPane.showMessageDialog(dp, String.format("%u0417%u0435%u043C%u043B%u044F %u0432%u0430%u0441 %u043D%u043E%u0441%u0438%u0442 %u0432%u0441%u0435%u0433%u043E-%u0442%u043E %s %u0434%u043D%u0435%u0439 ", daycount), "%u0412%u044B %u0442%u0430%u043A %u043C%u043E%u043B%u043E%u0434%u044B", JOptionPane.INFORMATION_MESSAGE);
        }
        //URLLabel myEgo = new URLLabel(new URI("http://alexkutsan.blogspot.com/"),"%u041F%u0440%u043E%u0433%u0440%u0430%u043C%u043C%u0443 %u0440%u0430%u0437%u0440%u0430%u0431%u043E%u0442%u0430%u043B %u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");
        //JLabel mersi = new JLabel("%u0411%u043B%u0430%u0433%u043E%u0434%u0430%u0440%u043D%u043E%u0441%u0442%u0438:\n \t %u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0438%u043B%u0438%u043C%u043D%u0438%u043A \t \n %u0410%u043B%u0438%u0441%u0430 %u0411%u0440%u044B%u043B%u044C");
        JOptionPane.showMessageDialog(dp,new About(), "%u0410%u0432%u0442%u043E%u0440: ", JOptionPane.INFORMATION_MESSAGE);
        System.exit(0);
    }
}

Я тут создаю панельку, котороую описал выше, вычисляю сегодняшнюю дату, создаю диалог и жду, пока пользователь не нажмёт что-то.
 

        ComplexDialog<Date> dp = new ComplexDialog<Date>(null,p,"%u0412%u0432%u0435%u0434%u0438%u0442%u0435 %u0434%u0430%u0442%u0443 %u0432%u0430%u0448%u0435%u0433%u043E %u0440%u043E%u0436%u0434%u0435%u043D%u0438%u044F", true);
        dp.setVisible(true);
        Date born = dp.getReturnedStatus();
Выглядит диалог Так:
По нажатию, у меня в born лежит либо дата, которую выбрал пользователь, либо null. Если Дата != null, то выскочит сообщение c подсчитаными днями. Работать это будет только ,если вы ещё создадите эти классы. Они не нуждаются в дополнительном описании, разве что URLLable. Но о ней я расскажу позже. Скачать пример можно по этой ссылке ComplexDialog , в каталоге Examples.
 

//The contents of this file are subject to the Mozilla Public License Version 1.1
//(the "License"); you may not use this file except in compliance with the 
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003. 
//
//All Rights Reserved.
package alexkutsan_blogspot;


import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import javax.swing.JLabel;

public class URLLabel extends JLabel {

  boolean entered = false;

  boolean mousehover;
  private  URI site ;
  public URLLabel(URI uri) {
    this(uri, uri.toString());
  }

  public URLLabel(URI uri, String str) {
    super(str);
    this.site = uri;
    setForeground(Color.blue);
    mousehover = false;
    setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() > 0) {
                    if (Desktop.isDesktopSupported()) {
                        Desktop desktop = Desktop.getDesktop();
                        try {
                          
                            desktop.browse(site);
                            System.exit(0);
                        } catch (IOException ex) {
                            
                        }
                    } 

                }
            }
        });
  }

  public void mouseEntered(MouseEvent e) {
    entered = true;
    if (mousehover) {
      repaint();
    }
  }

  public void mouseExited(MouseEvent e) {
    setCursor(Cursor.getDefaultCursor());
    entered = false;

    if (mousehover) {
      repaint();
    }
  }

  public void mousePressed(MouseEvent e) {
  }

  public void mouseReleased(MouseEvent e) {
  }

    @Override
  public void paint(Graphics g) {
    super.paint(g);

    if (entered || !mousehover) {
      Rectangle r = g.getClipBounds();

      g.drawLine(0, r.height - this.getFontMetrics(this.getFont()).getDescent(), this
          .getFontMetrics(this.getFont()).stringWidth(this.getText()), r.height
          - this.getFontMetrics(this.getFont()).getDescent());
    }
  }
}


}


 

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package alexkutsan_blogspot;

import java.net.URI;
import java.net.URISyntaxException;

/**
 *
 * @author alex
 */
public class About extends javax.swing.JPanel {

    /**
     * Creates new form About
     */
    public About() {
        initComponents();
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        try{
            jLabel2 = new URLLabel(new URI("http://alexkutsan.blogspot.com/"), "%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");
            jLabel3 = new javax.swing.JLabel();
            jLabel4 = new javax.swing.JLabel();
            jLabel5 = new javax.swing.JLabel();

            jLabel1.setText("%u041F%u0440%u043E%u0433%u0440%u0430%u043C%u043C%u0443 %u0440%u0430%u0437%u0440%u0430%u0431%u043E%u0442%u0430%u043B:");

        }
        catch(URISyntaxException e){

        }
        jLabel2.setText("%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0443%u0446%u0430%u043D");

        jLabel3.setText("%u0411%u043B%u0430%u0433%u043E%u0434%u0430%u0440%u043D%u043E%u0441%u0442%u0438:");

        jLabel4.setText("%u0410%u043B%u0435%u043A%u0441%u0430%u043D%u0434%u0440 %u041A%u0438%u043B%u0438%u043C%u043D%u0438%u043A");

        jLabel5.setText("%u0410%u043B%u0438%u0441%u0430 %u0411%u0440%u044B%u043B%u044C");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(jLabel3))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jLabel2))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(41, 41, 41)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel5)
                            .addComponent(jLabel4))))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel4)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel5))
        );
    }// </editor-fold>                        
    // Variables declaration - do not modify                     
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    // End of variables declaration                   
}


В написании статьи хочу выразить благодарность этим людям: 
    Алиса Брыль; 
    Александр Килимник;
Читать дальше......
 
То, что скрыто под катом
Читать дальше......

воскресенье, 15 апреля 2012 г.

Oracle + Java

Всем привет, это продолжение изучения оракла, и его совокупления  с другими технологиями. Сейчас его будем сабачить к Java.


Поехали.

Нащупываем на затылке кнопочку включения мозгов, и рвёмся в бой.
Пока масажное приспособление нежно разминает мою спинку, я буду описывать ваши(наши) действия. Надёюсь вы знаете как выглядят java пограммы.
  public class HelloDB {

    public static void main(String[] args) {
        System.out.println("Hello DB");
    }
}
Запишем это в файлик HelloDB.java , вызовем javac HelloDB.java . А потом java HelloDB . И любуемся плодами своей тяжкой работы. Порадовались? Двигаемся дальше. Подключится java к базе - очень легко. Всего-то стоит
 
Class.forName("oracle.jdbc.driver.OracleDriver");
  System.out.println("Driver Loaded");

DriverManager.getConnection("jdbc:oracle:thin:@//localhost:1521","system","oracle");
    Statement stmt = conn.createStatement();
Ну что сложно,нет? Тогда включите сервер базы данных и запускайте.
АГА, тяжелый молот судьбы ударил по вашим амбициям? А почему? Не подумали? Давайте разбираться. Начиная с первой строчки. Мы загружаем драйвер оракла. А где мы его достанем? Для этого нужно подключить в программу jar file драйвера ojdbc6 . Есть 4 способа:
1) Указание в параметре classpath пути к этому драйверу -classpath /home/alex/NetBeansProjects/DbApp/ojdbc6.jar
2) Указание в глобальной переменной CLASSPATH пути к этому драйверу export CLASSPATH=/home/alex/NetBeansProjects/DbApp/ojdbc6.jar

Оба способа рабочие, но оба заменяют ВСЕ подключаемые библиотеки при компиляции, включая саму программу исходник, поэтому нужно ещё указать и программу исходник в этих переменных , или текущую папку PWD.

3) Написание файла манифеста, но я им ещё не пользовался.
4) Программировать в среде разработки, там подключение библиотек делается 3 кликами.

В моём случае( я использовал 1 способ) команды компиляции и выполнения выглядели так:
javac -classpath  /home/alex/NetBeansProjects/DbApp/ojdbc6.jar:$PWD  
DBApp.java 


java -classpath  /home/alex/NetBeansProjects/DbApp/ojdbc6.jar:$PWD  
DBApp

Давайте чуть дополним программу, и запустим. Дополним этими строками. Это получение результатов выполнения SQL запросов в базу,и  вывод на экран.
 

 ResultSet rset = stmt.executeQuery("select * from NLS_DATABASE_PARAMETERS;");
    
     while ( rset.next() )
     System.out.print(rset.getString(1).concat(" : ") +rset.getString(2)+ "\n");
   stmt.close();
Запускайте.Что? Опять ваша система даёт вам по щам? Но зато ошибка уже знакома.
ORA-12705: Cannot access NLS data files or invalid environment specified

Неприятно,неприятно. Но вполне разрешаемо. Многие способы я рассматривал в этом посте   . Способ с netbeans не проканает,даже если вы в нём. java  к нему не имеет никакого отношения.  Но сейчас я открою вам новый секретный способ, который я считаю единственным истинно правильным.
Дополним программу:
 
    Locale l  = Locale.getDefault();
    Locale.setDefault(Locale.US);
   ........
   Locale.setDefault(l);
Как видите, я сохраняю текушую локаль системы. Устанавливаю английский, а по завершении программы, возвращаю ту, что была.
Ну, подставляем другую щеку, компилируем и запускаем.
Что , опять огребли по самые помидоры? Но проблема теперь в запросе select. Дело в том, что executeQuery поддерживает выполнение 1 sql запроса, а значит, никаких точек с запятой там быть не может. Решение: убираем точку с запятой, крестимся, запускаем. ResultSet rset = stmt.executeQuery("select * from NLS_DATABASE_PARAMETERS"); И в долгожданном выводе получаем настройки национальных локалей вашей БД. А ведь что мы сделали? Мы выполнили запрос в базу и получили ответ. Дальше вы можете составлять свои более сложные таблицы, более сложные программы. И во всё работать с базами данных, Если у кого не получилось из обрывков кода склепать готовую программу, то вот она.
 
import java.sql.*;
import java.util.Locale;
public class DBApp
{
  public static void main(String[] args) throws SQLException,ClassNotFoundException  
  {
/*   Class.forName
("oracle.jdbc.driver.OracleDriver");
  System.out.println("Driver Loaded");
 */ 
    Locale l  = Locale.getDefault();
    Locale.setDefault(Locale.US);
    DriverManager.registerDriver ( new oracle.jdbc.driver.OracleDriver());
    Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//192.168.56.101:1521","system","oracle");
    Statement stmt = conn.createStatement();
    
    ResultSet rset = stmt.executeQuery("select * from NLS_DATABASE_PARAMETERS");
    
     while ( rset.next() )
     System.out.print(rset.getString(1).concat(" : ")  rset.getString(2)  "\n");
   stmt.close();
   Locale.setDefault(l);
  }
}
И запуск
javac -classpath  /home/alex/NetBeansProjects/DbApp/ojdbc6.jar:$PWD  
DBApp.java 


java -classpath  /home/alex/NetBeansProjects/DbApp/ojdbc6.jar:$PWD  
DBApp

Всё готово. Успехов вам в использовании баз данных.






Читать дальше......