I’m trying to rotate static bitmaps by converting them back to an image, applying the rotation
and converting back to a bitmap. That part of the process seems to work as long as it’s an
90 degree rotation and the bitmap fits neatly into a square. I’d like to be able to resize the
window to fit the bitmap after it’s been rotated if required. Also, the 45 degree rotation seems
a bit off as you’ll see. I’m not sure what’s happening there. I’d also like to know if there’s a problem clicking on the screen object and clicking on it again without moving the mouse.
On my Mac I have to move the mouse pointer to get another click. I’ve had this problem before when deleting bitmaps but I’ve found it in non-python code as well. I’m wondering if it works differently on another OS.
Thanks…
rotate.py (2.9 KB)
When you Rotate
with a non 90 degree angle then the image is resized to be able to fit everything from the original image in it. So your 200x200 image will end up being sized to 285x285. Then on the next rotation you Scale
it back down to 200x200 again, and then it’s sized up to to 285x285 again with the Rotate
. And so on… Each of those scales and rotates are cumulatively distorting the image and so it looks like it’s shrinking and getting uglier with just a few clicks.
A better approach would probably be to keep the original image and always start with it when you want to do a rotation. Also keep track of the current angle, and just increment it with the clicks and then rotate the original image the full amount of the current angle instead of doing it bit by bit. You should also probably do the Scale
after the Rotate
so you can fit a possibly expanded image into the size of the widget.
Hello,
Here’s what I have for rotate now:
def onRotate(self, value):
img = self._bmp.ConvertToImage()
W, H = self._bmp.GetSize()
print( "a: ", value, self._bmp.GetSize() )
# ctr = ((W/2), (H/2))
val = float(value * -pi/180) ## negative value rotates clockwise
img = img.Rotate(val, self._ctr) ## use orignal center
self._bmp = img.ConvertToBitmap()
print( "b: ", value, self._bmp.GetSize() )
self.Layout()
self.Update()
self.Refresh()
and here’s the result
Without doing anything the box seems to grow at 90 degrees and
at 45 it drifts out of the window.
I’m not sure if center is being properly set which may account for
the problem with non 90 degrees but I don’t understand why at 90
it seems to grow. I noticed it earlier with a bitmap scaled to fit,
that’s why I was setting the size back to stop it from getting cut off.
Thanks again,
Mel
Just removing the Scale
does not really comply with my suggestion. This is what I had in mind:
import wx
import wx.lib.statbmp
from math import pi as pi
print(wx.version())
class Actor(wx.lib.statbmp.GenStaticBitmap):
def __init__(self, parent, bitmap, idNum, imgFile, **kwargs):
wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, -1, bitmap, **kwargs)
self._parent = parent ## if needed
self._original_bmp = self._current_bmp = bitmap
self._W, self._H = self.GetSize()
self._ctr = ((self._W/2), (self._H/2))
self._current_angle = 0
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_LEFT_DOWN, self.rotate90)
self.Bind(wx.EVT_RIGHT_DOWN, self.rotate45)
def OnPaint(self, evt=None):
dc = wx.PaintDC(self) ## draw window boundary
dc.SetPen(wx.Pen('blue', 3, wx.SHORT_DASH))
dc.SetBrush(wx.Brush('blue', wx.TRANSPARENT))
dc.DrawRectangle(0,0,200,200)
brush = wx.TRANSPARENT_BRUSH
dc.SetBackground(brush)
dc.Clear()
dc.DrawBitmap(self._current_bmp, 0, 0, True)
def rotate90(self, evt):
self._current_angle += 90
self.onRotate(self._current_angle)
evt.Skip()
def rotate45(self, evt):
self._current_angle += 45
self.onRotate(self._current_angle)
evt.Skip()
def onRotate(self, value):
img = self._original_bmp.ConvertToImage()
val = float(value * -pi/180) ## negative value rotates clockwise
img = img.Rotate(val, self._ctr) ## use orignal center
img = img.Scale(self._W, self._H) ## use original size
self._current_bmp = img.ConvertToBitmap()
print('scale:{} ctr:{} size:{}'.format((self._W, self._H), self._ctr, self._current_bmp.GetSize()))
self.Refresh()
def makeBox():
bmp = wx.Bitmap(200,200)
dc = wx.MemoryDC(bmp)
dc.SetPen(wx.Pen('red', 3, wx.SOLID))
dc.SetBrush(wx.Brush('red'))
dc.DrawRectangle(50,25,100,150)
dc.SetFont(wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL))
dc.DrawText("Top", 80, 30)
dc.SelectObject(wx.NullBitmap)
return wx.Bitmap(bmp)
class Apanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.SetBackgroundColour('white')
bob = Actor(self, Actor.makeBox(), 1, "")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add((0,50),0)
sizer.Add(bob, 0, wx.ALIGN_CENTER)
sizer.Add((0,50),0)
lbl = wx.StaticText(self, label="Left Click for 90 degrees, right for 45")
sizer.Add(lbl, 0, wx.ALIGN_CENTER)
self.SetSizer(sizer)
class FrameIt(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, title='rotate', size=(400,400))
Apanel(self)
self.Center()
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = FrameIt(None)
app.MainLoop()
Thanks Robin,
Your changes helped solve the problem I was experiencing.
Mel