I am dynamically generating a figure as part of a website. The figure is generated by matplotlib as part of a Django view function. Within the view function, I render the figure as an SVG and then add that SVG's content to the view's context and then render a very simple template using that context. It's a graph of temperature over time from a particular sensor.
When I view the page in a browser, everything works right, in general terms: the graph is shown in the right place and generally shows the right info. But the image looks different when viewed in Safari vs either Chrome or Firefox.
Here's how it looks in Chrome/Firefox:
And here's how it looks in Safari:
A link to the SVG itself is here.
Here's the code that is generating the SVG:
from django.views.generic import TemplateView
from mysite.models import TempReading, TempSeries
import numpy as np
import pandas as pd
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import seaborn as sbn
import StringIO
class TestView(TemplateView):
template_name = 'mysite/test.html'
def get_context_data(self, **kwargs):
upstairs = TempSeries.objects.get(name='Upstairs')
upstairstemps = upstairs.tempreading_set.all().order_by('-timestamp')
frame = pd.DataFrame(list(upstairstemps.values()))
frame.set_index('timestamp', inplace=True)
fig = Figure()
ax = fig.add_subplot(1,1,1)
frame['value'].plot(ax=ax)
ax.get_xaxis().grid(color='w', linewidth=1)
ax.get_yaxis().grid(color='w', linewidth=1)
fig.set(facecolor='w')
canvas = FigureCanvas(fig)
imgdata = StringIO.StringIO()
canvas.print_svg(imgdata)
imgstr = imgdata.getvalue()
context = super(TestView, self).get_context_data(**kwargs)
context['svgtext'] = imgstr
return context
Any thoughts on why the SVG would render differently in different browsers?
Related
I have created a object model as below
from django.db import models
# Create your models here.
class ImageModel(models.Model):
image = models.ImageField(upload_to='images/')
editedImg = models.ImageField(upload_to='images/')
def delete(self, *args, **kwargs):
self.image.delete()
self.editedImg.delete()
super().delete(*args, **kwargs)
And here is what i am trying to do in a function
from django.shortcuts import render
from EditorApp.forms import ImageForm
from EditorApp.models import ImageModel
from django.http import HttpResponseRedirect
from PIL import Image
def edit_column(request):
codArr = request.POST.getlist('codArr[]')
imgs = ImageModel.objects.first()
orgImage = ImageModel.objects.first().image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
# croppedImg.show()
# imgs.editedImg = croppedImg
# imgs.save()
return HttpResponseRedirect("/editing/")
What i am trying to do is the codArr consists of coordinates of top(x, y) and bottom(x, y) in the array form(Which is not an issue and is tested(croppedImg.show() showed the desired cropped image) and handled and used to crop the image). Image crop is working fine. But what i am trying to do is to save the cropped image in editedImg of the model used above. The above commented one is what i tried but throw a error AttributeError: _committed
As i have not used any name for image in model as its not required.
Kindly help please, Would be very thankfull.
you should do it like this:
from io import BytesIO
from api.models import ProductPicture
from django.core import files
codArr = request.POST.getlist('codArr[]')
img_obj = ImageModel.objects.first()
orgImage = img_obj.image
orgImage = Image.open(orgImage)
croppedImg = orgImage.crop((int(codArr[0]), int(codArr[1]), int(codArr[2]), int(codArr[3])))
thumb_io = BytesIO() # create a BytesIO object
croppedImg.save(thumb_io, 'png')
editedImg = files.File(thumb_io, name=file_name)
img_obj.editedImg = editedImg
img_obj.save()
You can use Python's context manager to open the image and save it to the desired storage in that case I'm using the images dir.
Pillow will crop the image and image.save() will save it to the filesystem and after that, you can add it to Django's ImageField and save it into the DB.
The context manager takes care of the file opening and closing, Pillow
takes care of the image, and Django takes care of the DB.
from PIL import Image
with Image.open(orgImage) as image:
file_name = image.filename # Can be replaced by orgImage filename
cropped_path = f"images/croped-{file_name}"
# The crop method from the Image module takes four coordinates as input.
# The right can also be represented as (left+width)
# and lower can be represented as (upper+height).
(left, upper, right, lower) = (20, 20, 100, 100)
# Here the image "image" is cropped and assigned to new variable im_crop
im_crop = image.crop((left, upper, right, lower))
im_crop.save(cropped_path)
imgs.editedImg = cropped_path
imgs.save()
Pillow's reference
I have a seaborn barplot image that I would like to display inside a django template.
I have looked at a few solutions and I managed to display the image with HttpResponse. However, the image it displays is just an image. What I want is an image of the plot on a webpage and you can still click the navbar and home button etc.
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg
from io import BytesIO
from django.template.loader import render_to_string
In views.py
def top_causal(request):
# code for data manipulation
f = plt.figure(figsize=(10, 5))
ax = sns.barplot(data)
# labels, title etc...
FigureCanvasAgg(f)
buf = BytesIO()
plt.savefig(buf, format='png)
plt.close(f)
# this works
response = HttpResponse(buf.getvalue(), content_type='image/png')
return response
# What I want which is to display the image inside a template(does not work)
buffer = buf.getvalue()
content_type="image/png"
graphic = (buffer, content_type)
rendered = render_to_string('top_causal.html', {'graphic': graphic})
return HttpResponse(rendered)
in top_causal.html
<img src="data:image/png;base64,{{ graphic|safe }}" alt="Not working.">
HttpResponse displays the image as a page.
The response using render_to_string returns this instead of an image inside the webpage. I can still see the navbar.
\x06\x1d\x00\x80kT\xb7n]eee\x95\x18\xff\xe1\x87\x1f$I\xf5\xea\xd5\xb3\x19\xcf\xce\xce.\x11\x9b\x9d\x9d-WWW999U\xb8~\xa7N\x9d$\xfd\xf6\x02\xb2\xb0\xb00IR\x9b6mt\xe4\xc8\x91\x12\xb1G\x8e\x1c\x91\xc5b\xb9\xea\xe7\xe1\xda\xb5k\xa7\xf7\xdf\x7f_\xc5\xc5\xc5:|\xf8\xb0V\xadZ\xa5\xe7\x9f\x7f^\xb5j\xd5\xd2\xd4\xa9S\xafx\xee\xdbo\xbf\xad\x81\x03\x07j\xc9\x92%6\xe3\xb9\xb9\xb9\xe5\xba\x9e\xbau\xeb\xcab\xb1h\xc2\x84\t\x9a0aB\xa91M\x9b6-W\xae\xab\xa9W\xaf\x9e\xaaU\xab\xa6\xbd{\xf7\xaaj\xd5\xaa%\xe6]\\\\\xecR\xe7\xb2;\xef\xbcSqqq\x92~{Q\xde\xbb\xef\xbe\xaby\xf3\xe6\xa9\xb8\xb8X\x8b\x16-\xb2k-\x00\x80\xe3b\x07\x1d\x00\x80k\xd4\xb7o_\xa5\xa4\xa4\x94x\x13w||\xbc\xaaT\xa9\xa2\x90\x90\x10\x9b\xf1\xf7\xdf\x7f\xdfz\x8b\xb5$\x9d;wN[\xb7n-\x11W^\x97o\x8do\xd6\xac\x99ul\xc8\x90!JIIQJJ\x8au\xec\xd7 ...
You need to convert the image to byte array, encode it to base64 and embed it to your template. Here is how you do it:
<!DOCTYPE html>
<!--my_template.html-->
<html>
<div class='section'>
<div class='flex column plot mt15 mlr15 justify-left align-left'>
<img src='{{ imuri }}'/>
</div>
</div>
</html>
from django.template.loader import get_template
def top_causal(request):
# .......................
# Get your plot ready ...
# .......................
figure = plt.gcf()
buf = io.BytesIO()
figure.savefig(buf, format='png', transparent=True, quality=100, dpi=200)
buf.seek(0)
imsrc = base64.b64encode(buf.read())
imuri = 'data:image/png;base64,{}'.format(urllib.parse.quote(imsrc))
context = { 'plot': imuri}
# Now embed that to your template
template = get_template('my_template.html')
html = template.render(context=context)
# Your template is ready to go...
I discarded the idea of using matplotlib and went to use Highcharts https://www.highcharts.com/ instead to plot my graph and display it on the webpage.
The data displayed was done by using Django's ORM and Query API.
I don't understand what I'm doing wrong.
I'm trying to create a log in window. When you click Log in, I want to to go do stuff and after that close out and go to the main GUI.
I found a bunch of stuff online that I can't make heads or tails of. I do not want to cut and paste other people's code which I do not fully understand.
So I'd like to understand what I'm doing wrong here. To start I created a destroyWindow() method which I call from the button as just a starting point.
There is a scope issue where loginWindow does not exist within its own class. I thought the class application would get around the scope issue. I tried using 'self.' but to no avail. I tried random things I saw in other people's code. Please can someone pinpoint what I'm missing here? I get
NameError: global name 'loginWindow' is not defined
#!/Usr/bin/Python
import Tkinter,tkFileDialog,tkMessageBox
from Tkinter import *
from tkFileDialog import *
import sys, time, datetime
import pathlib
from pathlib import * #makes it really easy to travers folders if needed.
mainWindow = Tk()
mainWindow.wm_title("my prog")
mainWindow.wm_iconbitmap('fb_logo_sm.ico')
mainFrame = Frame(mainWindow)
mainFrame.grid(padx=10,pady=10)
class loginWindowClass():
def __init__(self):
loginWindow = Tkinter.Toplevel()
loginWindow.configure(bg='#22BEF2')
loginWindowFrame = Frame(loginWindow,bg='#22BEF2')
loginWindowFrame.grid(padx=90,pady=50)
loginWindow.wm_title("log in")
loginWindow.wm_iconbitmap('my.ico')
usernameLable = Tkinter.Label(loginWindowFrame,text="User Name",fg='#FFFFFF',bg='#22BEF2')
usernameLable.grid(row=1,column=2,padx=(0,5),sticky=W+S)
usernameField = Entry(loginWindowFrame)
usernameField.config(width=24)
usernameField.grid(row=2,column=2)
passwordLable =Tkinter.Label(loginWindowFrame,text="Password",fg='#FFFFFF',bg='#22BEF2')
passwordLable.grid(row=3,column=2,sticky=W+S)
passwordField = Entry(loginWindowFrame)
passwordField.config(width=24)
passwordField.grid(row=4,column=2)
loginButton = Button(loginWindowFrame, text='Log In', height=1, width=20, wraplength=100, fg='white',bg='#bbbbbb',command=self.destroyWindow).grid(row=5,column=2,pady=(10,0))
def destroyWindow(self):
loginWindow.destroy()
logwin = loginWindowClass()
mainWindow.mainloop()
Ah. 'self.' is the answer indeed.
I did not apply it everywhere. Here is the solution
#!/Usr/bin/Python
import Tkinter,tkFileDialog,tkMessageBox
from Tkinter import *
from tkFileDialog import *
import sys, time, datetime
import pathlib
from pathlib import * #makes it really easy to travers folders if needed.
mainWindow = Tk()
mainWindow.wm_title("my prog")
mainWindow.wm_iconbitmap('fb_logo_sm.ico')
mainFrame = Frame(mainWindow)
mainFrame.grid(padx=10,pady=10)
class loginWindowClass():
def __init__(self):
self.loginWindow = Tkinter.Toplevel()
self.loginWindow.configure(bg='#22BEF2')
loginWindowFrame = Frame(self.loginWindow,bg='#22BEF2')
loginWindowFrame.grid(padx=90,pady=50)
self.loginWindow.wm_title("log in")
self.loginWindow.wm_iconbitmap('my.ico')
usernameLable = Tkinter.Label(loginWindowFrame,text="User Name",fg='#FFFFFF',bg='#22BEF2')
usernameLable.grid(row=1,column=2,padx=(0,5),sticky=W+S)
usernameField = Entry(loginWindowFrame)
usernameField.config(width=24)
usernameField.grid(row=2,column=2)
passwordLable =Tkinter.Label(loginWindowFrame,text="Password",fg='#FFFFFF',bg='#22BEF2')
passwordLable.grid(row=3,column=2,sticky=W+S)
passwordField = Entry(loginWindowFrame)
passwordField.config(width=24)
passwordField.grid(row=4,column=2)
loginButton = Button(loginWindowFrame, text='Log In', height=1, width=20, wraplength=100, fg='white',bg='#bbbbbb',command=self.destroyWindow).grid(row=5,column=2,pady=(10,0))
def destroyWindow(self):
self.loginWindow.destroy()
logwin = loginWindowClass()
mainWindow.mainloop()
I visited tons of pages in the internet but only have examples in C and I dont understand how to make this in python. Can someone helpme.
I use Pyqt designer and Python 2.7. I need plot data whith dates on a GUI using qwtplot.
you need a QwtScaleDraw class e.g.:
class TimeScaleDraw(QwtScaleDraw):
def __init__(self, baseTime, *args):
QwtScaleDraw.__init__(self, *args)
self.baseTime = baseTime
def label(self, value):
upTime = self.baseTime.addSecs(int(value))
return QwtText(upTime.toString())
then you modify your x axis with this code:
self.setAxisScaleDraw(
QwtPlot.xBottom, TimeScaleDraw(self.cpuStat.upTime()))
where self's base class is QwtPlot. cpuStat.upTime() returns a QTime instance. value represents one of your time data points. This code is from the cpuDemo example code.
To get this example cpuDemo working with python xy 2.7. make these changes to the imported modules:
import os
import sys
import numpy as np
from PyQt4.QtGui import (QApplication, QColor, QBrush, QWidget, QVBoxLayout,
QLabel)
from PyQt4.QtCore import QRect, QTime
from PyQt4.QtCore import Qt
from PyQt4.Qwt5 import (QwtPlot, QwtPlotMarker, QwtScaleDraw, QwtLegend, QwtPlotCurve,
QwtPlotItem, QwtText)#, QwtLegendData
then comment out all legend lines.
I adopted this code to use the QDateTime class. Here is a snippet:
from PyQt4 import QtCore, QtGui, Qt
from PyQt4 import Qwt5
class TimeScaleDraw(Qwt5.QwtScaleDraw):
def __init__(self, *args):
Qwt5.QwtScaleDraw.__init__(self,unixBaseTime *args)
self.basetime=unixBaseTime
self.fmt='h:mm\nd-MMM-yyyy'
def label(self, value):
dt = QtCore.QDateTime.fromMSecsSinceEpoch((value+self.basetime))
return Qwt5.QwtText(dt.toString(self.fmt))
...
class Ui_TabWidget(object):
def setupUi(self, TabWidget):
self.qwtPlot = Qwt5.QwtPlot()
self.qwtPlot.setAxisScaleDraw(Qwt5.QwtPlot.xBottom, TimeScaleDraw(unixBaseTime))
admire pretty picture
Hi all reportlab master,
I've search the web and also here in stackoverflow but can't find a similar situation on my problem I'm trying to solve during this Holiday vacation.
In django admin, I'm trying to create an action to view my database to a specific format. If I select one record I can view the report in one page pdf. Which is fine. In case the user try to more record that's where the problem start. For example I select multiple record, I can view the report but all content is still in one page pdf.
Is there a way to show a record per page in pdf? All reportlab master jedi, Please help me how to do this the right way.
Here's my code on what I did.
from django.contrib import admin
from models import LatestRsl
from io import BytesIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from reportlab.lib.units import inch
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import BaseDocTemplate, PageTemplate, Paragraph, Frame
from reportlab.lib.pagesizes import letter
def go(modeladmin, request, queryset):
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'filename = testframe.pdf'
buffer = StringIO()
c = canvas.Canvas(buffer)
doc = BaseDocTemplate(buffer, showBoundary=1, leftMargin= 0.1*inch, rightMargin= 0.1*inch,
topMargin= 0.1*inch, bottomMargin= 0.1*inch)
signfr = Frame(5.1*inch, 1.2*inch, 2.8*inch, 0.44*inch, showBoundary=1)
modelfr = Frame(3.6*inch, 4.6*inch, 2.8*inch, 0.44*inch, showBoundary=1)
doc.addPageTemplates([PageTemplate(id= 'rsl_frame', frames=[signfr, modelfr]),
PageTemplate(id= 'rsl_frame2', frames=[signfr, modelfr])])
story = []
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='Verdana9', fontName= 'Verdana', fontSize= 9))
styles.add(ParagraphStyle(name='VerdanaB10', fontName= 'VerdanaB', fontSize= 10))
for obj in queryset:
#1st frame
model = Paragraph(obj.make,styles["Verdana9"])
story.append(model)
modelfr.addFromList(story,c)
#2nd frame
signatory = Paragraph(obj.signatory,styles["VerdanaB10"])
story.append(signatory)
signfr.addFromList(story,c)
doc.build(story)
c.showPage()
c.save()
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
Assuming your queryset variable contains all the records you need, you could insert a PageBreak object. Just add from reportlab.platypus import PageBreak to the top of your file, then append a PageBreak object to your document's elements.
If you want to change the template for each page, you can also append a NextPageTemplate and pass the id of your PageTemplate. You'll need to add from reportlab.platypus import NextPageTemplate to the top of your file as well.
for obj in queryset:
#1st frame
model = Paragraph(obj.make,styles["Verdana9"])
story.append(model)
modelfr.addFromList(story,c)
#2nd frame
signatory = Paragraph(obj.signatory,styles["VerdanaB10"])
story.append(signatory)
signfr.addFromList(story,c)
# Force the report to use a different PageTemplate on the next page
story.append(NextPageTemplate('rsl_frame2'))
# Start a new page for the next object in the query
story.append(PageBreak())
You could move the PageBreak wherever you need it, but it's a simple "function" flowable. NextPageTemplate can take the id of any valid PageTemplate object that you've added via addPageTemplates.