Zooming part of QGraphicsItem with its child items - zooming

I'm new to Qt and stuck with an issue. I've a QGraphicsPixmap item with few child items(rectangles and ellipses) in it which subsequently have some child items. Now I want to show a part of Graphicspixmap item with all the child items zooming in a QLabel on mousehover event.
So what I've done is as follows;
GraphicsPixmapItem::GraphicsPixmapItem(QPixmap pixmap):QGraphicsPixmapItem(pixmap)
{
setAcceptsHoverEvents(true);
}
void GraphicsPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
QPointF p = event->pos();
QRect rect(p.x(), p.y(), 100, 100);
lb->setPixmap(pixmap().copy(rect).scaled(lb->width(),lb->height()));
lb->repaint();
QApplication::processEvents();
}
void GraphicsPixmapItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
lb=new QLabel();
lb->resize(400,400);
lb->show();
}
void GraphicsPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
lb->close();
}
This is zooming Graphicspixmap perfectly but not the child items. My question is how to show Graphicspixmap with its child items in the QLabel and is there any better approach to do this?

I've found my solution by my own and thought of sharing that, so that anyone facing the same problem can save their time on this issue.
I've done it in two ways. First I've solved it using mousehover event on the same line as was working before, which is as follows:
void GraphicsPixmapItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
imageviewLabel=new QLabel();
imageviewLabel->setAlignment(Qt::AlignCenter);
imageviewLabel->setGeometry(QApplication::desktop()->width()/2, 50, 600, 600);
imageviewLabel->show();
}
void GraphicsPixmapItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
QRect viewrect(event->pos().x(), event->pos().y(), 100, 100);
imageviewLabel->setPixmap(QPixmap::grabWidget(QApplication::mainWidget(),viewrect).scaled(imageviewLabel->width(),imageviewLabel->height()));
imageviewLabel->repaint();
QApplication::processEvents();
}
void GraphicsPixmapItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
imageviewLabel->close();
}
To make this code working for you, just have to make your main window a main widget which I've set with the flag :
QApplication::setMainWidget(this); //within Mainwindow constructor
Then I found a better solution of my problem which decrease the line of code and complexity significantly. This is by implementing wheelMove event:
void GraphicsPixmapItem::wheelEvent(QGraphicsSceneWheelEvent *event)
{
prepareGeometryChange();
setScale(scale()*exp(-event->delta() / 600.0));
}
I hope this would help at least some of you. Enjoy!

Related

Drawing table borders in libgdx (Table.drawDebug())

I tried using this method to show debug lines:
table.debug();
Table.drawDebug(stage);
except Table.drawDebug expects a parameter of type: ShapeRenderer.
any direction would be great
You may just use a new ShapeRender() as parameter if your code doesn't contain one.
You don't need to call table.drawDebug(), just table.debug() is enough
I think you're looking for this?
Table table;
Stage stage;
public void show() {
table = new Table();
table.debug();
...
stage = new Stage(...);
stage.add(table);
}
public void render(float delta) {
Table.drawDebug(stage);
}
You debug draw the entire stage, but only the elements where debug() was called initially to mark them for debugging.
I hope this helps you.
P.S. btw this is from the official TableLayout docs here which is a must read if you're gonna mess around with Tables (if you haven't done so already).

Canvas repaint should be called by itself

I have an application with jTabbedPane. There are two tab (JPanel) in jTabbedPane. First tab includes canvas and second one includes simple JLabel. Button draws rectangle into canvas.
Every thing is fine until then. However, when switching tabs, canvas would lose everything. It should be repainted by itself.
Rectangle should exist after changing tabs. Do you have any idea about the problem?
My button code is here:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Graphics g = canvas1.getGraphics();
g.drawRect(10, 10, 100, 100);
}
Thanks in advance.
First of all, you shouldn't put AWT components inside Swing components. Use JComponent or JPanel instead of Canvas.
Second, no, it shouldn't repaint itself. When the button is clicked, you should simply store what should be painted in some variable, and the paintComponent() method should be overridden in order to paint what is stored in this variable. This way, every time the component is repainted, it will repaint what has been stored last in this variable.
For example:
public class RectangleComponent extends JComponent {
private boolean shouldPaintRectangle = false;
public void setShouldPaintRectangle(boolean b) {
this.shouldPaintRectangle = b;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (shouldPaintRectangle) {
g.drawRect(10, 10, 100, 100);
}
}
}
In general, you should never ask the Graphics of a component and paint on it. Instead, you should override paintComponent() and paint the component using the Graphics passed as argument.

Override get width or height for just one component- Flex Mobile

Just a little background on what I am trying to do. I have a custom skin where I have a stylable text field to display the date. When clicking on the stylable text field, which is binded to the date, a date spinner comes up. Behind the datespinner I draw a sprite which needs to cover the whole screen so I can detect a click and make the datespinner go away.
Now the problem-
Without overriding the get width or get height I haven't been able to fill the whole screen. However when I do this the datespinner breaks because its getting the height from the override. Just wondering if anyone knew a way to override just one component and set all others to their default values. I know this might seem like a noob question and maybe I am missing something obvious, I am mostly new to as3.
Here is some code-
override protected function createChildren():void {
super.createChildren();
if(!img) {
img = new dateButtonBG();
addChild(img);
}
if (!maskSprite) {
maskSprite = new Sprite();
maskSprite.graphics.beginFill(0xFFFFCC, .5);
maskSprite.graphics.drawRect(-((stage.height)/2),-((stage.width)/2), stage.width, stage.height);
maskSprite.graphics.endFill();
}
if(!dateButton) {
dateButton = new StyleableTextField();
todayDate = new Date();
BindingUtils.bindProperty(dateButton, "text", date, "selectedDate");
dateButton.addEventListener(MouseEvent.CLICK, onDateButtonClick);
addChild(dateButton);
}
invalidateDisplayList();
}
protected function removeSpinner( event:Event):void{
maskSprite.removeEventListener(MouseEvent.CLICK, removeSpinner);
removeChild(date);
removeChild(maskSprite);
}
protected function onDateButtonClick (event:Event):void {
addChild(maskSprite);
addChild(date);
maskSprite.addEventListener(MouseEvent.CLICK, removeSpinner);
}
override public function get width () : Number {
return _width;
}
override public function get height () : Number {
return _height;
}
The code is not complete but is just for getting my point across. Thanks for reading and all your help.
EDIT-
I figured out how to do it.Adding some information in case some one has the same problem-
Flex limits the size of your sprite( or any UI component) to the size of the container. If you try to go over the size of the container it just returns the size of the container. In my code-
override public function get width () : Number {
return _width;
}
override public function get height () : Number {
return _height;
}
This is commonly touted as a fix to go over the size of the container. This approach is flawed however because it overrides everything that asks for width and height. In this case it tries to make everything the size of _height and _width. For any skin that has more than 1 component this is a huge problem, either you can try to set sizes for items indivigually, which for me didn't work or find an alternate approach.
Here is what works-
public override function validateDisplayList():void {
super.validateDisplayList();
yourComponent.width = someConstantW;
yourComponent.height = someConstantH;
}
This is not going to be enough however. You might need to move your component outside of the container for this try-
override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
super.layoutContents(unscaledWidth, unscaledHeight);
setElementPosition(yourComponent, x, y);
}
Hopefully I saved someone a few hours of work :)
It's not quite clear from the code you have posted, but perhaps you should be overriding the updateDisplayList() method instead of the width and height.
updateDisplayList() is the Flex life cycle method that components implement to size and position their child objects.
updateDisplayList() is called by the Flex framework, and it passes in two values: unscaledWidth and unscaledHeight. Your implmentation of updateDisplayList() should honor those values and size/position the child objects accordingly. You should use other life cycle methods (shown below) to do the actual sizing/positioning.
Here's an example implementation to get you started. It may be off a little, as I don't fully understand the code snippet you provided.
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// size/position the background (or whatever it is that should occupy the whole screen)
background.setLayoutBoundsPosition(0,0); // unnecessary because 0,0 is default
background.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
// size/position the text in the center
var textWidth:Number = text.getPreferredBoundsWidth;
var textheight:Number = text.getPreferredBoundsHeight
text.setLayoutBoundsPosition( (unscaledWidth - textWidth)/2, (unscaledHeight - textHeight)/2 );
text.setLayoutBoundsSize(textWidth, textHeight); // probably not necessary
}

Better choice: TextLayout or JTextComponent for an "ellipse with editable text" component?

If you've ever used Visio or a UML class diagram editor, you have an idea of what I'm trying to accomplish: Within a JFrame, users can add ellipses that enclose a small editable text field. These ellipses can be repositioned within the frame when the user drags them. Clicking on an ellipse causes the text to become editable: a carat appears, highlighting a substring is possible, etc.
I've got the basic structure set up: the 'ellipse' is a self-contained component, with methods called on it from the containing JFrame and its listeners. I've tried two approaches:
in the component's draw() method, use a TextLayout to find bounds, position the contained text within the ellipse, and draw it to the frame using TextLayout's draw(). This is fast. Dragging the components around in the JFrame, mouse-over and mouse-click behavior are all straightforward. However for the editing functionality it looks like I will need to write a lot of custom code to handle hit testing, carat positioning, text highlighting, line wrapping, etc.
having the component contain a reference to the containing JFrame, and adding or repositioning a TextComponent in that JFrame after drawing the ellipse. This has the advantage of all the built-in TextComponent behavior for editing and line wrapping. But the logistics are really sloppy, and positioning the TextComponent becomes messy too - especially when the user drags the component around.
I'm quite possibly thinking about this all wrong. Can anyone suggest a simple way to do this that I haven't yet stumbled across?
Why don't you combine both your approaches. As long as you are editing, display the text component, otherwise paint all text using a TextLayout. The following example code shows such an approach extending a simple JComponent. It draws a rectangular shape with some text in it and if you click inside it shows an editing possibility. As soon as you click outside again, the component vanished. Note that all the edit-handling functionality is missing in this basic example.
class TestComponent extends JComponent {
JTextArea jta = new JTextArea("12345");
public TestComponent() {
setPreferredSize(new Dimension(400, 400));
setLayout(null);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (e.getX() >= 40 && e.getX() <= 200 && e.getY() >= 40 && e.getY() <= 80) {
TestComponent.this.add(jta);
jta.setBounds(42, 42, 156, 36);
} else {
TestComponent.this.remove(jta);
}
repaint();
}
});
}
});
}
#Override
public void paintComponent(Graphics _g) {
Graphics2D g = (Graphics2D) _g;
g.drawRect(40, 40, 160, 40);
TextLayout layout = new TextLayout("12345", g.getFont(), g.getFontRenderContext());
layout.draw(g, 42, 42 + layout.getAscent());
}
}

How to prevent JPopUpMenu disappearing when checking checkboxes in it?

I want to use JCheckBoxMenuItems in a JPopupMenu. It works, but the problem is that the popup menu disappears when a checkbox item has been checked or unchecked. So if one wants to check/uncheck several items, the popup needs to be launched repeatedly, which is irritating.
Curiously, if I use just plain JCheckBox items in the menu (instead of JCheckBoxMenuItems), the behavior is just as it should be: the popup stays there and the checkboxes can be checked/unchecked. Once done, the popup can be closed just by clicking outside it.
How do I make the popup to behave like that when the items there are JCheckBoxMenuItems? I would prefer using JCheckBoxMenuItems because of their looks.
Well, found working answer from http://forums.sun.com/thread.jspa?threadID=5432911. Basically, create a custom UI:
public class StayOpenCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI {
#Override
protected void doClick(MenuSelectionManager msm) {
menuItem.doClick(0);
}
public static ComponentUI createUI(JComponent c) {
return new StayOpenCheckBoxMenuItemUI();
}
}
And set it in the JCheckBoxMenuItem:
myJCheckBoxMenuItem.setUI(new StayOpenCheckBoxMenuItemUI());
Don't know if this is the most elegant possible solution, but works perfectly.
I ran into an issue with the nice Joonas Pulakka's answer because the "UIManager lookandFeel" was ignored.
I found the nice trick below on http://tips4java.wordpress.com/2010/09/12/keeping-menus-open/
The point is to reopen immediatly the menu after it has been closed, it's invisible and keep the application look and feel and behavior.
public class StayOpenCBItem extends JCheckBoxMenuItem {
private static MenuElement[] path;
{
getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (getModel().isArmed() && isShowing()) {
path = MenuSelectionManager.defaultManager().getSelectedPath();
}
}
});
}
public StayOpenCBItem(String text) {
super(text);
}
#Override
public void doClick(int pressTime) {
super.doClick(pressTime);
MenuSelectionManager.defaultManager().setSelectedPath(path);
}
}
I found a much easier solution for this problem
JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem("sample");
menuItem.putClientProperty("CheckBoxMenuItem.doNotCloseOnMouseClick", Boolean.TRUE);
I found this solution while reading the code from
BasicMenuItemUI.doNotCloseOnMouseClick()