If you set the rich menu from the Messaging API, you can set it quite flexibly, and you can set up to 20 buttons. However, it is ridiculously troublesome to create Json to set it, so I wrote a script that can be set easily.
For example, if you have an image like this If you set eight buttons to each circle, it would be too tedious to find out the coordinates, height, and width.
First, create an original image with a size of 2500x1686 for the rich menu. Just like the image this time, it's fine if it's just a simple figure, but since it can't recognize complicated figures such as photos and characters, it's a good idea to create individual images that emphasize only the part you want to use as a button.
Let's call the image where the button part is easy to understand `` `sample.png```
Feed the script.
$ python menu_gen.py sample.png
The script looks like this.
menu_gen.py
import cv2
import json
import collections as cl
import codecs
import sys
def main():
args = sys.argv
file_name = args[1]
img_color = cv2.imread(file_name) #Import original image
img_gray = cv2.imread(file_name, cv2.IMREAD_GRAYSCALE) #Image loading in grayscale
ret, img_binary = cv2.threshold(img_gray, 250, 255, cv2.THRESH_BINARY_INV) #Binarize grayscale images
contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #Contour extraction
#cv2.drawContours(img_color, contours, -1, (0,0,255), 2) #Draw red on the contour
print(len(contours),"I found one") #Number of contours
rich_menu_json = cl.OrderedDict()
rich_menu_json["size"] = {"width": 2500, "height": 1686}
rich_menu_json["selected"] = "true"
rich_menu_json["name"] = file_name
rich_menu_json["chatBarText"] = 'menu'
rich_menu_json["areas"] = []
for i, c in enumerate(contours):
x,y,w,h = cv2.boundingRect(c) #Rectangle judgment
# print("["+str(i)+"]",x,y,w,h)
img_color = cv2.rectangle(img_color, (x,y), (x+w, y+h), (255,0,0), 3)
img_color = cv2.putText(img_color, "b_"+str(i), (x+int(w/3),y+int(h/2)), cv2.FONT_HERSHEY_SIMPLEX, 4, (255,255,255), 2, cv2.LINE_AA)
tmp = cl.OrderedDict()
tmp["bounds"] = {
"x": x,
"y": y,
"width": w,
"height": h
}
tmp["action"] = {
"type": "postback",
"label": "b_"+str(i),
"data": "{ }",
"displayText": str(i)
}
rich_menu_json["areas"].append(tmp)
fw = codecs.open(file_name.split('.')[0]+"_created_.json", 'w', "utf-8")
json.dump(rich_menu_json, fw, indent=4, ensure_ascii=False)
print('''
Create a rich menu with the following contents
curl -v -X POST https://api.line.me/v2/bot/richmenu \\
-H 'Authorization: Bearer {Access token}' \\
-H 'Content-Type: application/json' \\
-d \\
'
Insert the edited Json here
'
''')
cv2.imwrite(file_name.split('.')[0]+"_created_.jpg ", img_color)
cv2.imshow("window", img_color)
cv2.waitKey()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
Then, an image with a label for each button like `` `b_1``` is generated like this.
At the same time, `` `sample_created.json``` is created from the coordinates, height, and width information.
It looks like the following
{
"size": {
"width": 2500,
"height": 1686
},
"selected": "true",
"name": "sample.png ",
"chatBarText": "menu",
"areas": [
{
"bounds": {
"x": 91,
"y": 1131,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_0",
"data": "{ }",
"displayText": "0"
}
},
{
"bounds": {
"x": 2002,
"y": 1130,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_1",
"data": "{ }",
"displayText": "1"
}
},
{
"bounds": {
"x": 1047,
"y": 1130,
"width": 406,
"height": 407
},
"action": {
"type": "postback",
"label": "b_2",
"data": "{ }",
"displayText": "2"
}
},
{
"bounds": {
"x": 1534,
"y": 640,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_3",
"data": "{ }",
"displayText": "3"
}
},
{
"bounds": {
"x": 559,
"y": 639,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_4",
"data": "{ }",
"displayText": "4"
}
},
{
"bounds": {
"x": 1047,
"y": 149,
"width": 406,
"height": 407
},
"action": {
"type": "postback",
"label": "b_5",
"data": "{ }",
"displayText": "5"
}
},
{
"bounds": {
"x": 91,
"y": 149,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_6",
"data": "{ }",
"displayText": "6"
}
},
{
"bounds": {
"x": 2002,
"y": 148,
"width": 407,
"height": 407
},
"action": {
"type": "postback",
"label": "b_7",
"data": "{ }",
"displayText": "7"
}
}
]
}
For the time being, it is in postback format, but it seems that you can save time by filling in the contents while comparing the contents around here with the image.
It's a rough explanation, but if it helps someone.