The documentation for use of the python wrappers for Gtk3 are somewhat limited. I have found several of the common widget examples. I am trying to use the Gtk.GLArea widget. The API documentation is for C and I have not had much luck guessing the equivalent python calls to use this widget. In the example the widget is created using the following C code:
// create a GtkGLArea instance
GtkWidget *gl_area = gtk_gl_area_new ();
g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
Then the render function has OpenGL commands in it:
static gboolean
render (GtkGLArea *area, GdkGLContext *context)
{
// inside this function it's safe to use GL; the given
// #GdkGLContext has been made current to the drawable
// surface used by the #GtkGLArea and the viewport has
// already been set to be the size of the allocation
// we can start by clearing the buffer
glClearColor (0, 0, 0, 0);
glClear (GL_COLOR_BUFFER_BIT);
// draw your object
draw_an_object ();
// we completed our drawing; the draw commands will be
// flushed at the end of the signal emission chain, and
// the buffers will be drawn on the window
return TRUE;
}
My question is how do you do the equivalent in python?
This is my attempt:
class RootWidget(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='GL Example')
self.set_default_size(800,500)
gl = Gtk.GLArea()
gl.connect("render", self.gl_render)
self.add(gl)
def gl_render(self, gl):
print(gl)
Gtk.glClearColor(0,0,0,1)
Gtk.glClear(Gtk.GL_COLOR_BUFFER_BIT)
return True
You'll notice I added "Gtk." to the gl commands. Not sure if this is correct but python has no idea what a glClearColor is in the function. I'm not super familiar with C namespaces but I can't figure out how the C function would understand what the gl commands are, either. The program does run and I receive the following errors in the console:
fb setup not supported
(python.exe:120048): Gdk-WARNING **: Compile failure in fragment
shader:
ERROR: 0:5: 'gl_FragColor' : undeclared identifier
ERROR: 0:5: 'texture2D' : no matching overloaded function found (using
implicit conversion)
Any input on this would be useful. My hope is to be able to use opengl commands to draw in a fixed widget area.
edit:
This is my latest attempt. In the on_render() function I print the context and I do see the context property is set to a <gtk.gdk.Win32GLContext object at 0x3f133f0> and debugging does show that that is the current context. Problem is I still get the same shader, Frag_Color, and texture errors and I'm not even calling any gl commands.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from OpenGL.GL import *
from OpenGL.GLU import *
class MyGLArea(Gtk.GLArea):
def __init__(self):
Gtk.GLArea.__init__(self)
self.connect("realize", self.on_realize)
def on_realize(self, area):
ctx = self.get_context()
ctx.make_current()
print("The context is {}".format(self.get_property("context")))
err = self.get_error()
if err:
print(err)
return
class RootWidget(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='GL Example')
self.set_default_size(800,500)
gl_area = MyGLArea()
self.add(gl_area)
win = RootWidget()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
edit 2:
Seems as if I am just having a installation issue with the GLArea widget. Seems odd since I'm using over 30 different Gtk widgets without issue. I have errors just adding the widget and not even sending commands. I ran code in Ubuntu python interpreter and it doesn't have any error creating the widget. I am having some issues with certain GL commands but may be from the python wrapper I installed. I'm still looking for the widget equivalent to pygame's or glut's init and set projection functions to setup the viewport. If anyone got ideas I'd love to hear them. The following code does run without errors:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from OpenGL.GL import *
from OpenGL.GLU import *
class MyGLArea(Gtk.GLArea):
def __init__(self):
Gtk.GLArea.__init__(self)
self.connect("realize", self.on_realize)
self.connect("render", self.render)
def on_realize(self, area):
ctx = self.get_context()
ctx.make_current()
err = self.get_error()
if err:
print("The error is {}".format(err))
def render(self, area, ctx):
glClearColor(1,0,0,1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
return True
class RootWidget(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='GL Example')
self.set_default_size(800,500)
gl_area = MyGLArea()
self.add(gl_area)
win = RootWidget()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Using the All-In-One installer for windows I noticed there is an option for OpenGL Extentions to GTK+. Now it works as expected.
Here's some working code:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from OpenGL.GL import *
from OpenGL.GL import shaders
import numpy as np
FRAGMENT_SOURCE ='''
#version 330
in vec4 inputColor;
out vec4 outputColor;
void main(){
outputColor = vec4(1.0,0.0,0.0,1.0);//constant red. I know it's a poor shader
};'''
VERTEX_SOURCE = '''
#version 330
in vec4 position;
void main(){
gl_Position = position;
}'''
class MyGLArea(Gtk.GLArea):
def __init__(self):
Gtk.GLArea.__init__(self)
self.connect("realize", self.on_realize)
self.connect("render", self.on_render)
def on_realize(self, area):
ctx = self.get_context()
print("realized", ctx)
def on_render(self, area, ctx):
ctx.make_current()
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
VERTEX_SHADER_PROG = shaders.compileShader(VERTEX_SOURCE, GL_VERTEX_SHADER)
FRAGMENT_SHADER_PROG = shaders.compileShader(FRAGMENT_SOURCE, GL_FRAGMENT_SHADER)
self.shader_prog = shaders.compileProgram(VERTEX_SHADER_PROG, FRAGMENT_SHADER_PROG)
self.create_object()
def create_object(self):
# Create a new VAO (Vertex Array Object) and bind it
vertex_array_object = glGenVertexArrays(1)
glBindVertexArray(vertex_array_object)
# Generate buffers to hold our vertices
vertex_buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer)
# Get the position of the 'position' in parameter of our shader and bind it.
position = glGetAttribLocation(self.shader_prog, 'position')
glEnableVertexAttribArray(position)
# Describe the position data layout in the buffer
glVertexAttribPointer(position, 3, GL_FLOAT, False, 0, ctypes.c_void_p(0))
# Send the data over to the buffer
vertices = np.array([-0.6, -0.6, 0.0,
0.0, 0.6, 0.0,
0.6, -0.6, 0.0,
0.7, -0.1, 0.0,
0.8, 0.1, 0.0,
0.9, -0.1, 0.0
], dtype=np.float32)
glBufferData(GL_ARRAY_BUFFER, 96, vertices, GL_STATIC_DRAW)
# Unbind the VAO first (Important)
glBindVertexArray(0)
# Unbind other stuff
glDisableVertexAttribArray(position)
glBindBuffer(GL_ARRAY_BUFFER, 0)
self.display(vertex_array_object)
def display(self, vert):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader_prog)
glBindVertexArray(vert)
glDrawArrays(GL_TRIANGLES, 0, 3)
glDrawArrays(GL_TRIANGLES, 4, 3)
glBindVertexArray(0)
glUseProgram(0)
class RootWidget(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title='GL Example')
self.set_default_size(800, 500)
gl_area = MyGLArea()
gl_area.set_has_depth_buffer(False)
gl_area.set_has_stencil_buffer(False)
self.add(gl_area)
win = RootWidget()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
I am not sure if this works for you, but it did work for me.
The Python file:
#!/usr/bin/env python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf, Gdk
from OpenGL.GL import glClearColor, glClear, glFlush, GL_COLOR_BUFFER_BIT
import os, sys
UI_FILE = "pygtk_gtkglarea.ui"
class GUI:
def __init__(self):
self.builder = Gtk.Builder()
self.builder.add_from_file(UI_FILE)
self.builder.connect_signals(self)
gl_area = Gtk.GLArea()
gl_area.connect('render', self.area_render)
gl_area.connect('realize', self.area_realize)
#gl_area.connect('create-context', self.area_context)
box = self.builder.get_object('box1')
box.pack_end(gl_area, True, True, 0)
window = self.builder.get_object('window')
window.show_all()
def area_realize (self, gl_area):
error = gl_area.get_error()
if error != None:
print("your graphics card is probably too old : ", error)
else:
print(gl_area, "realize... fine so far")
def area_context(self, gl_area):
# not needed except for special instances, read the docs
c = gl_area.get_context()
print(c , "context")
return c
def area_render(self, area, context):
#print gl_area
#print gl_context
glClearColor (0.5, 0.5, 0.5, 1.0)
glClear (GL_COLOR_BUFFER_BIT)
glFlush()
print("rendering... done")
return True
def on_window_destroy(self, window):
Gtk.main_quit()
def main():
app = GUI()
Gtk.main()
if __name__ == "__main__":
sys.exit(main())
And the pygtk_gtkglarea.ui file:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkWindow" id="window">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">window</property>
<property name="default_width">500</property>
<property name="default_height">400</property>
<signal name="destroy" handler="on_window_destroy" swapped="no"/>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</interface>
Related
I created a Panel in a wxFrame and then created a FigureCanvas. I would like to fit the FigureCanvas into the panel completely, but somehow the FigureCanvas does not go into the panel2_2, but exactly on just the opposite side.
Following is my code.
import wx
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class Frame1(wx.Frame):
def __init__(self, prnt):
wx.Frame.__init__(self, parent=prnt,
pos=wx.Point(0, 0), size=wx.Size(1340, 720),
style=wx.DEFAULT_FRAME_STYLE)
self.panel2_2 = wx.Panel(parent=self,
pos=wx.Point(940, 30), size=wx.Size(400, 690),
style=wx.TAB_TRAVERSAL)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self.panel2_2, -1, self.figure)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.canvas, 0, wx.EXPAND)
self.panel2_2.SetSizer(sizer)
self.panel2_2.Fit()
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
#Every wxWidgets application must have a class derived from wxApp
class MyApp(wx.App):
# wxWidgets calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
self.frame = Frame1(None)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == '__main__':
application = MyApp(0)
application.MainLoop()
and the result
I would like to have the image on the panel2_2 ( that is on the right hand side ) and not on the left hand side
I think the canvas is indeed going to panel2_2. The problem in your MWE is that you do not define a sizer for panel2_2. Therefore, panel2_2 is rendered from the upper left corner of the frame. This results in panel2_2 plus the canvas showing in the left of the frame. What you see to the right of the canvas is not panel2_2 but the rest of the frame since the size of the frame is larger than the size of panel2_2. If you add a blue panel1_1 and assign another wx.BoxSizer to the frame you get the canvas showing to the right.
import wx
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class Frame1(wx.Frame):
def __init__(self, prnt):
wx.Frame.__init__(self, parent=prnt,
pos=wx.Point(0, 0), size=wx.Size(1340, 720),
style=wx.DEFAULT_FRAME_STYLE)
self.panel1_1 = wx.Panel(parent=self, size=(400, 690))
self.panel1_1.SetBackgroundColour('blue')
self.panel2_2 = wx.Panel(parent=self,
pos=wx.Point(940, 30), size=wx.Size(400, 690),
style=wx.TAB_TRAVERSAL)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self.panel2_2, -1, self.figure)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.canvas, 0, wx.EXPAND)
self.panel2_2.SetSizer(sizer)
self.panel2_2.Fit()
sizerPanels = wx.BoxSizer(wx.HORIZONTAL)
sizerPanels.Add(self.panel1_1)
sizerPanels.Add(self.panel2_2)
sizerPanels.Fit(self)
self.SetSizer(sizerPanels)
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
self.Center()
#Every wxWidgets application must have a class derived from wxApp
class MyApp(wx.App):
# wxWidgets calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
self.frame = Frame1(None)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == '__main__':
application = MyApp(0)
application.MainLoop()
I'm trying to get the mouse coordinate on OpenGL scene.
My Code:
from PySide.QtGui import (QColor)
from PySide.QtCore import (Qt, QSize)
from PySide.QtOpenGL import (QGLWidget)
from OpenGL.GL import *
from OpenGL.GLU import *
class QGL(QGLWidget):
def __init__(self, parent=None):
self._pan_valid = False
super(QGL, self).__init__(parent)
self.setFocusPolicy(Qt.ClickFocus)
self.local_translate = (0.0, 0.0, 0.0)
self.zoomVal = 1.2
def minimumSizeHint(self):
return QSize(50, 50)
def sizeHint(self):
return QSize(800, 800)
def initializeGL(self):
self.qglClearColor(QColor.fromCmykF(0.0, 0.1, 0.0, 0.882))
glViewport( 0, 0, self.width(), self.height())
glEnable(GL_TEXTURE_2D)
glEnable(GL_CULL_FACE)
glEnable(GL_MULTISAMPLE)
glEnable(GL_LINE_SMOOTH, GL_LINE_WIDTH, GL_ALIASED_LINE_WIDTH_RANGE)
glShadeModel(GL_FLAT)
glEnable(GL_DEPTH_TEST)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
glDepthRange (0.1, 1.0)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
gluOrtho2D(-self.zoomVal, +self.zoomVal, -self.zoomVal, +self.zoomVal)
glLoadIdentity()
genList = glGenLists(1)
glNewList(genList, GL_COMPILE)
vertices = [
(0,0,0),
(0.5,0,0),
(0.5,0.5,0),
(0,0.5,0)
]
glBegin(GL_QUADS)
self.qglColor(QColor(0,255,255))
for vertex in vertices:
glVertex3fv(vertex)
glEnd()
glEndList()
glCallList(genList)
def mousePressEvent(self, event):
print event.pos()
print self.mapToGlobal(event.pos())
when I do this:
print event.pos()
it will give me the mouse position on the window.
and when I do this:
print self.mapToGlobal(event.pos())
it will give me the mouse position on the monitor.
How can I get the mouse position on the scene?
I'm using 2D viewport (gluOrtho2D).
I don't think there is a convenient built-in function for this, but it should be trivial to calculate, especially given that this is a 2D, orthographic scene. You know the size of the window in pixels, and you know where in that window the user clicked, given whatever event.pos() returns (also in pixels). What needs to happen then is that you need to map that range in pixels to your range specified in your gluOrtho2D call. The following code could be adapted as you see fit:
#specifying a bunch of example values
zoom_val = 1.2
window_size = (800, 600)
mouse_pos = (300, 150)
ortho_2d = (-zoom_val, +zoom_val, -zoom_val, +zoom_val)
#First, calculate the "normalized" mouse coordinates by dividing by window_size
mouse_norm_x = mouse_pos[0] / window_size[0]
mouse_norm_y = mouse_pos[1] / window_size[1]
#Now map those coordinates to your orthographic projection range
mouse_ortho_x = (mouse_norm_x * (ortho_2d[1] - ortho_2d[0])) + ortho_2d[0]
mouse_ortho_y = (mouse_norm_y * (ortho_2d[3] - ortho_2d[2])) + ortho_2d[2]
mouse_ortho = (mouse_ortho_x, mouse_ortho_y)
print(mouse_ortho)
Getting the z-coordinate is trickier. I would suggest reading up on the concept of "mouse-picking" for that. You would need to get the depth buffer, store its results in a texture, and sample the mouse coordinate at the appropriate location in that texture to get the z-coordinate. You can combine these two answers (1, 2) I have previously written for other questions together to get something working. Let me know if this helps!
I am new to opengl and I have written a simple program to draw a triangle using VBO in opengl.
I see the window appear but I do not see any triangle on the screen.
I do not see any errors either.
Can someone please help identify where exactly I am going wrong.
opengl version: 3.1.0
Code:
import numpy as np
from OpenGL import GL, GLU, GLUT
from OpenGL.arrays import ArrayDatatype
import sys
vertex = """
#version 130
in vec3 vin_position;
in vec3 vin_color;
out vec3 vout_color;
void main(void)
{
vout_color = vin_color;
gl_Position = vec4(vin_position, 1.0);
}
"""
fragment = """
#version 130
in vec3 vout_color;
out vec4 fout_color;
void main(void)
{
fout_color = vec4(vout_color, 1.0);
}
"""
vertex_data = np.array([0.75, 0.75, 0.0,
0.75, -0.75, 0.0,
-0.75, -0.75, 0.0],
dtype=np.float64)
color_data = np.array([1, 0, 0,
0 ,1, 0,
0, 0, 1],
dtype=np.float64)
ESCAPE = '\033'
class ShaderProgram(object):
""" Helper class for using GLSL shader programs
"""
def __init__(self, vertex, fragment):
"""
Parameters
----------
vertex : str
String containing shader source code for the vertex
shader
fragment : str
String containing shader source code for the fragment
shader
"""
self.program_id = GL.glCreateProgram()
vs_id = self.add_shader(vertex, GL.GL_VERTEX_SHADER)
frag_id = self.add_shader(fragment, GL.GL_FRAGMENT_SHADER)
GL.glAttachShader(self.program_id, vs_id)
GL.glAttachShader(self.program_id, frag_id)
GL.glLinkProgram(self.program_id)
if (GL.glGetProgramiv(self.program_id, GL.GL_LINK_STATUS) !=
GL.GL_TRUE):
info = GL.glGetProgramInfoLog(self.program_id)
GL.glDeleteProgram(self.program_id)
GL.glDeleteShader(vs_id)
GL.glDeleteShader(frag_id)
raise RuntimeError('Error linking program: %s' % (info))
GL.glDeleteShader(vs_id)
GL.glDeleteShader(frag_id)
def add_shader(self, source, shader_type):
""" Helper function for compiling a GLSL shader
Parameters
----------
source : str
String containing shader source code
shader_type : valid OpenGL shader type
Type of shader to compile
Returns
-------
value : int
Identifier for shader if compilation is successful
"""
try:
shader_id = GL.glCreateShader(shader_type)
GL.glShaderSource(shader_id, source)
GL.glCompileShader(shader_id)
if GL.glGetShaderiv(shader_id, GL.GL_COMPILE_STATUS) != GL.GL_TRUE:
info = GL.glGetShaderInfoLog(shader_id)
raise RuntimeError('Shader compilation failed: %s' % (info))
return shader_id
except:
GL.glDeleteShader(shader_id)
raise
def uniform_location(self, name):
""" Helper function to get location of an OpenGL uniform variable
Parameters
----------
name : str
Name of the variable for which location is to be returned
Returns
-------
value : int
Integer describing location
"""
return GL.glGetUniformLocation(self.program_id, name)
def attribute_location(self, name):
""" Helper function to get location of an OpenGL attribute variable
Parameters
----------
name : str
Name of the variable for which location is to be returned
Returns
-------
value : int
Integer describing location
"""
return GL.glGetAttribLocation(self.program_id, name)
class Triangle(object):
def __init__(self, window):
self.glut_window = window
self.program = None
self.vao_id = None
self.vbo_id = None
def init(self):
GL.glClearColor(0.95, 1.0, 0.95, 0)
self.program = ShaderProgram(fragment=fragment, vertex=vertex)
self.vao_id = GL.glGenVertexArrays(1)
GL.glBindVertexArray(self.vao_id)
print self.vao_id
self.vbo_id = GL.glGenBuffers(2)
print self.vbo_id
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo_id[0])
GL.glBufferData(GL.GL_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(vertex_data),
vertex_data,
GL.GL_STATIC_DRAW)
GL.glVertexAttribPointer(self.program.attribute_location('vin_position'),
3, GL.GL_FLOAT, GL.GL_FALSE, 0, None)
GL.glEnableVertexAttribArray(0)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo_id[1])
GL.glBufferData(GL.GL_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(color_data),
color_data,
GL.GL_STATIC_DRAW)
GL.glVertexAttribPointer(self.program.attribute_location('vin_color'),
3, GL.GL_FLOAT,
GL.GL_FALSE, 0, None)
GL.glEnableVertexAttribArray(1)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
GL.glBindVertexArray(0)
def display(self):
GL.glClear(GL.GL_COLOR_BUFFER_BIT)
GL.glUseProgram(self.program.program_id)
GL.glBindVertexArray(self.vao_id)
GL.glDrawArrays(GL.GL_TRIANGLES, 0, 3)
GL.glUseProgram(0)
GL.glBindVertexArray(0)
GLUT.glutSwapBuffers()
def resize(self, width, height):
if not height:
height = 1
# Reset the view port and
GL.glViewport(0, 0, width, height)
GL.glMatrixMode(GL.GL_PROJECTION)
GL.glLoadIdentity()
GLU.gluPerspective(45.0, float(width)/float(height), 0.1, 100.0)
GL.glMatrixMode(GL.GL_MODELVIEW)
def keyboard_activity(self, *args):
if args[0] == ESCAPE:
GLUT.glutDestroyWindow(self.glut_window)
sys.exit()
if __name__ =='__main__':
# GLUT init
GLUT.glutInit()
GLUT.glutInitDisplayMode(GLUT.GLUT_DOUBLE | GLUT.GLUT_RGBA)
GLUT.glutInitWindowSize(640, 480)
win = GLUT.glutCreateWindow('Triangle using VBO')
GLUT.glutFullScreen()
triangle_obj = Triangle(win)
GLUT.glutDisplayFunc(triangle_obj.display)
GLUT.glutIdleFunc(triangle_obj.display)
GLUT.glutReshapeFunc(triangle_obj.resize)
GLUT.glutKeyboardFunc(triangle_obj.keyboard_activity)
triangle_obj.init()
GLUT.glutMainLoop()
GL_FLOAT is indicating to GL that you are providing single-precision floats in your VBO, but you're passing double-precision floats (float64). GL_DOUBLE does exist as a valid type, but double-precision floats are generally overkill for the purposes of storing vertex data, and may carry a substantial performance cost. Thus I would suggest converting your VBO data to single-precision floats to bring it in line with your existing attribute configuration, which seems otherwise correct.
I wanted to use the matplotlib slider as seen in an example from a previous question (below) inside a GUI window (such as TkInter etc). But, in a non-global context, the variables for the plot ("spos, fig, ax") are not defined. My understanding is that because update is used as a callback function, one can't or shouldn't pass arguments.
If so, how can a plot be updated without global variables? or
How can I obtain the slider position outside the callback function?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
t = np.arange(0.0, 100.0, 0.1)
s = np.sin(2*np.pi*t)
l, = plt.plot(t,s)
plt.axis([0, 10, -1, 1])
axcolor = 'lightgoldenrodyellow'
axpos = plt.axes([0.2, 0.1, 0.65, 0.03], axisbg=axcolor)
spos = Slider(axpos, 'Pos', 0.1, 90.0)
def update(val):
pos = spos.val
ax.axis([pos,pos+10,-1,1])
fig.canvas.draw_idle()
spos.on_changed(update)
plt.show()
Related:
1) Another related question seems to cover this topic but does not seem to address how the position of the slider is obtained.
2) A similar question was asked and solved with Slider.set_val(). It seems in my case I would need Slider.get_val() instead.
It is possible to pass more arguments to the callback function, for example with functools.partial
def update(data, val):
pos = spos.val
ax.axis([pos,pos+10,-1,1])
fig.canvas.draw_idle()
data['position'] = pos
import functools
data = dict()
spos.on_changed(functools.partial(update, data))
plt.show()
try:
print data['position']
except KeyError:
pass
A class with __call__ method could also be used as a callback.
I currently have a Django project, with a view function, with code like the following (code copied from this post):
from pylab import figure, axes, pie, title
from matplotlib.backends.backend_agg import FigureCanvasAgg
def test_matplotlib(request):
f = figure(1, figsize=(6,6))
ax = axes([0.1, 0.1, 0.8, 0.8])
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15,30,45, 10]
explode=(0, 0.05, 0, 0)
pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True)
title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
canvas = FigureCanvasAgg(f)
response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response
It creates a png from a graph and serves it directly. How can I make the background of the graph transparent in this png?
Untested, but this should work. You don't have to write to an (actual) file, a file-like object should work. This code saves the image into the response.
def test_matplotlib(request):
f = figure(1, figsize=(6,6))
ax = axes([0.1, 0.1, 0.8, 0.8])
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
fracs = [15,30,45, 10]
explode=(0, 0.05, 0, 0)
pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True)
title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5})
#canvas = FigureCanvasAgg(f)
response = HttpResponse(content_type='image/png')
f.savefig(response, transparent=True, format='png')
#canvas.print_png(response)
return response