Hey fellow RuneScape enthusiasts,
I recently stumbled upon a Reddit thread where user PoftheM was on the hunt for a tool that could generate a comprehensive overview of their RuneScape skills, mirroring the in-game skills tab. The challenge was to find a solution that allowed for manual inputs of skill levels and quest points, ensuring a retroactive and consistent representation of their account progression.
Well, guess what? I’ve got exciting news for you! I’ve developed a tool that aligns perfectly with PoftheM’s request, and I’m thrilled to introduce the RuneStat Generator.
Here’s a quick breakdown of what the tool offers:
1. Authentic In-Game Look: The RuneStat Generator provides a user-friendly interface that mirrors the in-game skills tab, capturing the essence of your RuneScape journey.
2. Manual Skill Inputs: Say goodbye to automated username-based data fetching. With RuneStat, you have complete control over your skill levels and quest points. Input them manually for a personalized touch.
3. Customizable Layout: Enjoy the familiar layout and icons of the in-game skills view. The tool strives to replicate the RuneScape experience down to the smallest detail.
4. Retroactive Overview: RuneStat lets you create a retroactive overview of your account progression, allowing you to track your RuneScape journey accurately.
To give you a sneak peek, is an example card created using RuneStat:

The output runescape skills table image is saved in the folder where the runescape_stat_generator.exe was ran from.
And you can start crafting your own by downloading the file for free here. The file was generated using the pyinstaller module using the spec config file. Don’t want to download the file? No worries, if you want the source code you can find it here: https://github.com/slyautomation/osrs_stat_generator
Feel free to test it out and let me know what you think. Happy RuneScaping!
# Embedded file name: main.py
from PIL import Image
def resourcePath(relativePath):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
basePath = sys._MEIPASS
except Exception:
basePath = os.path.abspath(".")
return os.path.join(basePath, relativePath)
def create_digit_total(num):
from PIL import Image
length = len(str(num))
num = str(num)
#print(num)
#print(num[0])
#print(length)
x = 0
image_name_output = resourcePath('blank_image.png')
mode = 'RGBA'
w = length
size = (25 * w, 60)
color = (0, 0, 0, 0)
im = Image.new(mode, size, color)
im.save(image_name_output, 'PNG')
im.close()
background = Image.open(image_name_output)
while x < length:
filename = resourcePath(str(num[x]) + '.png')
frontImage = Image.open(filename)
background.paste(frontImage, (0 + x * 25, 0), frontImage.convert('RGBA'))
x += 1
#print("resource path:", os.path.abspath("."))
background.save((resourcePath('images\\' + str(num) + 'total.png')), format='png')
def create_digit(num, skill):
from PIL import Image
length = len(str(num))
if length > 1:
image_name_output = resourcePath('blank_image.png')
mode = 'RGBA'
size = (50, 60)
color = (0, 0, 0, 0)
im = Image.new(mode, size, color)
im.save(image_name_output, 'PNG')
im.close()
filename = resourcePath(str(num)[0] + '.PNG')
filename1 = resourcePath(str(num)[1] + '.PNG')
frontImage = Image.open(filename)
secImage = Image.open(filename1)
background = Image.open(image_name_output)
background.paste(frontImage, (0, 0), frontImage.convert('RGBA'))
background.paste(secImage, (24, 0), secImage.convert('RGBA'))
background.save(resourcePath(('images/' + str(num) + skill + '.png')), format='png')
else:
filename = resourcePath(str(num) + '.png')
frontImage = Image.open(filename)
frontImage.save(resourcePath(('images\\' + str(num) + skill + '.png')), format='png')
def generate_stat_card_ttf(name):
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
filename = resourcePath('Card.png')
img = Image.open(filename)
draw = ImageDraw.Draw(img)
draw.fontmode = '1'
font = ImageFont.FreeTypeFont('RuneScape-Chat-Bold-07.ttf', 13)
strength = draw.text((40, 10), '73', (255, 255, 0), font=font)
img.save(resourcePath('sample-out.png'))
def create_copy():
filename1 = resourcePath('Card.png')
copy = Image.open(filename1)
copy.save(resourcePath('New.png'), format='png')
def generate_stat_card(num, skill):
from PIL import Image
create_digit(num, skill)
filename = resourcePath('images\\' + str(num) + skill + '.png')
filename1 = resourcePath('New.png')
if num < 10:
size = (10, 10)
else:
size = (15, 15)
frontImage = Image.open(filename)
background = Image.open(filename1)
frontImage.thumbnail(size, Image.NORMAL)
frontImage = frontImage.convert('RGBA')
background = background.convert('RGBA')
dict = {'attack':(42, 13),
'strength':(42, 45),
'defence':(42, 77),
'ranged':(42, 109),
'prayer':(42, 141),
'magic':(42, 173),
'runecrafting':(42, 205),
'construction':(42, 237),
'health':(105, 13),
'agility':(105, 45),
'herblore':(105, 77),
'thieving':(105, 109),
'crafting':(105, 141),
'fletching':(105, 173),
'slayer':(105, 205),
'hunter':(105, 237),
'mining':(168, 13),
'smithing':(168, 45),
'fishing':(168, 77),
'cooking':(168, 109),
'firemaking':(168, 141),
'woodcutting':(168, 173),
'farming':(168, 205)}
dict_2 = {'attack':(59, 26),
'strength':(59, 58),
'defence':(59, 90),
'ranged':(59, 122),
'prayer':(59, 154),
'magic':(59, 186),
'runecrafting':(59, 218),
'construction':(59, 250),
'health':(122, 26),
'agility':(122, 58),
'herblore':(122, 90),
'thieving':(122, 122),
'crafting':(122, 154),
'fletching':(122, 186),
'slayer':(122, 218),
'hunter':(122, 250),
'mining':(185, 26),
'smithing':(185, 58),
'fishing':(185, 90),
'cooking':(185, 122),
'firemaking':(185, 154),
'woodcutting':(185, 186),
'farming':(185, 218)}
background.paste(frontImage, dict[skill], frontImage.convert('RGBA'))
if num < 10:
background.paste(frontImage, dict_2[skill], frontImage.convert('RGBA'))
else:
background.paste(frontImage, (dict_2[skill][0] - 5, dict_2[skill][1]), frontImage.convert('RGBA'))
background.save(resourcePath('New.png'), format='png')
def generate_total(total):
from PIL import Image
create_digit_total(total)
filename = resourcePath('images\\' + str(total) + 'total.png')
filename1 = resourcePath('New.png')
if total > 999:
size = (25, 25)
else:
if total > 99:
size = (19, 19)
else:
size = (15, 15)
frontImage = Image.open(filename)
background = Image.open(filename1)
frontImage.thumbnail(size, Image.NORMAL)
frontImage = frontImage.convert('RGBA')
background = background.convert('RGBA')
position = (155, 250)
if total > 999:
background.paste(frontImage, position, frontImage.convert('RGBA'))
else:
if total > 99:
background.paste(frontImage, (position[0] + 3, position[1]), frontImage.convert('RGBA'))
else:
background.paste(frontImage, (position[0] + 5, position[1]), frontImage.convert('RGBA'))
background.save(resourcePath('New.png'), format='png')
import os
def ensure_dir():
directory = os.path.dirname(resourcePath('images'))
#print(directory)
if not os.path.exists(resourcePath('images')):
os.makedirs(resourcePath('images'))
ensure_dir()
skill_list = [
'attack',
'strength',
'defence',
'ranged',
'prayer',
'magic',
'runecrafting',
'construction',
'health',
'agility',
'herblore',
'thieving',
'crafting',
'fletching',
'slayer',
'hunter',
'mining',
'smithing',
'fishing',
'cooking',
'firemaking',
'woodcutting',
'farming']
import tkinter
from tkinter import *
from PIL import Image, ImageTk
test = []
root = Tk()
root.title('OSRS Stat Generator')
root.geometry('670x600')
root.configure(background='#40362C')
Font_tuple = ('Unispace', 15)
filename = resourcePath('osrs_title_2.png')
image1 = Image.open(filename)
image1 = image1.convert('RGBA')
h = (500, 500)
image1.thumbnail(h, Image.NORMAL)
test1 = ImageTk.PhotoImage(image1)
label1 = tkinter.Label(image=test1, background='#40362C', anchor=CENTER, justify=CENTER)
label1.image = test1
label1.grid(column=0, columnspan=5, sticky='e')
x = 1
while x < 9:
lbl = Label(root, text=(skill_list[(x - 1)]), background='#40362C', fg='yellow', padx=20)
lbl.configure(font=Font_tuple)
lbl.grid(column=0, row=x)
x += 1
x = 9
while x < 17:
lbl = Label(root, text=(skill_list[(x - 1)]), background='#40362C', fg='yellow')
lbl.configure(font=Font_tuple)
lbl.grid(column=2, row=(x - 8))
x += 1
x = 17
while x < 24:
lbl = Label(root, text=(skill_list[(x - 1)]), background='#40362C', fg='yellow')
lbl.configure(font=Font_tuple)
lbl.grid(column=4, row=(x - 16))
x += 1
x = 1
while x < 9:
txt = Entry(root, width=5, background='#40362C', fg='yellow')
txt.insert(-1, 1)
txt.configure(font=Font_tuple)
test.append(txt)
txt.grid(column=1, row=x)
x += 1
x = 9
while x < 17:
txt = Entry(root, width=5, background='#40362C', fg='yellow')
txt.configure(font=Font_tuple)
txt.insert(-1, 1)
test.append(txt)
txt.grid(column=3, row=(x - 8))
x += 1
x = 17
while x < 24:
txt = Entry(root, width=5, background='#40362C', fg='yellow')
txt.configure(font=Font_tuple)
txt.insert(-1, 1)
test.append(txt)
txt.grid(column=5, row=(x - 16))
x += 1
total = 0
t = 0
while t < len(test):
total += int(test[t].get())
t += 1
lbl = Label(root, text=('Total Level: ' + str(total)), background='#40362C', fg='yellow', anchor=CENTER)
lbl.configure(font=Font_tuple)
lbl.grid(column=4, row=8, columnspan=2)
def clicked():
x = 0
create_copy()
total = 0
t = 0
while t < len(test):
total += int(test[t].get())
t += 1
lbl = Label(root, text=('Total Level: ' + str(total)), background='#40362C', fg='yellow', anchor=CENTER)
lbl.configure(font=Font_tuple)
lbl.grid(column=4, row=8, columnspan=2)
while x < 23:
generate_stat_card(int(test[x].get()), skill_list[x])
x += 1
generate_total(int(total))
result = tkinter.Toplevel()
result.title('OSRS Stat Result')
result.configure(background='#40362C')
canvas = Canvas(result, width=300, height=300)
canvas.pack()
filename = resourcePath('New.png')
img = PhotoImage(file=filename)
canvas.create_image(20, 20, anchor=NW, image=img)
import os, glob
basePath = os.path.abspath(".")
files = glob.glob(basePath + '/images/*')
for f in files:
os.remove(f)
frontImage = Image.open(filename)
#print(basePath)
frontImage.save(basePath + '\\Result.png', format='png')
#print('skills stats generated!!!')
result.mainloop()
btn = Button(root, text='Generate Stats', fg='yellow',
command=clicked,
background='#40362C',
pady=0)
btn.grid(column=2, row=10, columnspan=2, pady=20)
btn.configure(font=Font_tuple)
root.mainloop()
How does the stat generator code Work?
Here’s a breakdown of the main functionalities:
- Image Processing Functions:
create_digit_total(num)
: Creates a total level image by combining individual digit images for each digit in the given number.create_digit(num, skill)
: Creates an image for a specific skill level by combining two digit images (if necessary).generate_stat_card_ttf(name)
: Generates a stat card using a TrueType font and saves the result as ‘sample-out.png’.create_copy()
: Creates a copy of a card image (‘Card.png’) and saves it as ‘New.png’.generate_stat_card(num, skill)
: Generates a stat card for a specific skill level and overlays it onto the copied card image.generate_total(total)
: Generates a total level stat card and overlays it onto the copied card image.
- Utility Functions:
ensure_dir()
: Ensures the existence of the ‘images’ directory.resourcePath(relativePath)
: Returns the absolute path to a resource, considering both development and PyInstaller scenarios.
- Tkinter GUI:
- The GUI consists of an input section for entering skill levels and a “Generate Stats” button.
- Entries are organized in three columns corresponding to the skill categories: Attack, Strength, Defense, …, Farming.
- The total level is displayed at the bottom of the GUI.
- Clicking the “Generate Stats” button triggers the
clicked()
function, which calculates the total level, generates individual skill cards, and displays the result in a new Tkinter window.
- Cleanup and Display:
- After generating the result, the script cleans up temporary skill images and displays the resulting stat card in a new Tkinter window (‘Result.png’).
This is one awesome article.Really thank you! Really Cool.
A round of applause for your article post. Really Great.
I loved your blog article. Want more.
Thank you for your article.Thanks Again. Awesome.
Major thankies for the post.Really looking forward to read more. Will read on…
I loved your post.Really thank you! Will read on…
Thanks again for the blog.Much thanks again. Really Cool.
I really like and appreciate your article post.Really thank you! Want more.
Really informative blog. Want more.
Hey, thanks for the blog post.Really thank you!
A big thank you for your article post.Thanks Again. Really Cool.
I think this is a real great article.Really looking forward to read more. Fantastic.
Appreciate you sharing, great article post.Thanks Again. Really Cool.
I really enjoy the article post. Really Great.
I cannot thank you enough for the article.Thanks Again. Much obliged.
Im thankful for the post.Really thank you! Want more.
Heya i am for the primary time here. I came across this boardand I in finding It truly helpful & it helped me out a lot.I’m hoping to provide one thing back and aid others such as you helped me.
Very nice post. I just stumbled upon your blog and wanted to say thatI have truly enjoyed browsing your blog posts. After all I’ll besubscribing to your rss feed and I hope you write again very soon!
This is one awesome blog article. Will read on…
I appreciate you sharing this blog post.Really thank you! Want more.
A round of applause for your blog article.
Say, you got a nice article post.Really looking forward to read more. Much obliged.
Thanks for sharing, this is a fantastic blog.Much thanks again. Great.
Im grateful for the post.
Major thankies for the blog.Much thanks again. Great.
I think this is a real great blog.Really thank you! Keep writing.
Enjoyed every bit of your article post.Much thanks again. Really Cool.
I am so grateful for your post.Much thanks again. Great.
Thank you for your article.Really thank you! Really Great.
Thanks so much for the blog post.Really thank you! Really Great.
I really enjoy the article post. Awesome.
wow, awesome blog article. Want more.
Thanks so much for the article post.Really looking forward to read more. Fantastic.
Thank you for every other excellent article. The place else may anyone get that kind of information in such a perfect manner of writing? I’ve a presentation next week, and I am on the search for such info.
Really appreciate you sharing this article post.Really looking forward to read more. Keep writing.
I do agree with all the ideas you’ve offered on your post. They are very convincing and will certainly work. Still, the posts are too quick for novices. May you please extend them a little from subsequent time? Thanks for the post.
A big thank you for your blog article.Thanks Again. Great.
Looking forward to reading more. Great post.Really looking forward to read more. Great.
Thanks again for the blog.Thanks Again. Really Great.
I really enjoy the article post.Really thank you! Fantastic.
Major thanks for the blog article. Much obliged.
Thanks for sharing, this is a fantastic blog.Thanks Again.
best erectile dysfunction pills review: best erectile dysfunction pills ed pills for sale
Hey there! I know this is somewhat off topic but I was wondering if you knew where I could find a captcha plugin for my comment form?I’m using the same blog platform as yours and I’m having trouble finding one?Thanks a lot!
Very informative blog. Great.
Thank you for the good writeup. It in fact used to be aentertainment account it. Look advanced to more brought agreeable fromyou! By the way, how could we keep in touch?
I’m no longer certain where you’re getting your info, but good topic.I needs to spend a while learning much more or understanding more.Thanks for great info I was searching forthis info for my mission.
Hello! I just wanted to ask if you ever have any trouble with hackers?My last blog (wordpress) was hacked and I ended up losing several weeks ofhard work due to no back up. Do you have any solutions to protect against hackers?
Thank you ever so for you post.Thanks Again. Cool.
I quite like reading through a post that can make people think.Also, many thanks for allowing me to comment!
I cannot thank you enough for the blog article.Much thanks again. Want more.
I appreciate you sharing this blog post.Really looking forward to read more. Want more.
It’s difficult to find educated folks for this subject matter, nevertheless, you seem like you know very well what you’re referring to! Many thanks
Thanks for sharing, this is a fantastic article post.Really thank you! Keep writing.
This is one awesome article.Really looking forward to read more. Awesome.
I really liked your blog.Thanks Again. Fantastic.
Looking forward to reading more. Great post.Really looking forward to read more. Keep writing.
Looking forward to reading more. Great article.Really thank you! Really Great.
Really informative blog post.Much thanks again. Awesome.
Thank you ever so for you article post. Great.
Wow, great article. Cool.
Thanks for sharing, this is a fantastic blog post.Much thanks again. Will read on…
Im thankful for the article. Much obliged.
Hi, I do believe this is an excellent blog. I stumbledupon it 😉 I’m going to return once again since I saved as a favorite it. Money and freedom is the best way to change, may you be rich and continue to help others.
Superb post however , I was wondering if you could writea litte more on this topic? I’d be very thankful if youcould elaborate a little bit more. Bless you!
Alergia al césped: forma, enfermedad y cuidado.
OH MY GOD
In a particular poker game, a player’s poker money account is comprised of genuine money and real chips. Poker rules might seem quite perplexing for novices. This 1 is too essential as it does the negotiating for you.
I loved your blog post. Great.
Regards. Awesome information.how to write a good scholarship essay argumentative essay top essay writing service
Thank you ever so for you article post.Much thanks again. Awesome.
If your gambling is no longer exciting then don’t waitfor the trouble to get worse.
A big thank you for your blog post. Keep writing.
I read this paragraph fully about the resemblance of hottest and previous technologies, it’s awesome article.
Really enjoyed this blog post.Much thanks again. Cool.
Hi there, all the time i used to check blog posts hereearly in the break of day, since i love to learn more andmore.
I loved your article post.Really looking forward to read more. Really Cool.
Fantastic article.Thanks Again. Really Great.
I think this is a real great article post.Much thanks again. Really Cool.
Awesome article post.Really looking forward to read more. Awesome.
Very informative article.Really thank you! Keep writing.
There is certainly a lot to learn about this topic. I like all of the points you’ve made.
AccStores.com has a large selection of suppliers. This service send links to your orders via email and you have all the access to it in your personal account. It’s convenient. Highly recommend!Click[Link deleted]