Django
前回、Google FontsからSILライセンスのフォントをダウンロードする方法を解説しました。
今回はダウンロードしたフォントを使ってワードクラウドの画像を作成できるようアプリの修正を行っていきます。
またその際、一つのフォントではなく、複数のフォントを使用できるようラジオボタンの設置を行います。
ちなみに私が選んだフォントはこちらのフォントです。
しっぽり明朝(Regular)
Noto Sans JP(Regular)
Dela Gothic One
Potta One
はちまるポップ
New Tegomin
そして今回修正するファイルは以下の三つです。
- /webapp/wordcloudapp/forms.py
- /webapp/wordcloudapp/views.py
- /webapp/wordcloudapp/templates/wordcloudapp/index.html
webapp
├── ...
└── wordcloudapp
├── __init__.py
├── admin.py
├── apps.py
├── forms.py <-これ
├── migrations
│ └── __init__.py
├── models.py
├── static
│ └── wordcloudapp
│ └── wordcloud.png
├── templates
│ └── wordcloudapp
│ └── index.html <-これ
├── tests.py
├── urls.py
└── views.py <-これ
それでは始めていきましょう。
フォントファイルの設置
まずはダウンロードしたフォントファイルをアプリのフォルダ内に設置していきます。
フォントファイルは静的ファイルなので、staticフォルダ内に設置しました。
webapp
├── ...
└── wordcloudapp
├── __init__.py
├── apps.py
├── forms.py
├── migrations
│ └── __init__.py
├── models.py
├── static
│ └── wordcloudapp
│ ├── fonts
│ │ ├── Dela_Gothic_One
│ │ │ ├── DelaGothicOne-Regular.ttf
│ │ │ └── OFL.txt
│ │ ├── Hachi_Maru_Pop
│ │ │ ├── HachiMaruPop-Regular.ttf
│ │ │ └── OFL.txt
│ │ ├── New_Tegomin
│ │ │ ├── NewTegomin-Regular.ttf
│ │ │ └── OFL.txt
│ │ ├── Noto_Sans_JP
│ │ │ ├── NotoSansJP-Regular.otf
│ │ │ └── OFL.txt
│ │ ├── Potta_One
│ │ │ ├── OFL.txt
│ │ │ └── PottaOne-Regular.ttf
│ │ └── Shippori_Mincho
│ │ ├── OFL.txt
│ │ └── ShipporiMincho-Regular.ttf
│ └── wordcloud.png
├── templates
│ └── wordcloudapp
│ └── index.html
├── tests.py
├── urls.py
└── views.py
このように/webapp/wordcloudapp/static/wordcloudapp内に「fonts」フォルダを作成し、それぞれのフォントをフォルダで分けて保存しました。
今回、再配布するわけではないので、ライセンスファイルを残しておく必要はないのですが、後々どんなフォントか分からなくなり、下手に使ってしまうのを避けるため、ライセンスファイルを残しておきました。
そのため、それぞれのフォントでフォルダを分けたということです。
また「しっぽり明朝」と「Noto Sans JP」に関しては複数のスタイルがあったのですが、今回使用するRegularのファイルだけにしました。
/webapp/wordcloudapp/forms.py
それではforms.pyの修正に移ります。
今回はラジオボタンを設置ということで、これまでにも何度かしてきているので、詳しい解説は省きます。
最終的にはこんな感じです。
from django import forms
from django.forms.widgets import CheckboxInput
bgcolor_list = [('White','白'),('Black','黒')]
textcolor_list = [('bwr','青-白-赤'),('hot','暖色系'),('cool','寒色系'),('Reds','赤'),('Blues','青'),('Greens','緑'),('Greys','白-黒'),('rainbow','虹色')]
pic_size_list = [(100,'100'),(200,'200'),(300,'300'),(400,'400'),(500,'500')]
mkline_color_list = [('Black','黒'),('Grey','灰色'),('White','白'),('Red','赤'),('Blue','青'),('Green','緑')]
font_list = [('ShipporiMincho','しっぽり明朝'),('NotoSans','Noto Sans'),('DelaGothicOne','Dela Gothic One'),('PottaOne','Potta One'),('HachiMaruPop','はちまるポップ'),('NewTegomin','New Tegomin')]
class WordCloudForm(forms.Form):
text = forms.FileField(label='テキストファイル')
bgcolor_choice = forms.ChoiceField(widget=forms.RadioSelect, choices=bgcolor_list, initial='White')
textcolor_choice = forms.ChoiceField(widget=forms.RadioSelect, choices=textcolor_list, initial='bwr')
pic_size_height = forms.ChoiceField(widget=forms.Select, choices=pic_size_list, initial=300)
pic_size_width = forms.ChoiceField(widget=forms.Select, choices=pic_size_list, initial=400)
pic_transparence = forms.BooleanField(widget=CheckboxInput, required=False)
min_word = forms.IntegerField(initial=0, min_value=0)
mask_file = forms.ImageField(required=False)
mask_line_width = forms.IntegerField(initial=0, min_value=0)
mask_line_color = forms.ChoiceField(widget=forms.RadioSelect, choices=mkline_color_list, initial='White')
fonts_choice = forms.ChoiceField(widget=forms.RadioSelect, choices=font_list, initial='ShipporiMincho')
追加した部分は選択肢のリストとフィールドです。
font_list = [('ShipporiMincho','しっぽり明朝'),('NotoSans','Noto Sans'),('DelaGothicOne','Dela Gothic One'),('PottaOne','Potta One'),('HachiMaruPop','はちまるポップ'),('NewTegomin','New Tegomin')]
fonts_choice = forms.ChoiceField(widget=forms.RadioSelect, choices=font_list, initial='ShipporiMincho')
詳細はこちらの記事で解説していますので、よかったらどうぞ。
/webapp/wordcloudapp/views.py
次にviews.pyを修正していきます。
修正後はこんな感じです。
from django.shortcuts import render
from django.http import HttpResponse
from .forms import WordCloudForm
import sys
sys.path.append('../')
from webapp import definitions
from janome.tokenizer import Tokenizer
from wordcloud import WordCloud
from PIL import Image
import numpy as np
def wordcloudmake(text, bgcolor, textcolor, height, width, transparence, min_word, mask_file, mask_line_width, mask_line_color, char_fonts):
tk = Tokenizer(wakati=True)
tokens = tk.tokenize(text)
words = ' '.join(list(tokens))
if transparence == 'on':
bgcolor = None
mode = 'RGBA'
elif transparence == 'off':
bgcolor = bgcolor
mode = 'RGB'
if mask_file == None:
wordcloud = WordCloud(width=width, height=height, mode=mode, background_color=bgcolor, colormap=textcolor, min_word_length=min_word, font_path=char_fonts).generate(words)
else:
img = Image.open(mask_file)
mask_img = np.array(img)
wordcloud = WordCloud(width=width, height=height, mask=mask_img, contour_width=mask_line_width, contour_color=mask_line_color, mode=mode, background_color=bgcolor, colormap=textcolor, min_word_length=min_word, font_path=char_fonts).generate(words)
wordcloud.to_file('./wordcloudapp/static/wordcloudapp/wordcloud.png')
def index(request):
params = definitions.readjson()
initial_val = {
'text':'明日天気になぁれ',
'bgcolor':'White',
'textcolor':'bwr',
'height':300,
'width':400,
'transparence':'off',
'min_word':0,
'mask_file':None,
'mask_line_width':0,
'mask_line_color':'White'
}
ShipporiMincho = r'./wordcloudapp/static/wordcloudapp/fonts/Shippori_Mincho/ShipporiMincho-Regular.ttf'
NotoSans = r'./wordcloudapp/static/wordcloudapp/fonts/Noto_Sans_JP/NotoSansJP-Regular.otf'
DelaGothicOne = r'./wordcloudapp/static/wordcloudapp/fonts/Dela_Gothic_One/DelaGothicOne-Regular.ttf'
PottaOne = r'./wordcloudapp/static/wordcloudapp/fonts/Potta_One/PottaOne-Regular.ttf'
HachiMaruPop = r'./wordcloudapp/static/wordcloudapp/fonts/Hachi_Maru_Pop/HachiMaruPop-Regular.ttf'
NewTegomin = r'./wordcloudapp/static/wordcloudapp/fonts/New_Tegomin/NewTegomin-Regular.ttf'
wordcloudmake(initial_val['text'], initial_val['bgcolor'], initial_val['textcolor'], initial_val['height'], initial_val['width'], initial_val['transparence'], initial_val['min_word'], initial_val['mask_file'], initial_val['mask_line_width'], initial_val['mask_line_color'], ShipporiMincho)
params['forms']= WordCloudForm(request.POST or None, initial=initial_val)
if (request.method == 'POST'):
textfile = request.FILES.get('text_file').file
text = str(textfile.read(), 'utf-8')
bgcolor = str(request.POST['bgcolor_choice'])
textcolor = str(request.POST['textcolor_choice'])
height = int(request.POST['pic_size_height'])
width = int(request.POST['pic_size_width'])
if ('pic_transparence' in request.POST):
transparence = 'on'
else:
transparence = 'off'
min_word = int(request.POST['min_word'])
mask_file = request.FILES.get('mask_file')
mask_line_width = int(request.POST['mask_line_width'])
mask_line_color = str(request.POST['mask_line_color'])
selected_fonts = str(request.POST['fonts_choice'])
if selected_fonts == 'ShipporiMincho':
output_fonts = ShipporiMincho
elif selected_fonts == 'NotoSans':
output_fonts = NotoSans
elif selected_fonts == 'DelaGothicOne':
output_fonts = DelaGothicOne
elif selected_fonts == 'PottaOne':
output_fonts = PottaOne
elif selected_fonts == 'HachiMaruPop':
output_fonts = HachiMaruPop
elif selected_fonts == 'NewTegomin':
output_fonts = NewTegomin
wordcloudmake(text, bgcolor, textcolor, height, width, transparence, min_word, mask_file, mask_line_width, mask_line_color, output_fonts)
params['forms'] = WordCloudForm(request.POST)
return render(request, 'wordcloudapp/index.html', params)
ちなみにこれまでよりも変更点は多くなっているので、一つずつ解説していきます。
wordcloudmake関数
まずはwordcloudmake関数ですが、ここはこれまでの変更点と大きく変わらず、wordcloudmakeの引数の変更とWordCloudの引数の変更です。
フォント用に「char_fonts」という変数を設定してみました。
def wordcloudmake(text, bgcolor, textcolor, height, width, transparence, min_word, mask_file, mask_line_width, mask_line_color, char_fonts):
WordCloudでワードクラウド画像を作成しているのは2カ所。
これらに「font_path=char_fonts」を追加します。
wordcloud = WordCloud(width=width, height=height, mode=mode, background_color=bgcolor, colormap=textcolor, min_word_length=min_word, font_path=char_fonts).generate(words)
wordcloud = WordCloud(width=width, height=height, mask=mask_img, contour_width=mask_line_width, contour_color=mask_line_color, mode=mode, background_color=bgcolor, colormap=textcolor, min_word_length=min_word, font_path=char_fonts).generate(words)
つまり「char_fonts」の中身としてはフォントのパスになるわけです。
def index(request): (if (request.method == ‘POST’):以前)
def index(request):の前半ですが、ここでそれぞれのフォントのパスを設定します。
ShipporiMincho = r'./wordcloudapp/static/wordcloudapp/fonts/Shippori_Mincho/ShipporiMincho-Regular.ttf'
NotoSans = r'./wordcloudapp/static/wordcloudapp/fonts/Noto_Sans_JP/NotoSansJP-Regular.otf'
DelaGothicOne = r'./wordcloudapp/static/wordcloudapp/fonts/Dela_Gothic_One/DelaGothicOne-Regular.ttf'
PottaOne = r'./wordcloudapp/static/wordcloudapp/fonts/Potta_One/PottaOne-Regular.ttf'
HachiMaruPop = r'./wordcloudapp/static/wordcloudapp/fonts/Hachi_Maru_Pop/HachiMaruPop-Regular.ttf'
NewTegomin = r'./wordcloudapp/static/wordcloudapp/fonts/New_Tegomin/NewTegomin-Regular.ttf'
最終的にサーバー上に置かれるので、相対パスで設定しています。
そしてwordcloudmakeにフォントを渡すのですが、ここでは「ShipporiMincho」を直接指定しました。
wordcloudmake(initial_val['text'], initial_val['bgcolor'], initial_val['textcolor'], initial_val['height'], initial_val['width'], initial_val['transparence'], initial_val['min_word'], initial_val['mask_file'], initial_val['mask_line_width'], initial_val['mask_line_color'], ShipporiMincho)
今までは「initial_val」に初期値を設定して、wordcloudmakeに「initial_val[‘XXXXX’]」のように渡していたのですが、今回フォントパスを「initial_val」の後で設定しているため、「initial_val」に変数ShipporiMinchoを設定できません。
そのため、直接指定する方法にしたというわけです。
(initial_valとフォントのパスの指定を逆にすればできるのかも?試していないので分かりません)
def index(request): (if (request.method == ‘POST’):以降)
今度は情報が送信された後の動作の「if (request.method == ‘POST’):以降」の部分を修正していきます。
ここでは選択されたラジオボタンの情報からフォントパスへと変換し、wordcloudmake関数へ渡すことが必要になります。
ということでまずはラジオボタンの値の取得。
selected_fonts = str(request.POST['fonts_choice'])
このラジオボタンの値は先ほどのforms.pyで指定したように、「ShipporiMinchio」「NotoSans」「DelaGothicOne」「PottaOne」「HachiMaruPop」「NewTegomin」という形で得られます。
font_list = [('ShipporiMincho','しっぽり明朝'),('NotoSans','Noto Sans'),('DelaGothicOne','Dela Gothic One'),('PottaOne','Potta One'),('HachiMaruPop','はちまるポップ'),('NewTegomin','New Tegomin')]
そこでその情報からパスへと変換します。
if selected_fonts == 'ShipporiMincho':
output_fonts = ShipporiMincho
elif selected_fonts == 'NotoSans':
output_fonts = NotoSans
elif selected_fonts == 'DelaGothicOne':
output_fonts = DelaGothicOne
elif selected_fonts == 'PottaOne':
output_fonts = PottaOne
elif selected_fonts == 'HachiMaruPop':
output_fonts = HachiMaruPop
elif selected_fonts == 'NewTegomin':
output_fonts = NewTegomin
ラジオボタンから「’SipporiMincho’」という値が得られたら変数output_fontsに変数ShipporiMinchoを格納します。
この変数SipporiMinchoは先ほど設定したしっぽり明朝のフォントのパスになっているので、これでoutput_fontsにフォントのパスが格納されるわけです。
そして最後にwordcloudmake関数にoutput_fontsを渡して完了です。
wordcloudmake(text, bgcolor, textcolor, height, width, transparence, min_word, mask_file, mask_line_width, mask_line_color, output_fonts)
/webapp/wordcloudapp/templates/wordcloudapp/index.html
次にブラウザ上に表示するためのindex.htmlを修正していきます。
{% extends "basetemplates/base.html" %}
{% block title %}
<title>{{ title }}|ワードクラウド</title>
{% endblock %}
{% block content %}
<h1>その他:ワードクラウド</h1>
<form action="/wordcloudapp/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem;">テキストファイル:<input type="file" name="text_file", required=True></div>
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem">単語の最小文字数:{{ forms.min_word }}</div>
</div>
<div class="row">
<div class="col-lg-2" style="text-align: left; margin: 1rem 0rem;">出力画像サイズ</div>
<div class="col-lg-3" style="text-align: left; margin: 1rem 0rem;">縦:{{ forms.pic_size_height }} px</div>
<div class="col-lg-3" style="text-align: left; margin: 1rem 0rem;">横:{{ forms.pic_size_width }} px</div>
<div class="col-lg-4" style="text-align: left; margin: 1rem 0rem;">注:マスク使用時はマスクの画像サイズで出力</div>
</div>
<div class="row">
<div class="col" style="text-align: left; margin: 1rem 0rem;">フォント:{% for choice in forms.fonts_choice %} {{ choice }} {% endfor %}</div>
</div>
<div class="row">
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem;">背景色:{% for choice in forms.bgcolor_choice %} {{ choice }} {% endfor %}</div>
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem;">背景透明化:{{ forms.pic_transparence }}</div>
</div>
<div class="row">
<div class="col" style="text-align: left; margin: 1rem 0rem;">テキスト色:{% for choice in forms.textcolor_choice %} {{ choice }} {% endfor %}</div>
</div>
<div class="row">
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem;">マスク画像:<input type="file" name="mask_file"></div>
<div class="col-lg-6" style="text-align: left; margin: 1rem 0rem;">マスク線幅:{{ forms.mask_line_width }}</div>
</div>
<div class="row">
<div class="col" style="text-align: left; margin: 1rem 0rem;">マスク線色:{% for choice in forms.mask_line_color %} {{ choice }} {% endfor %}</div>
</div>
<p style="text-align: center;"><input type='submit' value="実行"></p>
</form>
<p style="text-align: center; margin:5rem;"><img src="/static/wordcloudapp/wordcloud.png"></p>
{% endblock %}
追加したのはこの部分。
<div class="row">
<div class="col" style="text-align: left; margin: 1rem 0rem;">フォント:{% for choice in forms.fonts_choice %} {{ choice }} {% endfor %}</div>
</div>
これでラジオボタンがついたフォント名が横並びで表示されます。
ここまでできたらサーバーを起動し、確認してみましょう。
それぞれのフォントを試してみましょう。
しっぽり明朝
Noto Sans JP
Dela Gothic One
Potta One
はちまるポップ
New Tegomin
それぞれフォントで受ける印象がだいぶ変わりますね。
これでワードクラウドアプリはできたので、あとはサーバーにアップして、多少のリンクの修正をすれば完成です。
サーバーへのアップの仕方や画像が表示しない場合などの修正の方法はこちらの記事で解説していますので、よかったら参考にしてください。
次回からはまた新しいアプリを作成していきましょう。
ではでは今回はこんな感じで。
コメント