In my application I allow the user to resize certain images. All of the images are read on opening the application and held in a dictionary.
My solution allows for resizing, but as the image is Rescaled the quality degrades. I would like to do something like a deepcopy on the image before it is resized, but I don't seem to able to achieve it.
(I know the code seems convoluted, but I have extracted it from a much larger and more complex set of code)
I am just using any old png file as the image source.
import wx
import copy
class MainFrame(wx.Frame):
IMAGE_SIZE = ['large', 'medium', 'small']
IMAGE_NAME = 'question'
def __init__(self, *args, **kwargs):
super(MainFrame, self).__init__(None, *args, **kwargs)
self.Title = 'Test image size'
self.images = self._get_images()
self.panel = MainPanel(self)
sizer = wx.BoxSizer()
sizer.Add(self.panel)
self.SetSizerAndFit(sizer)
self.Centre()
self.Show()
def _get_images(self):
image = wx.Image(self.IMAGE_NAME+'.png')
return {self.IMAGE_NAME: image}
def on_image_size_click(self, event):
size_index = self.panel.image_size.GetSelection()
image_size = self.IMAGE_SIZE[size_index]
self.panel.image.refresh(self.IMAGE_NAME, image_size)
self.Layout()
class MainPanel(wx.Panel):
def __init__(self, parent, *args, **kwargs):
super(MainPanel, self).__init__(parent, *args, **kwargs)
self.images = parent.images
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.image_size = wx.RadioBox(parent=self, label='Image_size',
choices=parent.IMAGE_SIZE,
style=wx.RA_SPECIFY_ROWS)
self.image_size.Bind(wx.EVT_RADIOBOX, parent.on_image_size_click)
self.image = ImagePanel(self, parent.IMAGE_NAME, parent.IMAGE_SIZE[0])
sizer.Add(self.image_size)
sizer.Add(self.image, flag=wx.ALIGN_CENTER)
self.SetSizer(sizer)
class ImagePanel(wx.Panel):
IMAGE_SIZE = {'large': (48, 36), 'medium': (30, 22), 'small': (24, 16)}
def __init__(self, parent, image_name, size, *args, **kwargs):
super(ImagePanel, self).__init__(parent, *args, **kwargs)
self.images = parent.images
self.image_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.refresh(image_name, size)
def refresh(self, image_name, size):
self.clear_sizer(self.image_sizer)
image_size = self.IMAGE_SIZE[size]
image = self.images[image_name]
#image = copy.deepcopy(self.images[image_name])
image.Rescale(image_size[0], image_size[1], wx.IMAGE_QUALITY_HIGH)
bitmap = wx.Bitmap(image)
static_bitmap = wx.StaticBitmap(self, bitmap=bitmap)
self.image_sizer.Add(static_bitmap)
self.SetSizer(self.image_sizer)
#staticmethod
def clear_sizer(sizer):
for child in sizer.GetChildren():
sizer_child = child.GetWindow()
sizer_child.Hide()
sizer.Detach(sizer_child)
if __name__ == '__main__':
"""Run the application."""
screen_app = wx.App()
main_frame = MainFrame()
screen_app.MainLoop()
wx.Image has it's own Copy function, which will work well for your purposes.
image = self.images[image_name]
#image = copy.deepcopy(self.images[image_name])
image = image.Copy()
Related
Image does not show up when trying to draw a rectangle over QLabelimage. I want to be able to draw a rectangle over the photo and be able to keep the rectangle/hide it. Here's what I tried after checking suggestions here:
How to draw a rectangle and adjust its shape by drag and drop in PyQt5
from PyQt5.QtGui import QPixmap, QImage, QPainter, QBrush, QColor
from PyQt5.QtWidgets import QMainWindow, QApplication, QDesktopWidget, QVBoxLayout, QWidget, QLabel
from PyQt5.QtCore import QPoint, QRect
import sys
import cv2
class TestRect(QLabel):
def __init__(self):
super().__init__()
self.begin = QPoint()
self.end = QPoint()
def paintEvent(self, event):
qp = QPainter(self)
br = QBrush(QColor(100, 10, 10, 40))
qp.setBrush(br)
qp.drawRect(QRect(self.begin, self.end))
def mousePressEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
def mouseMoveEvent(self, event):
self.end = event.pos()
self.update()
def mouseReleaseEvent(self, event):
self.begin = event.pos()
self.end = event.pos()
self.update()
class TestWindow(QMainWindow):
def __init__(self):
super().__init__()
self.current_image = None
win_rectangle = self.frameGeometry()
center_point = QDesktopWidget().availableGeometry().center()
win_rectangle.moveCenter(center_point)
self.move(win_rectangle.topLeft())
self.main_layout = QVBoxLayout()
self.central_widget = QWidget(self)
self.test_image()
self.show()
def test_image(self):
self.central_widget.setLayout(self.main_layout)
self.setCentralWidget(self.central_widget)
image = TestRect()
self.main_layout.addWidget(image)
uploaded = cv2.imread('test.jpg')
resized = cv2.resize(uploaded, (500, 500))
height, width = 500, 500
self.current_image = QImage(resized, height, width, QImage.Format_RGB888)
image.setPixmap(QPixmap(self.current_image))
if __name__ == '__main__':
test = QApplication(sys.argv)
test_window = TestWindow()
sys.exit(test.exec_())
If you override a method without calling the parent's implementation through super then the previous behavior will not be used, and that is what happens in your case: The normal behavior of QLabel is to draw but to override and not to call super it was eliminated . The solution is:
def paintEvent(self, event):
super().paintEvent(event)
qp = QPainter(self)
br = QBrush(QColor(100, 10, 10, 40))
qp.setBrush(br)
qp.drawRect(QRect(self.begin, self.end))
My wxPython dirPickerCtrl text box is appearing over the top of its browse button.
My code is:
class chooseFolder(wx.DirPickerCtrl):
def __init__(self, *args, **kwargs):
wx.DirPickerCtrl.__init__(self, *args, **kwargs)
dataFolder = chooseFolder(self, -1, message= 'Choose Folder', pos = (55,24),
style= wx.DIRP_DEFAULT_STYLE,
size = (250, 25))
I have tried applying different sizers in that panel to see if that solves it but to no avail.
I am using wx version 4.0.3
EDIT: Below is a minimal code example to demonstrate the problem I am having, shown in the second picture.
import wx
import os
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self, -1)
chooseFolder(parent=self,
pos = (10,100),
style= wx.DIRP_DEFAULT_STYLE,
size = (250, 30))
self.Centre()
self.Show()
class chooseFolder(wx.DirPickerCtrl):
def __init__(self, *args, **kwargs):
wx.DirPickerCtrl.__init__(self, *args, **kwargs)
if __name__ == "__main__":
app = wx.App()
ButtonFrame()
app.MainLoop()
picture of text box on top of button
EDIT: Picture of output from minimal code example
Edit based on code sample supplied:
I cannot test on Windows but it looks as if it may be a bug (it works on Linux).
Do you get the same result if you don't sub-class DirPickerCtrl but use it directly? Like this:
import wx
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.panel = wx.Panel(self, -1)
chooseFolder = wx.DirPickerCtrl(self.panel, -1, pos = (10,100), path = "",
style= wx.DIRP_DEFAULT_STYLE,
size = (250, 30))
# chooseFolder(parent=self,
# id = -1,
# pos = (10,100),
# path = "/home",
# style= wx.DIRP_DEFAULT_STYLE)#,
# #size = (250, 30))
self.Centre()
self.Show()
#class chooseFolder(wx.DirPickerCtrl):
# def __init__(self, *args, **kwargs):
# wx.DirPickerCtrl.__init__(self, *args, **kwargs)
if __name__ == "__main__":
app = wx.App()
ButtonFrame()
app.MainLoop()
You mention that you have tried applying sizers to the panel but in the question, there is no reference to a panel at all.
Try playing with the code below and see if it helps.
For simplicity, it relies solely on fixed positions (no sizers).
import wx
import os
class ButtonFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.panel = wx.Panel(self, -1)
self.BtnPressHere = wx.Button(self.panel, -1, "Press Here", pos=(10,10))
self.BtnPressHere.Bind(wx.EVT_BUTTON, self.OnPress)
self.Bind(wx.EVT_DIRPICKER_CHANGED, self.DirChange)
self.Centre()
self.Show()
print("Current Dir: ",os.getcwd())
def OnPress(self,event):
dataFolder = chooseFolder(parent=self,
id=-1,
path='/home',
message='Choose Folder',
pos = (10,100),
style= wx.DIRP_DEFAULT_STYLE|wx.DIRP_CHANGE_DIR,
size = (250, 30))
def DirChange(self,event):
print("Dir changed to: ", event.GetPath())
print("Checking Current Dir: ",os.getcwd())
class chooseFolder(wx.DirPickerCtrl):
def __init__(self, *args, **kwargs):
wx.DirPickerCtrl.__init__(self, *args, **kwargs)
if __name__ == "__main__":
app = wx.App()
ButtonFrame()
app.MainLoop()
I think the problem comes from the size given to the constructor. Try setting the height (second parameter of size) to -1 .
I have this code which is not giving the desired output and shows the above error.
require 'gosu'
def media_path(file)
File.join(File.dirname(File.dirname(
__FILE__)),'media', file)
end
#def media_path(file); File.expand_path "media/#{file}", File.dirname(__FILE__)
# end
class Explosion
FRAME_DELAY = 10 # ms
SPRITE = media_path('space.png')
def self.load_animation(window)
Gosu::Image.load_tiles(
window, SPRITE, 128, 128, false)
end
def initialize(animation, x, y)
#animation = animation
#x, #y = x, y
#current_frame = 0
end
def update
#current_frame += 1 if frame_expired?
end
def draw
return if done?
image = current_frame
image.draw(
#x - image.width / 2.0,
#y - image.height / 2.0,
0)
end
def done?
#done ||= #current_frame == #animation.size
end
private
def current_frame
#animation[#current_frame % #animation.size]
end
def frame_expired?
now = Gosu.milliseconds
#last_frame ||= now
if (now - #last_frame) > FRAME_DELAY
#last_frame = now
end
end
end
class GameWindow < Gosu::Window
BACKGROUND = media_path('image.jpg')
def initialize(width=800, height=600, fullscreen=false)
super
self.caption = 'Hello Animation'
#background = Gosu::Image.new(
self, BACKGROUND, false)
#animation = Explosion.load_animation(self)
#explosions = []
end
def update
#explosions.reject!(&:done?)
#explosions.map(&:update)
end
def button_down(id)
close if id == Gosu::KbEscape
if id == Gosu::MsLeft
#explosions.push(
Explosion.new(
#animation, mouse_x, mouse_y))
end
end
def needs_cursor?
true
end
def needs_redraw?
!#scene_ready || #explosions.any?
end
def draw
#scene_ready ||= true
#background.draw(0, 0, 0)
#explosions.map(&:draw)
end
end
window = GameWindow.new
window.show
It can't find ./media/image.jpg are you sure it is there, and can be opened, and is the current directory "." what you think it is?
I never figured out how to use the style parameter. Can someone tell me how to use it to make nicer toggle buttons? Or if you can't use it directly, is there a way to do it manually?
Try something like this:
import wx
class MyForm(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Toggle")
panel = wx.Panel(self, wx.ID_ANY)
self.button = wx.ToggleButton(panel, label="Press Me")
self.button.Bind(wx.EVT_TOGGLEBUTTON, self.onToggle)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.button, 0, wx.ALL, 5)
panel.SetSizer(sizer)
def onToggle(self, event):
if self.button.GetValue() == True:
self.button.SetLabel("On")
else:
self.button.SetLabel("Off")
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
First things first, the debugger doesn't touch my breakpoint.
It is set at the first instruction in the OnPaint method in my custom class.
import wx.aui, wx.lib.agw.aui
from wx.lib import platebtn
import wx.lib.scrolledpanel as spanel
class GuiScrolledPanel(spanel.ScrolledPanel):
def __init__(self, *args, **kwargs):
spanel.ScrolledPanel.__init__(self, *args, **kwargs)
self.SetSizer(GuiSchemaSizer())
self.SetupScrolling()
self.caption = "No active schema - Create a new schema or load one "
def OnChildFocus(self, *args, **kwargs):
self.Layout()
self.AdjustScrollbars()
return spanel.ScrolledPanel.OnChildFocus(self, *args, **kwargs)
def updateCaption(self, caption):
self.caption = caption
def OnPaint(self, *args, **kwargs):
some_result = spanel.ScrolledPanel.OnPaint(self, *args, **kwargs)
print 'OnPaint in MyDrawingArea'
dc = wx.PaintDC(self)
dc.BeginDrawing()
if self.BufferBmp != None:
print '...drawing'
dc.DrawBitmap(self.BufferBmp, 0, 0, True)
#should draw smth here, but what's the point when the method isn't even called, heh?
else:
print '...nothing to draw'
dc.EndDrawing()
return some_result
Thx a lot :)
It seems that the OnPaint method mUst be registered with the event wx.EVT_BIND.
self.Bind(wx.EVT_PAINT, self.OnPaint)