Mouse not drawing in pyglet - python

My custom mouse image is not showing up in pyglet. It loads normally(When you create the window outside a class), but when i make the window in a class and try yo add the custom mouse cursor nothing happens
class x:
def __init__(self):
self.game_window = pyglet.window.Window()
self.on_mouse_press = self.game_window.event(self.on_mouse_press)
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.game_window.set_mouse_cursor(self.cursor)
I have tried printing every one of the lines of code (for mouse image loading)
When i print - self.game_cursor = pyglet.image.load('image.png') - This is the result:
ImageData 85x82
When i print - self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70) - This is the result:
pyglet.window.ImageMouseCursor object at 0x7f4dad76b390
When i print - self.game_window.set_mouse_cursor(self.cursor) - This is the result:
None
How do I fix make the mouse show?

I strongly suggest you inherit the window class in to your class instead of keeping it as a internal value if possible. So you can use override hooks to replace the on_ functions. Perhaps this is an outdated approach, but for the time I've learned to work with these things - it's reommended.
class x(pyglet.window.Window):
def __init__(self):
super(x, self).__init__()
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.set_mouse_cursor(self.cursor)
def on_mouse_press():
# Your code handling on_mouse_press
Here's a working example:
from pyglet import *
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.game_cursor = pyglet.image.load('image.png')
self.cursor = pyglet.window.ImageMouseCursor(self.game_cursor, 50, 70)
self.set_mouse_cursor(self.cursor)
self.x, self.y = 0, 0
self.keys = {}
self.mouse_x = 0
self.mouse_y = 0
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
self.mouse_x = x
def on_key_release(self, symbol, modifiers):
try:
del self.keys[symbol]
except:
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
self.keys[symbol] = True
def render(self):
self.clear()
## Add stuff you want to render here.
## Preferably in the form of a batch.
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()

According to pyglet documentation for ImageMouseCursor class link method draw(x, y) is abstract. So I've tried sub-classing ImageMouseCursor and implementing draw method like this:
import pyglet
pyglet.resource.path = ['resources']
pyglet.resource.reindex()
# subclass definition
class GameMouse(pyglet.window.ImageMouseCursor):
# class initialization
def __init__(self):
self.game_cursor = pyglet.resource.image('game_cursor.png')
super().__init__(self.game_cursor, 0, 34)
# method override
def draw(self, x, y):
self.game_cursor.blit(x, y)
However, this will not work if gl blending is not enabled. gl blending is needed to display alpha channels. I've managed to enable it by sub-classing Window class, and using glEnable / glBlendFunc functions. This is the part of the code that does as described:
# subclass definition:
class GameApp(pyglet.window.Window):
# class initialization
def __init__(self):
super(GameApp, self).__init__()
pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA,
pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
Hope this helps

Related

animate a QGraphicsPixmapItem

I am trying to animate a QGraphicsPixmapItem in PyQt5.
The program as is crashes without any error message, and removing the lines about the variable 'anime' makes the program work normally.
here is the QGraphicsPixMapItem:
class QStone(QGraphicsPixmapItem,QGraphicsObject):
def __init__(self, color, movable):
QGraphicsPixmapItem.__init__(self)
QGraphicsObject.__init__(self)
if movable:
self.setFlag(QGraphicsItem.ItemIsMovable)
white = QPixmap("ressources/white2.png")
black = QPixmap("ressources/black_stone.png")
empty = QPixmap("ressources/no_stone.jpg")
if color == Player.white:
self.setPixmap(white.scaled(60, 60, Qt.KeepAspectRatio))
elif color == Player.black:
self.setPixmap(black.scaled(60, 60, Qt.KeepAspectRatio))
self.w = self.boundingRect().width()
self.h = self.boundingRect().height()
def hoverEnterEvent(self, event):
self.setCursor(Qt.OpenHandCursor)
self.setOpacity(0.5)
event.accept()
def hoverLeaveEvent(self, event):
self.setCursor(Qt.ArrowCursor)
self.setOpacity(1.)
event.accept()
the QGraphicsObject inheritance seems to be required for using QPropertyAnimation.
here is the code containing this animation:(this method belongs to a QGraphicsView's subclass):
def display_stone(self, x, y, color=None):
stone = ""
# if color is None:
# stone = QStone("", True)
if color == Player.white:
stone = QStone(Player.white, False)
elif color == Player.black:
stone = QStone(Player.black, False)
stone.setOpacity(0.0)
anime = QPropertyAnimation(stone, b"opacity",self)
anime.setDuration(800)
anime.setStartValue(0.0)
anime.setEndValue(1.0)
anime.start()
stone.setPos(x - stone.w / 2, y - stone.h / 2)
self.scene.addItem(stone)
stone.setZValue(10)
any idea?
thank you
Unlike the Qt C++ API, PyQt does not allow double inheritance (except in exceptional cases(1)) so you cannot implement a class that inherits from QGraphicsPixmapItem and QGraphicsObject.
In this case there are the following options:
1. In this case it is to create a QObject that handles the property you want to modify, and that is the object that is handled by the QPropertyAnimation:
class OpacityManager(QObject):
opacityChanged = pyqtSignal(float)
def __init__(self, initial_opacity, parent=None):
super(OpacityManager, self).__init__(parent)
self._opacity = initial_opacity
#pyqtProperty(float, notify=opacityChanged)
def opacity(self):
return self._opacity
#opacity.setter
def opacity(self, v):
if self._opacity != v:
self._opacity = v
self.opacityChanged.emit(self._opacity)
class QStone(QGraphicsPixmapItem):
def __init__(self, color, movable=False):
QGraphicsPixmapItem.__init__(self)
self.manager = OpacityManager(self.opacity())
self.manager.opacityChanged.connect(self.setOpacity)
if movable:
self.setFlag(QGraphicsItem.ItemIsMovable)
# ...
# ...
anime = QPropertyAnimation(stone.manager, b"opacity", stone.manager)
# ...
2. Another option is QVariantAnimation:
# ...
anime = QVariantAnimation(self)
anime.valueChanged.connect(stone.setOpacity)
anime.setDuration(800)
# ...
(1) https://www.riverbankcomputing.com/static/Docs/PyQt5/qt_interfaces.html

Pyglet running but not responding to esc commands

I am relatively new to Python and Pyglet, I am trying to create an app that dynamically displays photos accordingly to the ID of a command sent via serial.
Here is my code:
import pyglet
from pyglet import clock
import serial
import json
import os
base_dir = 'data'
data = []
currentDatum = ''
def initialiseData():
global data
#load the json
with open('dataset.json') as f:
data = json.load(f)
#for every file in the json load the image
for d in data:
d['media'] = pyglet.image.load(os.path.join(base_dir, d['name']))
print("Scan a tag")
def readSerial(dt):
global currentDatum
tag = ser.readline()
tag = tag.strip()
for d in data:
if d['id'] == tag:
currentDatum = d
print(currentDatum)
ser = serial.Serial('/dev/cu.usbmodem1421', 9600)
initialiseData()
window = pyglet.window.Window(1000, 800, resizable = True)
#window.event
def on_draw():
window.clear()
currentDatum['media'].anchor_x = currentDatum['media'].width/2 - window.width/2
currentDatum['media'].anchor_y = currentDatum['media'].height/2 - window.height/2
currentDatum['media'].blit(0, 0)
clock.schedule(readSerial)
pyglet.app.run()
The application works fine, in the sense that it loads the data from the son and when I send the serial ID it gets read instantly, but every mouse interaction freezes the app: I cannot close the window, cannot resize it, it just gets stuck. Any advice?
I have no idea what the program actually does, or how to use it.
And since I can't replicate your environment (you've got a usbmodem for instance), I'll try my best approach at a generic solution that should work and plan ahead for future development on your project:
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self, width=800, height=600, fps=False, *args, **kwargs):
super(main, self).__init__(width, height, *args, **kwargs)
self.x, self.y = 0, 0
self.batch = pyglet.graphics.Batch()
self.data = []
self.currentDatum = ''
self.ser = serial.Serial('/dev/cu.usbmodem1421', 9600)
self.initialiseData()
pyglet.clock.schedule(self.readSerial)
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_mouse_motion(self, x, y, dx, dy):
pass
def on_mouse_release(self, x, y, button, modifiers):
pass
def on_mouse_press(self, x, y, button, modifiers):
pass
def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
pass
def on_key_release(self, symbol, modifiers):
pass
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
def render(self):
self.clear()
# For future reference, use batches:
self.batch.draw()
# But this is how it's implemented atm:
self.currentDatum['media'].anchor_x = self.currentDatum['media'].width/2 - self.width/2
self.currentDatum['media'].anchor_y = self.currentDatum['media'].height/2 - self.height/2
self.currentDatum['media'].blit(0, 0)
self.flip()
def initialiseData(self):
#load the json
with open('dataset.json') as f:
self.data = json.load(f)
#for every file in the json load the image
for d in self.data:
d['media'] = pyglet.image.load(os.path.join(base_dir, d['name']))
print("Scan a tag")
def readSerial(self, dt):
tag = self.ser.readline()
tag = tag.strip()
for d in self.data:
if d['id'] == tag:
self.currentDatum = d
print(self.currentDatum)
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
if __name__ == '__main__':
x = main()
x.run()
It's bound to be a few run time errors in this code, syntax that I've might have messed up. But I've ported as much of your code as possible into a object oriented way of thinking. Aka a class that inherits the Window class in order for you to modify elements within and the window itself.
There's also some placeholder-functions that you can use to handle mouse and keyboard events. These are inherited/overlapping the pyglet.window.Window class. So any function supported by Window can be put into this class.
There's also a custom pyglet.app.run() that handles the event polling, making sure you don't get stuck on keyboard, mouse or window (resize, move etc) events.
Hope this works.

pyqt and weak/strong ref: how to proceed

[edit]
It seems I solved the problem... In fact, I now do that:
class Gameboard(QGraphicsScene):
def deletePawn(self, num):
pawnToDelete = self.pawns.pop(num)
pawnToDelete.delete()
class Pawn(QGraphicsItem):
def delete(self):
child.prepareGemotryChange()
child.setParent(None)
#idem for each child
self.gameboard.removeItem(self)
self.gameboard = None
[/edit]
What is the good way to implement references in a pyqt QGraphicsScene?
I made a class Gameboard which inherits QGraphicsScene, and this gameboard contains a variable number of pawns (wich inherit QGraphicsPolygonItem)
Pawns can be created or deleted dynamically, creation is ok but deletion sometimes crash...
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtOpenGL
class Gameboard(QGraphicsScene):
def __init__(self):
super(Gameboard, self).__init__()
self.pawns = {}
self.currentPawn = None
def createSquares(self):
#create the polygons of the squares and affect their coordinates x,y
def createPawn(self):
pawn = Pawn(self)
num = len(self.pawns)
self.pawns[num] = pawn
self.addItem(self.pawns[num])
def deletePawn(self, num):
self.currentPawn = None
self.pawns[num].beforeDelete()
self.pawns[num].prepareGeometryChange()
self.removeItem(self.pawns[num])
del self.pawns[num]
def selectPawn(self, pawn):
#a pawn is selected, by click for example
self.currentPawn = pawn
def keyPressEvent(self, event):
ekey = event.key()
if ekey == Qt.Key_Delete and self.currentPawn != None:
num = self.currentPawn.number
self.deletePawn(num)
class Pawn(QGraphicsItem):
def __init__(self, gameboard):
super(Pawn, self).__init__()
self.gameboard = gameboard
self.number = 0
self.pos = (-1,-1)
self.name = ""
def create(self, x, y, num):
#create a QGraphicsPolygonItem, a QGraphixPixmapItem and a QGraphicsTextItem which are child of the QGraphicsItem Pawn, and set position
self.number = num
self.pos = (x,y)
self.gameboard.squares[(x,y)].occupiedBy = self
def move(self, newPos):
self.gameboard.squares[self.pos].occupiedBy = None
self.pos = newPos
self.gameboard.squares[self.pos].occupiedBy = None
def beforeDelete(self):
#a function I add trying to get rid of the crash
self.gameboard = None
self.graphicsPolygon.setParent = None
self.graphicsPix.setParent = None
self.text.setParent = None
def mousePressEvent(self, event):
super(Pawn, self).mousePressEvent(event)
if event.button() == 1:
self.gameboard.currentPawn = self
event.accept()
class Square(QGraphicsPolygonItem):
def __init__(self, gameboard):
self.coordinates = (x,y)
self.occupiedBy = None
What is the proper way to proceed, should I use deleteLater?
Or maybe something with the weakref lib?
Is it because of the variable gameboard in Pawn?

pyqt - error while running, probably wrong paintevent method implementation

Me and my colleagues are writing a data processing application in python.
We are currently working on the frontend part of the application.
We have a big problem though, that's that the application gets the following error after a random amount of time:
QWidget::repaint: Recursive repaint detected
This one also pops up from time to time:
QPainter::begin: Paint device returned engine == 0, type: 1
This is the file where all gui related stuff happens, I cut out the irrelevant methods for the sake of not being to lengthy:
gfx.py:
import sys, random, math
from PyQt4 import QtGui, QtCore
from random import randrange
from eventbased import listener
app = QtGui.QApplication(sys.argv)
def exec():
return app.exec_()
class MapView(QtGui.QMainWindow, listener.Listener):
def __init__(self, mapimagepath = 0, nodes = 0):
QtGui.QMainWindow.__init__(self)
listener.Listener.__init__(self)
self.setWindowTitle('Population mapping')
self.map = Map(self, mapimagepath)
self.setCentralWidget(self.map)
self.map.start()
self.center()
def center(self):
screen = QtGui.QDesktopWidget().screenGeometry()
size = self.geometry()
self.move(50, 0)
def handle(self, event):
if(event.type == 0):
self.map.addNode(event.object.scanner)
if(event.type == 1):
self.map.delNode(event.object.scanner)
if(event.type == 2):
self.map.addBranch(event.object.node1.scanner, event.object.node2.scanner)
if(event.type == 3):
self.map.delBranch(event.object.node1.scanner, event.object.node2.scanner)
if(event.type == 4):
self.map.changeNode(event.object.scanner.sensorid, event.result)
if(event.type == 5):
self.map.changeBranch(event.object.node1.scanner.sensorid, event.object.node2.scanner.sensorid, event.result)
self.repaint(self.map.contentsRect())
self.update(self.map.contentsRect())
######################################################################
class Map(QtGui.QFrame):
def __init__(self, parent, mapimagepath):
QtGui.QFrame.__init__(self, parent)
#self.timer = QtCore.QBasicTimer()
#coordinaten hoeken NE en SW voor kaart in map graphics van SKO
self.realmap = RealMap(
mapimagepath,
(51.0442, 3.7268),
(51.0405, 3.7242),
550,
800)
parent.setGeometry(0,0,self.realmap.width, self.realmap.height)
self.refreshspeed = 5000
self.mapNodes = {}
def addNode(self, scanner):
coord = self.realmap.convertLatLon2Pix((scanner.latitude, scanner.longitude))
self.mapNodes[scanner.sensorid] = MapNode(scanner, coord[0], coord[1])
# type: 4 --> changenode ,
#((change, gem_ref, procentuele verandering ref), scanner object)
def changeNode(self, sensorid, branchdata):
self.mapNodes[sensorid].calcDanger(branchdata[2])
def paintEvent(self, event):
painter = QtGui.QPainter(self)
rect = self.contentsRect()
#teken achtergrond
self.realmap.drawRealMap(painter)
#teken nodes
for sensorid, mapNode in self.mapNodes.items():
mapNode.drawMapNode(painter, self.realmap)
######################################################################
class RealMap:
def __init__(self, path, coordRightTop,
coordLeftBot, width, height, pixpermet = 2.6):
self.path = path
self.coordLeftBot = coordLeftBot
self.coordRightTop = coordRightTop
self.width = width
self.height = height
self.realdim = self.calcRealDim()
self.pixpermet = pixpermet
def drawRealMap(self, painter):
image = QtGui.QImage(self.path)
painter.drawImage(0,0,image)
######################################################################
class MapNode:
dangertocolor = {"normal":"graphics//gradients//green.png",
"elevated":"graphics//gradients//orange.png",
"danger":"graphics//gradients//red.png"}
def __init__(self, scanner, x, y, danger = 0):
self.scanner = scanner
self.x = x
self.y = y
self.danger = 'normal'
self.calcDanger(danger)
def drawMapNode(self, painter, realmap):
radiusm = self.scanner.range
radiusp = radiusm*realmap.pixpermet
factor = radiusp/200 # basis grootte gradiƫnten is 200 pixels.
icon = QtGui.QImage("graphics//BT-icon.png")
grad = QtGui.QImage(MapNode.dangertocolor[self.danger])
grad = grad.scaled(grad.size().width()*factor, grad.size().height()*factor)
painter.drawImage(self.x-100*factor,self.y-100*factor, grad)
painter.drawImage(self.x-10, self.y-10,icon)
painter.drawText(self.x-15, self.y+20, str(self.scanner.sensorid) + '-' + str(self.scanner.name))
An object is made through our application class:
mapview = gfx.MapView(g_image)
mapview.show()
So the first question is. What are we doing wrong in the paintEvent method?
Secondly question
Is there a way to make the paintevent not be called at EVERY RANDOM THING that happens ? (like mouseovers, etc)?
I tried something like:
def paintEvent(self, event):
if(isinstance(event, QtGui.QPaintEvent)):
painter = QtGui.QPainter(self)
rect = self.contentsRect()
#teken achtergrond
self.realmap.drawRealMap(painter)
#teken nodes
for sensorid, mapNode in self.mapNodes.items():
mapNode.drawMapNode(painter, self.realmap)
else:
pass
This 'works' but is to general I guess.. It actually makes the error appear a lot faster then without the conditional.
When in your gfx.py you have:
self.repaint(self.map.contentsRect())
self.update(self.map.contentsRect())
Calling repaint and calling update one right after another is redundant. And if a paint event comes through that handler and you call repaint() there, you are asking for infinite recursion.
Take note of any Warnings or Notes in the documentation.
http://doc.qt.io/qt-4.8/qwidget.html#update
http://doc.qt.io/qt-4.8/qwidget.html#repaint
http://doc.qt.io/qt-4.8/qwidget.html#paintEvent
I don't see the cause for your other error right off, but it probably has to do with QPainter getting used when it shouldn't...
http://doc.qt.io/qt-4.8/qpainter.html#begin
http://doc.qt.io/qt-4.8/qpainter.html#details
Hope that helps.

Python - wxPython custom button -> unbound method __init__()? what?

After looking at questions like this it doesn't make sense that my __init__(self, parrent, id) would be throwing a unbound error? help?
main.py
import wx
from customButton import customButton
from wxPython.wx import *
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(400, 400))
# Non-important code here...
# This is the first declaration of the Button1
# This is also where the ERROR is thrown.
# Omitting this line causes the window to execute
# flawlessly.
self.Button1 = customButton.__init__(self, parent, -1)
# ... finishes in a basic wx.program style...
customButton.py
# I've included all of the code in the file
# because have no idea where the bug/error happens
import wx
from wxPython.wx import *
class Custom_Button(wx.PyControl):
# The BMP's
Over_bmp = None #wxEmptyBitmap(1,1,1) # When the mouse is over
Norm_bmp = None #wxEmptyBitmap(1,1,1) # The normal BMP
Push_bmp = None #wxEmptyBitmap(1,1,1) # The down BMP
def __init__(self, parent, id, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
# Set the BMP's to the ones given in the constructor
#self.Over_bmp = wx.Bitmap(wx.Image(MOUSE_OVER_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Norm_bmp = wx.Bitmap(wx.Image(NORM_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Push_bmp = wx.Bitmap(wx.Image(PUSH_BMP, wx.BITMAP_TYPE_ANY).ConvertToBitmap())
#self.Pos_bmp = self.pos
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self._mouseIn = self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def _onPaint(self, event):
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp)
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self, self.Mouse_over_bmp, self.Pos_bmp, useMask=False)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self, self.Push_bmp, self.Pos_bmp, useMask=False)
You don't create an object like this:
self.Button1 = customButton.__init__(self, parent, -1)
you do it like this:
self.Button1 = customButton(parent, -1)
__init__ is an implicitly invoked method during object creation.
Don't call __init__() explicitly unless you know you need to.
self.Button1 = customButton(parent, -1)

Categories

Resources