How can I use pyglet batches to draw scenes or levels - pyglet

So I'm currently learning pyglet for Python 2.7 and I'm trying to make a simple game that has levels. The 1st 'scene' would be the title/intro part, 2nd would be a tutorial of some sort, and the rest are the game levels themselves.
For this, I've created 7 batches(1 intro, 1 tutorial, 5 levels) namely batch, batch1, ... batch6. I've also created 7 classes for each of these batches that represent the scenes/levels. This is what I've done for the intro batch and class:
batch = pyglet.graphics.Batch()
batch1 = pyglet.graphics.Batch()
class StartState:
def __init__(self):
self.welcome = pyglet.text.Label('WELCOME TO', font_name='Arial', font_size=32, color=(200,255,255,255), x=400, y=550, anchor_x='center', anchor_y='center', batch=batch)
self.title = pyglet.text.Label("MY GAME", font_name='Arial', font_size=32, color=(100,200,170,255), x=400, y=450, anchor_x='center', anchor_y='center', batch=batch)
self.press = pyglet.text.Label("press 'SPACE' to continue", font_name='Arial', font_size=32, color=(200,255,150,255), x=400, y=250, anchor_x='center', anchor_y='center', batch=batch)
def update(self, dt):
if keymap[pyglet.window.key.SPACE]:
self.welcome.delete()
self.title.delete()
self.press.delete()
states.pop()
batch1.draw()
The other scenes would also look like that. the states list is a list that I use to store my classes/scenes. states = [Level5(), Level4(), ... , TutorialState(), StartState()]. So every time the condition to advance is fulfilled, which in this class is to press 'SPACE', the window will be 'cleared' i.e. delete the sprites/labels and proceed to the next scene by using states.pop() and batch1.draw().
After I've typed these classes, I added this at the end:
#window.event
def on_draw():
window.clear()
batch.draw()
def update(dt):
if len(states):
states[-1].update(dt)
else:
pyglet.app.exit()
states.append(Level5())
states.append(Level4())
states.append(Level3())
states.append(Level2())
states.append(Level1())
states.append(TutorialState())
states.append(StartState())
pyglet.clock.schedule_interval(update, 1.0/60.0)
window.clear()
window.flip()
window.set_visible(True)
pyglet.app.run()
The problem here is that it only loads the starting batch/scene. Whenever I press 'SPACE' to go to the tutorial scene the labels/sprites of the starting batch disappear but it doesn't draw batch1 or load the the tutorial class/scene. Any suggestions?

After creating a batch for each scene class:
import pyglet
from pyglet.window import key
class SceneTemplate(object):
"""a template with common things used by every scene"""
def __init__(self, text):
self.batch = pyglet.graphics.Batch()
self.label = pyglet.text.Label(
text,
font_name='Arial', font_size=32,
color=(200, 255, 255, 255), x=32, y=704,
batch=self.batch)
# (...)
class MainMenuScene(SceneTemplate):
def __init__(self):
super(MainMenuScene, self).__init__(text='MainMenuScene')
# (...)
class IntroScene(SceneTemplate):
def __init__(self):
super(IntroScene, self).__init__(text='Introduction')
# (...)
class Level1(SceneTemplate):
def __init__(self):
super(Level1, self).__init__(text='Level 1')
# (...)
You can control the state/scene in another class, such as a window class (personally I like to subclass the pyglet window, to keep things organized and some other reasons):
class Window(pyglet.window.Window):
def __init__(self):
super(Window, self).__init__(width=1024, height=768)
self.states = [MainMenuScene(), IntroScene(), Level1()] # and so on...
self.current_state = 0 # later you change it to get the scene you want
self.set_visible()
def on_draw(self):
self.clear()
self.states[self.current_state].batch.draw()
def on_key_press(self, symbol, modifiers):
if symbol == key.SPACE:
new_state = self.current_state + 1
new_state = new_state % len(self.states)
self.current_state = new_state
# if you want each scene to handle input, you could use pyglet's push_handlers(), or even something like:
# self.states[self.current_state].on_key_press(symbol, modifiers)
# giving them access to the window instance might be needed.
if __name__ == '__main__':
window = Window()
pyglet.app.run()

Related

Coin Class HW assignment

So I am doing a Coin Flip Class program for class, and this code straight from the book is giving me errors in Pycharm and VSC. I have reread this 10 times and can not find the error to get the program to run. Trying to figure out if I am missing something, or the example source code is off.
import random
# The Coin Class simulates a coin that can be flipped
class coin:
def __init__(self):
side.up = 'Heads'
# Side up data attribute w/ heads
# The Toss generates a random number in
# the range of 0 - 1. If the number is 0, then side up is heads, otherwise side up is tails
def toss(self):
if random.randint(0, 1) == 0:
self.sideup = 'Heads'
else:
self.sideup = 'Tails'
# The get_sideup method returns the value referenced by sideup
def get_sideup(self):
return self.sideup
# The main function
def main():
# create an object from the coin class
my_coin = coin()
# Display that side facing up
print('This side is up:', my_coin.get_sideup())
# Toss Coin
print('I am tossing the coin . . .')
my_coin.toss()
# Display the side of the coin that is facing up
print('This side is up:', my_coin.get_sideup())
# Call the main function
main()
``
You have a variable that is equal to nothing, self, and you are attempting to access an attribute of it called "up"
def __init__(self):
side.up = 'Heads'

Passing variables between classes in Tkinter, Python 3

I'm kind of a newbie to Python, and I'm writing some code to take data via a user input and put it into a .csv file. To do that, the program needs to pass data from class to class.
To teach myself how to pass data, I took code from here. I did have to alter the code a bit to get it to start up, making sure that the make_widget and print_it functions can pull the "name" variable stored in self.app_data data structure properly.
from tkinter import *
from tkinter import ttk
class MyApp(Tk):
def __init__(self):
Tk.__init__(self)
self.app_data={'name': StringVar}
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand = True)
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky = NSEW)
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller=controller
ttk.Label(self, text='PageOne').grid(padx=(20,20), pady=(20,20))
self.make_widget(controller)
def make_widget(self, controller):
self.controller=controller
self.some_entry = ttk.Entry(self, textvariable=self.controller.app_data['name'], width=8)
self.some_entry.grid()
button1 = ttk.Button(self, text='Next Page',command=lambda: controller.show_frame(PageTwo))
button1.grid()
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller=controller
ttk.Label(self, text='PageTwo').grid(padx=(20,20), pady=(20,20))
button1 = ttk.Button(self, text='Previous Page',command=lambda: controller.show_frame(PageOne))
button1.grid()
button2 = ttk.Button(self, text='press to print', command= self.print_it())
button2.grid()
def print_it(self):
value=self.controller.app_data['name'].get()
print ('The value stored in StartPage some_entry = ', value)#What do I put here
#to print the value of some_input from PageOne
When I run this program, it does start up, and I can move from frame to frame, but it does not print the "name" variable.
When I close the window, I get the error:
TypeError: get() missing 1 required positional argument: 'self'
Which the traceback blames on the line:
value=self.controller.app_data['name'].get()
What am I doing wrong? For what it's worth, I'm writing the code in Python 3.5.
I really appreciate any help that you guys could give me.

PySide - QSortFilterProxyModel and QListView - indexWidget pointer get deleted when filtering

I've a problem with a custom QListView I'm trying to make, here the problem:
I'm using QListView to show a list of QWidget by using QListView.setIndexWidget(index,widget).
This is working pretty fine, but now I want to filter the items model by using QSortFilterProxyModel()
with .setFilterWildcard()
It is not working very well because the second time the model is filtered
I got error like this :
RuntimeError: Internal C++ object (PySide.QtGui.QLabel) already deleted.
Without using filtering and QSortFilterProxyModel everything works fine, but it seems I'm missing
something with the filtering operation, the indexWidget() is deleted when using filtering :(
here a sample code where you can reproduce the bug, when list view is shown, hit 1,2 or 3 keyboard
key to activate filtering ( Backspace to set filtering empty to show all items )
Here the sample code to reproduce the problem:
import PySide.QtGui as QtGui
import PySide.QtCore as QtCore
_DEFAULT_ITEM_SIZE = QtCore.QSize(100, 85)
_USER_ROLE = QtGui.QStandardItem.UserType + 1
class CustomItemWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CustomItemWidget, self).__init__(parent=parent)
#self.setAutoFillBackground(True)
self.main_layout = QtGui.QVBoxLayout(self)
self.label = QtGui.QLabel(self)
self.main_layout.addWidget(self.label)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
# Default brush and pen
bg_brush = QtGui.QBrush(QtGui.QColor("#8C8C8C"))
pen = QtCore.Qt.NoPen
painter.save()
painter.setPen(pen)
painter.setBrush(bg_brush)
painter.drawRoundedRect(self.rect(), 12, 12)
painter.restore()
def setData(self, role, value):
if role == QtCore.Qt.DisplayRole:
self.label.setText(value)
class CustomItem(QtGui.QStandardItem):
def __init__(self):
super(CustomItem, self).__init__()
self.number = None
self.item_widget = CustomItemWidget()
self.setSelectable(True)
def type(self):
return _USER_ROLE
def data(self, role):
if role == QtCore.Qt.DisplayRole:
value = "DATA %s" % str(self.number)
self.item_widget.setData(role, value)
return value
if role == QtCore.Qt.SizeHintRole:
return _DEFAULT_ITEM_SIZE
return QtGui.QStandardItem.data(self, role)
class CustomItemDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None):
super(CustomItemDelegate, self).__init__(parent=parent)
class CustomItemModel(QtGui.QStandardItemModel):
def __init__(self, parent=None):
super(CustomItemModel, self).__init__(parent)
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | \
QtCore.Qt.ItemIsSelectable | \
QtCore.Qt.ItemIsDragEnabled | \
QtCore.Qt.ItemIsDropEnabled
class CustomItemFilterProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, parent=None):
super(CustomItemFilterProxyModel, self).__init__(parent)
self.setDynamicSortFilter(True)
self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setFilterKeyColumn(0)
class CustomView(QtGui.QListView):
def __init__(self, parent=None):
super(CustomView, self).__init__(parent=parent)
self.setIconSize(_DEFAULT_ITEM_SIZE)
self.setMovement(QtGui.QListView.Static)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setSelectionBehavior(QtGui.QAbstractItemView.SelectItems)
self.setViewMode(QtGui.QListView.IconMode)
self.setUniformItemSizes(True)
self.setFlow(QtGui.QListView.LeftToRight)
self.setResizeMode(QtGui.QListView.Adjust)
self.data_model = CustomItemModel(self)
self.proxy_model = CustomItemFilterProxyModel(self)
self.proxy_model.setSourceModel(self.data_model)
self.setModel(self.proxy_model)
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_1:
self.proxy_model.setFilterWildcard("*1*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_2:
self.proxy_model.setFilterWildcard("*2*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_3:
self.proxy_model.setFilterWildcard("*3*")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_Backspace:
self.proxy_model.setFilterFixedString("")
print self.proxy_model.filterRegExp()
if event.key() == QtCore.Qt.Key_Plus:
self.addNewItem()
QtGui.QListView.keyPressEvent(self, event)
def addNewItem(self):
item = CustomItem()
item.number = self.data_model.rowCount()
self.addItem(item)
def addItem(self, item):
self.data_model.appendRow(item)
proxy_index = self.proxy_model.mapFromSource(item.index())
self.setIndexWidget(proxy_index, item.item_widget)
if __name__ == '__main__':
import sys
qapplication = QtGui.QApplication(sys.argv)
layout = QtGui.QVBoxLayout()
window = QtGui.QDialog()
window.setLayout(layout)
view = CustomView(window)
view.resize(800, 600)
layout.addWidget(view)
for i in range(0, 10):
item = CustomItem()
item.number = i
view.addItem(item)
window.show()
sys.exit(qapplication.exec_())
or sample code here:
https://gist.github.com/66e29df303d1f1825a53
Can someone please help me on this? is this a known bug ? or I'm doing it completely wrong :P
Thanks in advance for your help.
This is an old question, but as I struggled with a similar problem for quite a while, here the solution I found and a possible explanation:
Instead of caching the custom widget on the model item, I cached the data needed to create the widget. In my case, I wanted to use a custom label with html in order to be able to format parts of text in different colour. Hence, I cached the html string on the item.
Then, in the initStyleOption method of the item delegate, I recreated the widget if it didn't yet exist or had disappeared after filtering:
label = self.parent().indexWidget(modelIndex)
if not label:
label = CustomLabel(item.html)
self.parent().setIndexWidget(modelIndex, label)
The reason why filtering deletes the widget cached on the item is as follows, I believe: the widget can "exist" only in one place. When it is put as indexWidget, it "exists" on a row in the view, not in an item of the model any more. As filtering removes rows from view, widgets on those rows get deleted. - A poor explanation, but I've often got similar surprises when manipulating html elements with JavaScript if I've forgotten to clone the element.

Calling an global function from inside a class. Python 2.7 using Tkinter

I am working on creating a LabelFrame class using Tkinter, which creates a wrapper around a group of items I wish to include one by one, by pressing an add button, that calls a function to create more of that item.
I have the code running, where I can see the LabelFrame and addbuttun. But once I press the button the function being called I get an error:
addmeter() takes exactly 1 argument (0 given)
I need to this function to add a class inside the LabelFrame, and this is where I am stuck.
I have listed my code below.
from Tkinter import *
root = Tk()
root.title("LabelFrame with embedded add voltmeters")
root.geometry("600x200+400+400")
def addmeter(self):
#Create frame for the voltmeter
voltsmet1 = LabelFrame(self.master, text = "Volts")
#add Text box for the serial output.
voltinfo = Text(voltsmet1, bg="BLACK", height=10, width =20 )
#add in reg command to find our data from queue and display it
#packs the widgets on the grid for display
voltsmet1.pack(side=LEFT, expand=True)
voltinfo.pack(side=LEFT, expand=True)
class wrapper(LabelFrame):
def __init__(self,master):
self.master = master
self.create_wrapper()
def create_wrapper(self):
wrapper = LabelFrame(self.master, text = "Volt Meters")
add_button = Button(wrapper, text="add", command=addmeter)
wrapper.pack()
add_button.pack()
new= wrapper(root)
root.mainloop()
Use lambda function:
add_button = Button(wrapper, text="add", command=lambda:addmeter(self))
EDIT:
Do you mean this ?
I use wrapper in lambda function
add_button = Button(wrapper, text="add", command=lambda:addmeter(wrapper))
and I remove .master in addmeter
Full code:
from Tkinter import *
root = Tk()
root.title("LabelFrame with embedded add voltmeters")
root.geometry("600x200+400+400")
def addmeter(parent):
#Create frame for the voltmeter
voltsmet1 = LabelFrame(parent, text = "Volts")
#add Text box for the serial output.
voltinfo = Text(voltsmet1, bg="BLACK", height=10, width =20 )
#add in reg command to find our data from queue and display it
#packs the widgets on the grid for display
voltsmet1.pack(side=LEFT, expand=True)
voltinfo.pack(side=LEFT, expand=True)
class wrapper(LabelFrame):
def __init__(self,master):
self.master = master
self.create_wrapper()
def create_wrapper(self):
wrapper = LabelFrame(self.master, text = "Volt Meters")
add_button = Button(wrapper, text="add", command=lambda:addmeter(wrapper))
wrapper.pack()
add_button.pack()
new= wrapper(root)
root.mainloop()
btw: I change name self to parent in addmeter() to make names more logical.

Wx Matplotlib Event Handling

I am working on a WX-MPL app to display and interactively process data based on user input. I am having a difficulties setting up the MPL mouse events to work within the WX app.
The goal is to have a series of editable vertical lines that delineate the begin and end times for features which have been identified in earlier processing. Users should be able to drag the lines along the x-axis, delete unwanted lines, and insert new lines.
So far I have been able to achieve this functionality using the standard MPL plot figure (I think its Tk). When I try to integrate these classes into my app, something is going astray with the event handling and I am not able to interact with the line objects or create new line objects. So I backed up a step and have been trying to add complexity into my working example to determine where the problems are coming into the picture.
When running the working example as a simple WX app, the NewVLineListener class appears to no longer receive button_press_events. Below is the code related to the the specific problem I am experiencing.
This is the first time I have worked with mouse events and I am surely missing something... Any advice would be greatly appreciated.
Also, I am running WX 2.8.12 and MPL 1.1.1rc.
from matplotlib.figure import Figure
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
class NewVLineListener:
global castLog, count, dvls, ax1
lock = None # only one can be animated at a time
def __init__(self, fig):
self.fig = fig
def connect(self):
'connect to all the events we need'
self.cidpressright = self.fig.canvas.mpl_connect(
'button_press_event', self.on_press_right)
def on_press_right(self, event):
global count, castLog, dvls, ax1
print event.button
if event.button != 3: return
tag = 'Begin'
# Increase castLog Key to accomodate new Vline
count += 1
print 'count: ', count
# Set castLog values to x-value of triggering event
castLog[str(count)] = { tag:event.xdata }
# Spawn a new DraggableVline instance
color_map = {'Begin':'g', 'End':'r'}
dvl = DraggableVline(self.fig, ax1.axvline(x=castLog[str(count)][tag], linewidth=2, color=color_map[tag]), count, tag)
dvl.connect()
dvls.append(dvl)
canvas = self.fig.canvas
canvas.draw()
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY)
global castLog, count, ax1, dvls
fig = Figure()
canvas = FigureCanvasWxAgg(self, -1, fig)
ax1 = fig.add_subplot(111)
# Create empty dict that will hold new V-line Data
castLog = { }
dvls = [ ]
count = 0
# Instantiate New Vline Listener
NVL = NewVLineListener(fig)
NVL.connect()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = MainFrame()
app.frame.Show()
app.MainLoop()
I got it!
I think the reference to the NewVlineListener object was being garbage collected so the events were never being received. By adding the NVL object reference to the array of references to draggable vline objects, it sticks around and receives the events as expected.
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY)
global castLog, count, ax1, dvls
fig = Figure()
canvas = FigureCanvasWxAgg(self, -1, fig)
ax1 = fig.add_subplot(111)
# Create empty dict that will hold new V-line Data
castLog = { }
dvls = [ ]
count = 0
# Instantiate New Vline Listener
NVL = NewVLineListener(fig)
NVL.connect()
dvls.append(NVL) # This keeps NVL reference from being garbage collected?
I still find it interesting that naming the NewVlineListener object is not enough to keep the reference around, and that it works fine with pyplot but now with wx, however this seems to work.