O framework QGraphicsView do Qt oferece uma solução robusta e flexível para a exibição e manipulação de elementos gráficos 2D. Esta arquitetura é composta por três classes principais interligadas: QGraphicsScene (a cena), QGraphicsView (a visualização) e QGraphicsItem (os itens gráficos). Compreender a função de cada uma é fundamental para desenvolver aplicações gráficas interativas.
O Core do Framework: QGraphicsScene, QGraphicsView e QGraphicsItem
QGraphicsSceneatua como um container lógico, invisível por si só, que hospeda todos os itens gráficos. É responsável por gerenciar esses itens, distribuir eventos (como cliques do mouse e teclas) entre eles e manter seu estado (seleção, foco). Além disso, a cena oferece capacidaeds de renderização sem transformação, úteis para operações como impressão.QGraphicsViewé o componente visível que exibe o conteúdo de umaQGraphicsScene. Uma única cena pode ser visualizada por múltiplas instâncias deQGraphicsView, cada uma podendo ter sua própria transformação (zoom, rotação, deslocamneto), permitindo diferentes perspectivas da mesma cena.QGraphicsItemé a classe base para todos os elementos que podem ser adicionados a umaQGraphicsScene. O Qt fornece subclasses padrão para formas comuns, como retângulos (QGraphicsRectItem), elipses (QGraphicsEllipseItem) e texto (QGraphicsTextItem), bem como itens para imagens (QGraphicsPixmapItem).
Funcionalidades da QGraphicsScene
A QGraphicsScene desempenha um papel central no gerenciamento de itens gráficos:
- Gerenciamento de Itens: Itens podem ser adicionados à cena usando
QGraphicsScene::addItem(). A recuperação de itens é possível através de métodos comoQGraphicsScene::items()(que pode retornar todos os itens que se interseccionam com um ponto, retângulo, polígono ou caminho vetorial) eQGraphicsScene::itemAt()(para o item mais superior em um ponto específico). A ordem de empilhamento (Z-order) é respeitada, com os itens mais superiores retornados primeiro. - Propagação de Eventos: A cena é o primeiro ponto de contato para eventos de entrada. Ela encaminha esses eventos para os itens apropriados. Por exemplo, um clique do mouse é enviado ao item mais superior sob o cursor.
- Gerenciamento de Estado: A cena controla estados importantes como a seleção de itens e o foco do teclado.
- Seleção: Múltiplos itens podem ser selecionados usando
QGraphicsScene::setSelectionArea(), que aceita umQPainterPathpara definir a área de seleção. A lista de itens selecionados pode ser obtida comQGraphicsScene::selectedItems(). - Foco: O foco do teclado pode ser definido em um item específico via
QGraphicsScene::setFocusItem()ouQGraphicsItem::setFocus(). O item atualmente com foco é recuperado comQGraphicsScene::focusItem().
- Seleção: Múltiplos itens podem ser selecionados usando
- Renderização: A função
QGraphicsScene::render()permite desenhar a cena diretamente em um dispositivo de pintura, útil para saídas não interativas.
Exemplo de Implementação de Operações de Imagem
As funcionalidades interativas são tipicamente implementadas manipulando as propriedades dos QGraphicsItem e as transformações da QGraphicsView. A seguir, demonstraremos como implementar operações comuns como zoom, rotação e troca de camadas.
Controle de Zoom com Roda do Mouse
O zoom pode ser implementado capturando eventos da roda do mouse. Para um zoom centralizado na posição do cursor, a transformação da visualização deve ser ajustada corretamente.
void MinhaVisualizacaoGrafica::wheelEvent(QWheelEvent *evento)
{
// Definir o ponto de âncora para a transformação como a posição do mouse.
// Isso garante que o zoom seja centralizado onde o cursor está.
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
// Fatores de escala
qreal fatorDeEscala = 1.1; // Fator para zoom in
if (evento->angleDelta().y() < 0) { // Roda do mouse para trás (zoom out)
fatorDeEscala = 1.0 / fatorDeEscala; // Fator para zoom out
}
// Limites de zoom para evitar escalas extremas
qreal escalaAtual = transform().m11(); // Obtém a escala atual no eixo X (assumindo escala uniforme)
if ((fatorDeEscala > 1 && escalaAtual > 50.0) || // Limite máximo (50x o tamanho original)
(fatorDeEscala < 1 && escalaAtual < 0.1)) { // Limite mínimo (0.1x o tamanho original)
return;
}
// Aplicar a escala à visualização
scale(fatorDeEscala, fatorDeEscala);
}
Rotação de Imagens
A rotação de itens gráficos, como um QGraphicsPixmapItem, envolve a definição de um ponto de origem de transformação e o ajuste do ângulo de rotação.
void EditorDeImagens::rotacionarEsquerda() {
if (!camadasDeImagem.isEmpty()) {
QGraphicsPixmapItem *camadaAtiva = camadasDeImagem[seletorDeCamadas->currentIndex()];
// O ponto de origem da transformação é o centro do item para uma rotação em torno de seu próprio eixo.
QPointF centro = camadaAtiva->boundingRect().center();
camadaAtiva->setTransformOriginPoint(centro);
// Diminui o ângulo para rotação para a esquerda
camadaAtiva->setRotation(camadaAtiva->rotation() - 15);
}
}
void EditorDeImagens::rotacionarDireita() {
if (!camadasDeImagem.isEmpty()) {
QGraphicsPixmapItem *camadaAtiva = camadasDeImagem[seletorDeCamadas->currentIndex()];
QPointF centro = camadaAtiva->boundingRect().center();
camadaAtiva->setTransformOriginPoint(centro);
// Aumenta o ângulo para rotação para a direita
camadaAtiva->setRotation(camadaAtiva->rotation() + 15);
}
}
Troca de Camadas
Para gerenciar a visibilidade ou a ordem de empilhamento (Z-order) de diferentes camadas, podemos iterar sobre os itens e ajustar suas propriedades. Por exemplo, para "selecionar" ou destacar uma camada, podemos garantir que apenas uma esteja ativa ou trazer a selecionada para a frente.
void EditorDeImagens::selecionarCamada(int indiceDaCamada) {
// Desativa a seleção de todas as camadas visíveis
for (QGraphicsPixmapItem *camada : camadasDeImagem) {
if (camada) { // Verifica se a camada não é nula
camada->setSelected(false);
camada->setZValue(0); // Reseta Z-value para manter a ordem base
}
}
// Ativa a nova camada selecionada, se o índice for válido
if (indiceDaCamada >= 0 && indiceDaCamada < camadasDeImagem.size()) {
QGraphicsPixmapItem *novaCamada = camadasDeImagem[indiceDaCamada];
if (novaCamada) {
novaCamada->setSelected(true);
novaCamada->setZValue(1); // Traz a camada selecionada para frente
// Opcional: ajustar a visibilidade ou focar a visualização na nova camada
}
}
}