Всем доброго времени суток
С Qt знаком всего полторы недели, и то, все это время читал книги. Вот захотелось реализовать виджет выбора рисунка (не хочется делать просто текстовое поле с адресом). За основу взял http://qt.gitorious.org/qt-labs/graphics-dojo/trees/master/dragremote с гита Qt. Но к моему сожалению понял, что прочитанных разделов о графике в книгах не достаточно .
Хочется сделать что-то типа такого :
XpycT,
void ImageWidget::paintEvent(QPaintEvent*)
{
QPainter painter(this);
QPainter painter1(this);
...
Первый вопрос решен, спасибо.
никаких слотов. Обрабатываешь эвент клика мышки, определяешь попал ли пользователь на кнопку, если да, то на какую, в зависимости от этого вызываешь нужную функцию или посылаешь сигнал, который и коннектися к какому-то слоту.
плюс - в маус мув эвенте отлавливать положение мыши на кнопке и рисовать ее другой картинкой (чтобы пользователь знал куда будет давить), и при нажатии - тоже менять картинку.
но про слоты и т.п. - первый абзац.
Как я понимаю, надо ловить координаты мыши в mousePressEvent и сравнивать, попадают ли они в координаты рисунка-кнопки в paintEvent'a, если да - вызывать действие. Ну и аналогично при движении d mouseMoveEvent для изменения состояния кнопки.
Или я не прав, и есть более простой способ?
Да, именно так. Кстати, вызывать действие я советую только если пользователь не только нажал на кнопку мыши над этим рисунком-кнопкой, но и отпустил мышь на нем. Проверь - нормальные кнопки под винду именно так и работают. Т.е. в pressEvent ты ловишь нажал ли пользователь эту кнопку, сохраняешь себе в память данные об этом нажатии (как угодно, хоть булевой переменной и т.п.), а в releaseEvent смотришь, была ли кнопка нажата, затем проверяешь попал ли этот эвент на нужный прямоугольник, и если попал, то только тогда вызываешь действие.
а в маус муве то же самое - если кнопка уже нажата, то картинку не меняем. Если не нажата - меняем на "в фокусе" ,если мышка на картинке, а если не на картике - на "не в фокусе".
я пользую у себя такой класс на данный момент:
//-----------------------------------------------------------------------------
// File: a_cupics_layoutbutton.h
//
// Desc:
//-----------------------------------------------------------------------------
#ifndef A_CUPICS_LAYOUTBUTTON_H
#define A_CUPICS_LAYOUTBUTTON_H
#include <QObject>
#include <QPixmap>
#include <QRect>
//--------------------------------------------------------------------------------------
// Name: cUPicsLayoutButton
// Desc:
//--------------------------------------------------------------------------------------
class cUPicsLayoutButton
: public QObject
{
Q_OBJECT
public:
// Constructor & destructor
//----------------------------------------------------------------------------------
cUPicsLayoutButton(QPixmap pxm, QObject *parent = 0);
// Enums & others
//----------------------------------------------------------------------------------
enum State { Normal, Pressed, Hot };
// Access functions
//----------------------------------------------------------------------------------
QPixmap getView() const;
QRect rect() { return m_btnRect; }
int state() const { return m_btnState; }
// Control functions
//----------------------------------------------------------------------------------
void setRect(QRect rect) { m_btnRect = rect; }
void setState(State s) { m_btnState = s; }
void setPixmap(QPixmap pxm, State s = Normal);
private:
// Page paint data
//----------------------------------------------------------------------------------
QRect m_btnRect;
State m_btnState;
QPixmap m_pxmNormal,
m_pxmPressed,
m_pxmHot;
};
На счет опускания мыши даже не подумал.. надо будет учесть.
А вот на счет определения позиции мыши над кнопкой.
Гдето в исходниках вроде как видел определение самого верхнего рисунка слоя при клике на нем, но не помню толи для пеинтера толи для график виевера, и как на зло не могу вспомнить где . Строка была чтото вроде
qobject_cast<const тип*>(sender)
void ImageWidget::mouseMoveEvent(QMouseEvent *event)
{
mousePos = event->pos();
if((mousePos.x() > (rect().width()-pic_add.width()-25)) &
(mousePos.x() < (rect().width()-25)) &
(mousePos.y() > (rect().height()-22)) &
(mousePos.y() < (rect().height()-6)))
{
setCursor(Qt::PointingHandCursor);
setButton(Open);
}else if((mousePos.x() > (rect().width()-pic_del.width()-5)) &
(mousePos.x() < (rect().width()-5)) &
(mousePos.y() > (rect().height()-22)) &
(mousePos.y() < (rect().height()-6)))
{
setCursor(Qt::PointingHandCursor);
setButton(Clear);
}else{
setCursor(Qt::ArrowCursor);
setButton(None);
}
}
Да, лучше всего работать через QRect в данном случае. У него есть очень удобная функция для проверки принадлежности точки данному прямоугольнику.
Спасибо за советы, думаю разобрался
Благодаря вашей помощи появился мой первый виджет, собственно скрин в первом посте. Реализована загрузка рисунков через QFileDialog, очистка, drag-and-drop локальных и удаленных(скачиваются в временную директорию) рисунков и запоминание пути к нему.
Делал для формы заливки на сайт - может кому и пригодится. Правда хотелось бы сделать правное "выезжание" панельки, но к сожалению в Qt еще не силен
Собственно сам виджет :
imagewidget.zip ( 5.79 килобайт )
: 132
ЗЫ Хотел сделать плагин для дизайнера, откомпилил нормально... но при клике в панели виджетов вываливается крит. ошибка и приглашение в отладчик
Молодец что доделал
Виджет твой скачал, посмотрел. Только он не комплится в таком виде, пришлось комментарии кое где убрать
Если хочешь, могу очень нудно и досконально откомментировать, т.к. есть очень много мест, над которыми нужно работать
1. оформление кода. Представь, что у тебя будет не один такой файлик, а десятки, многие из которых в кучу раз длиннее. Как ты в нем ориентироваться будешь?
2. комментирование кода. Название функций, что делают, и т.д. и т.п.
Т.е. код должен быть хорошо оформлен, более читабелен. Это приходит с опытом и по началу кажется излишним, но - лучше начни уделять этому внимание сейчас, потом будет куда легче.
3. Не стоит формировать виджет в main функции. Пусть это будет отдельный класс. В мейне - просто создаем что надо и делаем основную настройку приложения.
4. Сам класс. Если ты делаешь виджет, который в принципе может использоваться не в одном экземпляре и даже других проектах - он должен быть "вещью в себе", но не более чем. Грубо говоря, цель его - показывать картинку с рамочкой. А значит, он должен уметь:
Комментариев не хватает,согласен, просто надо привыкнуть всегда их ставить
На счет сигналов/слотов и ... если я тебя правильно понял, то допустим для открытия рисунка через диалог лучше в mousePressEvent просто вызвать сигнал, допустим emit imageOpen(const QString &imageName)?
Хотел еще спросить на счет drag-and-drop'a .Почему, когда я использую в dragEnterEvent
if(event->mimeData()->hasImage())
if (event->mimeData()->hasFormat("image/jpeg") && ...)
по сигналам - не совсем. Откуда у тебя взялось имя файла? При нажатии на кнопку ты получаешь желание его добавить изображение, т.е. можно выслать emit( imageDemand() ). А вот когда ты на виджет перетащил файл мышью из системы, то вот тогда - добавляешь на виджет, и - посылаешь emit( imageAdded(QString) ). Или можно слать указатель на картинку и т.п.
А про дрэг н дроп - в dragEnterEvent сделай вывод qDebug() << event->mimeData()->formats() Там увидишь какие форматы и что шлет тебе систем. Я на память не помню. Так когда из системы перетаскиваешь, будет не картика, а путь к ней, если не ошибаюсь.
Что-то у меня со слотами не получается в виджете на клик по рисунку "открыть" поставил emit imageBrowse();. в форме приложения прикрутил сигнал, но после выбора рисунок не открывается и путь к нему пустой Хотя если диалог открытия в самом виджете - все работает. Делал так :
testApp.h
#ifndef TESTAPP_H
#define TESTAPP_H
#include <QDialog>
#include "ImageWidget.h"
class TestApp : public QDialog
{
Q_OBJECT
public:
TestApp(QWidget *parent=0);
private slots:
void openImage();
private:
ImageWidget screen1;
ImageWidget screen2;
};
#endif // TESTAPP_H
#include <QtGui>
#include "testapp.h"
#include "ImageWidget.h"
TestApp::TestApp(QWidget *parent)
:QDialog(parent)
{
ImageWidget *screen1=new ImageWidget;
ImageWidget *screen2=new ImageWidget;
screen2->loadImage("D:\\21.jpg"); // <<< Если на прямую с конструктора - все работает прекрасно.
QHBoxLayout *layout =new QHBoxLayout;
layout->addWidget(screen1);
layout->addWidget(screen2);
setLayout(layout);
connect(screen1,SIGNAL(imageBrowse()),this,SLOT(openImage()));
}
void TestApp::openImage()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("Select Image"),
QApplication::applicationDirPath(),
tr("Image Files (*.jpg *.jpeg *.png *.bmp *.gif)"));
if(fileName.isEmpty())
return;
else
screen1.loadImage(fileName);
}
при чем тут слоты?
ты посмотри что написал то?
...
private:
ImageWidget screen1;
ImageWidget screen2;
...
...
ImageWidget *screen1=new ImageWidget;
ImageWidget *screen2=new ImageWidget;
....
Слот у тебя приватный, а присоединяешь его к сигналу, поступаемому от чужого класса, никак не связанного с данным. Сделай слот public.
Т.е. посмотри на мессадж выше и кое-что подправь.
#ifndef _R_
#define _R_
#include <QObject>
#include <QtDebug>
#include <QtGlobal>
class Sender: public QObject
{
Q_OBJECT
public:
Sender(QObject* par=0):QObject(par){ startTimer(1000);}
signals:
void sg();
protected:
void timerEvent(QTimerEvent*)
{
emit sg();
}
};
class Rec: public QObject
{
Q_OBJECT
public:
Rec(QObject* par=0):QObject(par)
{
sender__ = new Sender;
connect(sender__,SIGNAL(sg()),this,SLOT(sl()));
}
private slots:
void sl(){ qDebug()<<__func__;}
private:
Sender* sender__;
};
#endif //_R_
Rescaling image....
[Zoom 113%] "C:\Documents and Settings\XpycT\Мои документы\404.jpg"
i_fileName = C:\Documents and Settings\XpycT\Мои документы\404.jpg
XpycT,
#include <QtGui>
#include "testapp.h"
#include "ImageWidget.h"
TestApp::TestApp(QWidget *parent)
:QDialog(parent)
{
//screen2->loadImage("D:\\21.jpg"); // <<< Если на прямую с конструктора - все работает прекрасно.
QHBoxLayout *layout =new QHBoxLayout;
layout->addWidget(&screen1);
layout->addWidget(&screen2);
setLayout(layout);
connect(&screen1,SIGNAL(imageBrowse()),this,SLOT(openImage()));
}
void TestApp::openImage()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("Select Image"),
QApplication::applicationDirPath(),
tr("Image Files (*.jpg *.jpeg *.png *.bmp *.gif)"));
if(fileName.isEmpty())
return;
else
screen1.loadImage(fileName);
}
ufna,
не пойму тебя
приведи свой пример.
вот мой модифицированный
#ifndef _R_
#define _R_
#include <QObject>
#include <QtDebug>
#include <QtGlobal>
class Sender: public QObject
{
Q_OBJECT
public:
Sender(QObject* par=0):QObject(par){ startTimer(1000);}
signals:
void sg();
protected:
void timerEvent(QTimerEvent*)
{
emit sg();
}
};
class Rec: public QObject
{
Q_OBJECT
public:
Rec(QObject* par=0):QObject(par)
{
Sender* sender__ = new Sender;
connect(sender__,SIGNAL(sg()),this,SLOT(sl()));
}
private slots:
void sl(){ qDebug()<<__func__;}
};
#endif //_R_
#ifndef _R_
#define _R_
#include <QObject>
#include <QtDebug>
#include <QtGlobal>
class Sender: public QObject
{
Q_OBJECT
public:
Sender(QObject* par=0):QObject(par){ startTimer(1000);}
signals:
void sg();
protected:
void timerEvent(QTimerEvent*)
{
emit sg();
}
};
class Rec: public QObject
{
Q_OBJECT
public:
Rec(QObject* par=0):QObject(par)
{
// Sender* sender__ = new Sender;
// connect(sender__,SIGNAL(sg()),this,SLOT(sl()));
}
Sender* CreateSender(){return new Sender;};
private slots:
void sl(){ qDebug()<<__func__;}
};
#endif //_R_
////
файл main.cpp
////
#include <QApplication>
#include "rec.h"
int main(int a,char** b)
{
QApplication app(a,b);
Rec rec;
Sender* sender__ = rec.CreateSender();
QObject::connect(sender__,SIGNAL(sg()),&rec,SLOT(sl()));
return app.exec();
}
kwisp, сорри, тупил. После поста Юрия в доки глянул, понял.
Как то всегда руководствовался правилом - private slot - для внутренних объектов, public slot - для внешних. Изначально как понял, так и делал. Блин )
ufna,
принимается.
со всяким бывает.
твою ж... точно не правильно виджеты создал. Вот что значит невнимательность. Спасибо все работает.
Вот только хотел спросить , как правельнее создать одинаковые QObject::connect'ы перебором, если у меня допустим их штук 20. Ведь как я понимаю - не правильно будет их дублировать типа
connect(screen1,SIGNAL(imageBrowse()),this,SLOT(openImage()));
connect(screen...,SIGNAL(imageBrowse()),this,SLOT(openImage()));
connect(screen20,SIGNAL(imageBrowse()),this,SLOT(openImage()));
XpycT, для случая разных отправителей, лучше используй "карту сигналов" QSignalMaper, я где-то пример на форуме давал, да и в Асистенте он описан достаточно.
П.С. или "отображатель сигналов", или "перенаправитель сигналов", или "коллектор сигналов" не знаю как лучше сказать
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)