PyQt Pixmap scaling, keep resolution for later - python

I am creating a label with a pixmap in a cell of a QTableWidget, and I want to be able to "zoom" in and out of the table. I accomplish this by scaling the pixmap with .scaled(width, height) which works totally fine, however once the pixmap is scaled down I can not scale it back up again and maintain the original resolution. In other words once it has drawn to a smaller size I no longer have those pixels for later use, so when I scale it up I'm re-sampling the image.
How can I make it so that I am able to "zoom" in and then back out again of a pixmap while efficiently maintaining the image resolution? Is scaled() the wrong way to go altogether? I could theoretically just reference the original file each time I zoom in and out and create a fresh pixmap at the desired scale, but that assumes that the files are always accessible (which they may not be in my case).
EDIT:
Okay so based on comments I need to create a new pixmap copy for each scale cycle, now the question is how cleanest to do that and how do I make a new instance of the pixmap without editing the original? Here's a snippet of basically what I have now:
global pixArray
pixArray = []
# pix array
label = QtGui.QLabel()
pic = QtGui.QPixmap(imageFile)
label.setPixmap(pic)
#toss this label into the master array for later
pixArray.append(label)
# apply scaled image
label = pixArray[len(pixArray)-1]
picScaled = label.pixmap()
label.setPixmap(picScaled.scaled(rowWidth, rowHeight))
# so now the user wants to scale everything, rowWidth and rowHeight have changed:
for column in range(self.table.columnCount()):
# resize the cells
self.table.setColumnWidth(column, rowWidth)
self.table.setRowHeight(0, rowHeight)
# resize the pixmap label
item = self.table.cellWidget(0, column)
label = pixArray[column]
pic = label.pixmap()
# at this point I am actually scaling the original pixmap, how to create a copy?
item.setPixmap(pic.scaled(rowWidth, rowHeight))

Related

Qt QTableView resize to fit content

Hi, I have created MainWindow as shown above. I want to expand the first widget (plots) as much as possible so that the other two widgets fit content (or actually, I want to remove white empty space below tables).
I don't know how to do that.
Currently, both table vertical header size policy is set to FitToContent.
Also, it needs to be dynamic so if I add a new row to the table, a new row should be visible (table will be larger).
I hope I'm clear enough, and also hope there is no need for runnable code.
Ok, I figure it out.
Reimplementing the resizeEvent will do the trick.
def resizeEvent(self, event):
super(Table, self).resizeEvent(event)
height = self.horizontalHeader().height()
for row in range(self.model().rowCount()):
height += self.rowHeight(row)
if self.horizontalScrollBar().isVisible():
height += self.horizontalScrollBar().height()
self.setMaximumHeight(height + 2)
I'm changing the height of QTableView. I'm including height of horizontal header + height of all rows + height of horizontalScrollBar if it is visible.

Repeating texture in a Rectangle of a variable size

I have a texture that I want to repeat inside a Rectangle drawn by canvas.before. The problem is that I don't know what is going to be the size of the Rectangle (it's used as a background for its widget).
For example, I have a Rectangle that has 48 px height and width 100 - 500 px. I want to fill its content by horizontally repeating a 48x48 texture.
I know and tried setting texture.wrap = 'repeat' and texture.uvsize and it works correctly but only if I know the widget size beforehand. For example, setting uvsize = (3, 1) for a widget with size 144x48 works fine.
However, this doesn't work when I want to update uvsize before redrawing the widget. I created a canvas callback and updated uvsize there but this has no effect for some reason:
...
with self.canvas.before:
self.cb = Callback(self.on_canvas_redraw)
...
def on_canvas_redraw(self, instr):
self.texture.uvsize = (self.width / 48, 1)
So how can I dynamically update uvsize? Or is there a better way to handle widget resize or a better way to this altogether?

Python: How to Resize Raster Image with PyQt

I need to find a way to re-size an input raster image (such as jpg) to a specified width/height resolution (given in pixels). It would be great if PyQt while resizing a new image would keep an original image's aspect ratio (so there is no stretching but scaling only).
src = '/Users/usrName/Images/originalImage.jpg' (2048x1024) (rectangular image 2:1 ratio)
dest= '/Users/usrName/Images/originalImage_thumb.jpg' (64x64) (output image is square 1:1 ratio).
Thanks in advance!
POSTED RESULTED FUNC:
...could be used to resize and to convert an image to any format QT supports so far... such as: 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pbm', 'tiff', 'svg', 'xbm'
def resizeImageWithQT(src, dest):
pixmap = QtGui.QPixmap(src)
pixmap_resized = pixmap.scaled(720, 405, QtCore.Qt.KeepAspectRatio)
if not os.path.exists(os.path.dirname(dest)): os.makedirs(os.path.dirname(dest))
pixmap_resized.save(dest)
Create a pixmap:
pixmap = QtGui.QPixmap(path)
and then use QPixmap.scaledToWidth or QPixmap.scaledToHeight:
pixmap2 = pixmap.scaledToWidth(64)
pixmap3 = pixmap.scaledToHeight(64)
With a 2048x1024 image, the first method would result in an image that is 64x32, whilst the second would be 128x64. Obviously it is impossible to resize a 2048x1024 image to 64x64 whilst keeping the same aspect ratio (because the ratios are different).
To avoid choosing between width or height, you can use QPixmap.scaled:
pixmap4 = pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio)
which will automatically adjust to the largest size possible.
To resize the image to an exact size, do:
pixmap5 = pixmap.scaled(64, 64)
Of course, in this case, the resulting image won't keep the same aspect ratio, unless the original image was also 1:1.

Mirror Image but wrong size

I am trying to input an image (image1) and flip it horizontally and then save to a file (image2). This works but not the way I want it to
currently this code gives me a flipped image but it just shows the bottom right quarter of the image, so it is the wrong size. Am I overwriting something somewhere? I just want the code to flip the image horizontally and show the whole picture flipped. Where did I go wrong?
and I cannot just use a mirror function or reverse function, I need to write an algorithm
I get the correct window size but the incorrect image size
def Flip(image1, image2):
img = graphics.Image(graphics.Point(0, 0), image1)
X, Y = img.getWidth(), img.getHeight()
for y in range(Y):
for x in range(X):
r, g, b = img.getPixel(x,y)
color = graphics.color_rgb(r, g, b)
img.setPixel(X-x, y, color)
win = graphics.GraphWin(img, img.getWidth(), img.getHeight())
img.draw(win)
img.save(image2)
I think your problem is in this line:
win = graphics.GraphWin(img, img.getWidth(), img.getHeight())
The first argument to the GraphWin constructor is supposed to be the title, but you are instead giving it an Image object. It makes me believe that maybe the width and height you are supplying are then being ignored. The default width and height for GraphWin is 200 x 200, so depending on the size of your image, that may be why only part of it is being drawn.
Try something like this:
win = graphics.GraphWin("Flipping an Image", img.getWidth(), img.getHeight())
Another problem is that your anchor point for the image is wrong. According to the docs, the anchor point is where the center of the image will be rendered (thus at 0,0 you are only seeing the bottom right quadrant of the picture). Here is a possible solution if you don't know what the size of the image is at the time of creation:
img = graphics.Image(graphics.Point(0, 0), image1)
img.move(img.getWidth() / 2, img.getHeight() / 2)
You are editing your source image. It would be
better to create an image copy and set those pixels instead:
create a new image for editing:
img_new = img
Assign the pixel values to that:
img_new.setPixel(X-x, y, color)
And draw that instead:
win = graphics.GraphWin(img_new, img_new.getWidth(), img_new.getHeight())
img_new.draw(win)
img_new.save(image2)
This will also check that your ranges are correct. if they are not, you will see both flipped and unflipped portions in the final image, showing which portions are outside of your ranges.
If you're not opposed to using an external library, I'd recommend the Python Imaging Library. In particular, the ImageOps module has a mirror function that should do exactly what you want.

wxpython: automatically resize a static image (staticbitmap) to fit into size

My wxPython project has a frame, with multiple nested sizers.
One of the sizers contains a wxStaticImage with a bitmap that is read from a file.
I need the image to resize (grow/shrink) every time the frame is resized, so it will fit it's sizer's boundaries.
(I think that) I know how to resize an image. What I don't know is how to:
how to get the image's container's width or height?
maybe i overlooked a property that does it auotmatically?
(for now, I don't mind the proportions)
Edit: Complete solution
i understood wrong about wxStaticBitmapin.Size. it does NOT describe the size of the image (i.e. image resolution), but rather - wxStaticBitmapin.Size gives the sizer's slot dimentions, or in other words: the current widget's size.
so with Mik's code i now how to fit into the slot.
in addition to mike's solution: when using onSize event on a frame, don't forget to add event.skip(). otherwise the sizers will stop re-aligning. Altertanively, just use the image's onSize.
here's the complete event method:
def bitmap1_onSize(self, e=None):
W, H = self.bitmap1.Size
if W > H:
NewW = W
NewH = W * H / W
else:
NewH = H
NewW = H * W / H
img = wx.Image(self.frame_file_picker.Path, wx.BITMAP_TYPE_ANY)
img = img.Scale(NewW,NewH)
self.bitmap1.SetBitmap(wx.BitmapFromImage(img))
e.Skip()
You will need to catch EVT_SIZE or EVT_SIZING. You can check out this tutorial I wrote about creating an image viewer. It has some scaling code in it: http://www.blog.pythonlibrary.org/2010/03/26/creating-a-simple-photo-viewer-with-wxpython/
I would just take that scaling code and use it to update your image. You'll want to make sure you stop scaling your image up past its maximum size or you'll end up with a lot of pixelization though.

Categories

Resources