1

I am working with OpenGL in python and trying to attach 2d images to a canvas (the images will change according to a certain frequence).

I managed to achieve that but to continue my task i need two things:

  1. the major problem: I need to get the image position (or bounds), sorry if i don't have the correct term, i am new to this. basically i just need to have some kind of positions to know where my picture is in the canvas. i tried to look into the methods and attributes of self.view.camera I could not find anything to help.

  2. one minor problem: i can move the image with the mouse along the canvas and i zoom it. i wonder if it is possible to only allow the zoom but not allow the right/left move [this is resolved in the comments section]

here is my code:

import sys
from PySide2 import QtWidgets, QtCore
from vispy import scene
from PySide2.QtCore import QMetaObject
from PySide2.QtWidgets import *
import numpy as np
import dog
import time
import imageio as iio

class CameraThread(QtCore.QThread):
    new_image = QtCore.Signal(object)

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
            try:
                while True:
                    frame = iio.imread(dog.getDog(filename='randog'))
                    self.new_image.emit(frame.data)
                    time.sleep(10.0)
            finally:
                print('end!')


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 400)

        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")

        self.groupBox = QGroupBox(self.centralwidget)
        self.groupBox.setObjectName("groupBox")

        self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1)

        MainWindow.setCentralWidget(self.centralwidget)

        QMetaObject.connectSlotsByName(MainWindow)



class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # OpenGL drawing surface
        self.canvas = scene.SceneCanvas(keys='interactive')
        self.canvas.create_native()
        self.canvas.native.setParent(self)


        self.setWindowTitle('MyApp')


        self.view = self.canvas.central_widget.add_view()

        self.view.bgcolor = '#ffffff'   # set the canvas to a white background

        self.image = scene.visuals.Image(np.zeros((1, 1)),
                                         interpolation='nearest',
                                         parent= self.view.scene,
                                         cmap='grays',
                                         clim=(0, 2 ** 8 - 1))


        self.view.camera = scene.PanZoomCamera(aspect=1)
        self.view.camera.flip = (0, 1, 0)
        self.view.camera.set_range()
        self.view.camera.zoom(1000, (0, 0))

        self._camera_runner = CameraThread(parent=self)
        self._camera_runner.new_image.connect(self.new_image, type=QtCore.Qt.BlockingQueuedConnection)
        self._camera_runner.start()


    @QtCore.Slot(object)
    def new_image(self, img):
        try:
            self.image.set_data(img)
            self.image.update()
        except Exception as e:
            print(f"problem sending image: {e}")


def main():
    import ctypes
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('my_gui')

    app = QtWidgets.QApplication([])

    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Josh.h
  • 71
  • 1
  • 13
  • concerning the minor problem. it is easily fixable by reimplementing "viewbox_mouse_event" of the class PanZoomCamera and acting on the button events... – Josh.h Dec 02 '22 at 13:00
  • why you didn't use [QGraphicsScene](https://doc.qt.io/qt-6/qgraphicsscene.html) and [QGraphicsView](https://doc.qt.io/qt-6/qgraphicsview.html)? – Parisa.H.R Dec 04 '22 at 20:26
  • would those libraries offer more flexibility? – Josh.h Dec 06 '22 at 08:05
  • yes, you can see their examples [Graphics View Examples](https://doc.qt.io/qt-6/examples-graphicsview.html) – Parisa.H.R Dec 06 '22 at 08:43
  • Sorry I missed this, the notification got lost in my inbox. When you say image position, do you mean the coordinates of the image position as you see it on the canvas? Without any other transforms applied, an ImageVisual has vertices from 0 to width and 0 to height iirc. You can do some of the transform tricks from other answers to convert that to "canvas" space or any other coordinate system that vispy knows about. So when you pan your view so the image is to the right, do you want the coordinates as if the canvas corner is (0, 0)? – djhoese Dec 22 '22 at 19:21

2 Answers2

0

Do you want to know the coordinates of the picture in the viewport (the window), or do you want the coordinates of the picture on the canvas? Vispy actually puts the image at (0,0) by default inside the Vispy canvas. When you move around the canvas you actually aren't moving the canvas around, you are just moving the camera which is looking at the canvas so the coordinates of the picture stay at (0,0) regardless if you move around the viewport or the camera or not. Also the coordinates of the Vispy canvas correspond one to one with the pixel length and width of your image. One pixel is one unit in Vispy. You can check this by adding this method to your MainWindow class:

def my_handler(self,event):
    

    transform = self.image.transforms.get_transform(map_to="canvas")
    img_x, img_y = transform.imap(event.pos)[:2]
    print(img_x, img_y)
    # optionally do the below to tell other handlers not to look at this event:
    event.handled = True

and adding this to your __init__ method:

self.canvas.events.mouse_move.connect(self.my_handler)

You can see that when you hover over the top left corner of your image, it should print roughly (0,0).

  • THank you for your answer. something like this could help me. Yes I understand the 0,0 story. whatever zoom i do on the image it will always be at 0,0 on the top left corner. is there any possibility to get the absolute position in the viewport then ??? – Josh.h Dec 05 '22 at 08:18
  • @Josh.h Sorry, but I do not think that there is a way to get the absolute coordinates of the image in the viewport, but you can get the absolute coordinates of a mouse event by using event.pos. In the code which I showed you if you replace print(img_x, img_y) with print(event.pos) then you get the coordinates of the mouse in absolute coordinates in the viewport, but sadly there isn't a way to get the absolute coordinates of the image. – Some nerd who does not have a Dec 10 '22 at 00:32
  • thank you for your answer. i searched a lot and i was under the same impression that there is no way to get that position – Josh.h Dec 14 '22 at 11:52
0

def my_handler(self,event):

transform = self.image.transforms.get_transform(map_to="canvas")
img_x, img_y = transform.imap(event.pos)[:2]
print(img_x, img_y)
# optionally do the below to tell other handlers not to look at this event:
event.handled = True
  • i want the position of the image in the canvas. so if i move i know where it is. i dont want the positions of the image itself where 0,0 is always on the top left – Josh.h Dec 07 '22 at 19:25
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 10 '22 at 22:22