You are not logged in.
Pages: 1
Hello everybody
I just wanted to share a python script I made to launch MAME roms.
This is not a frontend, you can just browse your roms, with snapshot displayed, and launch them trough MAME. You can create rom lists to hold, let's say, your favorites.
I made this script because I didn't find a frontend for Linux without QT dependencies or without a complicated interface. I'm not a programmer, I just copied python lines of code from the internet. I'm sure there are a lot of programs to manage MAME way better then this script.
Here is a readme for the script and the script itself follows it.
MAME Launcher Script
It's a python script that displays a list with roms detected from the rompath
specified in MAME and the associated snapshot, if present, in the snapshot_directory,
also specified in MAME, using the command mame -sc, which in most cases reads
mame.ini configuration file. Read MAME documentation if you wish to know more
about mame.ini.
IMPORTANT: this is not a frontend, it just launches MAME with the selected rom.
This script is provided as is, without any express, implied and/or statutory
warranties. Use at your own risk, the author takes no responsibility. Also
remember that to use roms protected by copyright you must have permission by
the copyright holder, or own the original machine.
Before you attempt to run MAME Launcher, you should make sure you can start
MAME by itself, using the command line mame <rom name>.
A MAME logo picture must exist in the same directory as the script, or any
other picture of your choice: you can configure the picture name via the
configuration file, or you can rename your custom picture to mame.png.
Dependecies
This script uses tkinter for the gui, so you have to install python tcl/tk
bindings for it to work.
For Ubuntu/Debian install packages python-tk and python-pil.imagetk.
Obviously you have to install MAME, as said before.
Structure
By default, three files are created in the same diretory as the MAME Launcher
script:
- mame_launcher_descs.cfg
When MAME Launcher is first started, it dumps out from the command mame -ll
a full list of the supported roms and relative description, e.g.:
Car Polo;carpolo
Star Fire 2;starfir2
The Adventures of Robby Roto!;robby
It's just a csv file, with ";" separator.
Later executions of MAME Launcher will read this file to retrieve the
corresponding description for roms.
- mame_launcher_all_games.cfg
After the creation of the previous file, MAME Launcher writes to this file
the list of roms found in the rompath retrieved from MAME configuration,
using the command mame -sc, which in most cases reads mame.ini configuration
file. In this file you'll find just rom names, like
carpolo
starfir2
robby
this rom list is not alphabetically ordered, nor it needs to be.
When the gui starts, the description file will be read and for each rom the
associated description is displayed, ordered alphabetically. This is a special
rom group, they're discussed below.
- mame_launcher_last_roms.cfg
This file is for writing the last rom selected in a rom group, and to track
the last group displayed, so the next time you select a rom group the last rom
selected will be active, or, the next time you start MAME Launcher, the last
rom group will be displayed, with the last rom selected, activated.
Rom Groups
You can create rom groups using the button in the gui, this simply will create a
file in the same directory as MAME Launcher, named mame_launcher_<name>.cfg.
The first line will be a special formatted line to define the group name as will
be displayed in the combobox. The line is:
game group title: <name of group>
Once the group is created, you can select roms in another rom group, let's say
all games, and add them to your newly created group. This means a line will be
written in the mame_launcher_<name>.cfg file. This cfg file contains simply the
rom names like the mame_launcher_all_games.cfg. You can easily create a file by
hand, just remember the first line and to insert rom names one per line.
In this cfg files, the roms are not alphabetically ordered, nor they need to be.
When you select a rom group, the corresponding cfg file will be read and
descriptions will be retrieved from the mame_launcher_descs.cfg, populating the
games list, alphabetically ordered by description.
There are two special groups:
- the All Games group (mentioned before), it will be always present and contains
all games found in the rompath specified in mame configuration. You can't
modify or delete this group
- the Working games group, you can create it by clicking the "Scan for working
games" button in the gui, this means that every rom present in All Games group
will be tested with the command mame -verifyroms <rom name>, and if the rom
passes the test will be added to the Working games group. The rom list will be
written to mame_launcher_working_games.cfg. You can modify or delete this
group. Please note: testing every rom you have on your disk can be a very long
process, depending on how many roms you have, you can monitor the script work
viewing the output in the shell window
Configuration file
Optionally, you can create a general configuration file, to set few parameters.
You can create the default configuration file with the command:
python mame_launcher.py -wc
this creates the default configuration file, mame_launcher.cfg, in the same
directory the MAME Launcher script is. In this configuration file you can set:
- mame_exe: the location of the mame executable,
- mame_logo: the file name for a mame logo, or whatever picture you want
displayed when the selected rom has no snapshot available, it has to be in the
same directory of MAME Launcher script,
- all_games_name: a different name for the All Games rom group: please note that
this name will be written in the all games cfg file the first time you create
it, but, just for this group, the name you specify in mame_launcher.cfg
configuration file will be displayed in the combobox, in MAME Launcher gui.
Other groups will be displayed in the combobox with the name specified in
their respective cfg file.
- working_games_name: a different name for the working games group: remember
that the first time you create the working games group, the name you specified
in mame_launcher.cfg will be written in mame_launcher_working_games.cfg, and
then every time you start MAME Launcher the name specified in
mame_launcher_working_games.cfg will be displayed in the combobox.
- last_group: a cfg file name to load when MAME Launcher starts, or you can
leave it empty to load the last active group saved in
mame_launcher_last_roms.cfg
Launching games
Select a game in the listbox and press Enter, or double click it, or click the
button Play. Eventually, you can specify additional parameters for MAME
executable, using the labeled entry field near the Play button.
Ending note
I'm not a programmer, I just wanted to play with MAME on my Linux box, and I
didn't find a fronted suited to my needs. Luckly, python is easy and a lot of
documentation can be found on the internet. You can modify the script as you
wish.
Also, sorry for my english.
And here is the python script
## MAME Launcher Script
import os, tkMessageBox, ImageTk, PIL, subprocess, csv, ttk, re, ConfigParser, platform
from sys import exit, argv
from PIL import Image
from Tkinter import *
# https://code.activestate.com/recipes/578894-mousewheel-multiplatform-tkinter/
# adds mouse wheel binding to scroll
class ScrollingArea(object):
OS = platform.system()
def __init__(self, root, factor = 2):
self.activeArea = None
if type(factor) == int:
self.factor = factor
else:
raise Exception("Factor must be an integer.")
if self.OS == "Linux" :
root.bind_all('<4>', self.onMouseWheel, add='+')
root.bind_all('<5>', self.onMouseWheel, add='+')
else:
# Windows and MacOS
root.bind_all("<MouseWheel>", self.onMouseWheel, add='+')
def onMouseWheel(self,event):
if self.activeArea:
self.activeArea.onMouseWheel(event)
def mouseWheel_bind(self, widget):
self.activeArea = widget
def mouseWheel_unbind(self):
self.activeArea = None
def build_function_onMouseWheel(self, widget, orient, factor = 1):
view_command = getattr(widget, orient+'view')
if self.OS == 'Linux':
def onMouseWheel(event):
if event.num == 4:
view_command("scroll",(-1)*factor,"units" )
elif event.num == 5:
view_command("scroll",factor,"units" )
elif self.OS == 'Windows':
def onMouseWheel(event):
view_command("scroll",(-1)*int((event.delta/120)*factor),"units" )
elif self.OS == 'Darwin':
def onMouseWheel(event):
view_command("scroll",event.delta,"units" )
return onMouseWheel
def add_scrolling(self, scrollingArea, xscrollbar=None, yscrollbar=None):
if yscrollbar or not yscrollbar is None:
#scrollingArea.configure(xscrollcommand=xscrollbar.set)
scrollingArea.configure(yscrollcommand=yscrollbar.set)
yscrollbar['command']=scrollingArea.yview
if xscrollbar or not xscrollbar is None:
#scrollingArea.configure(yscrollcommand=yscrollbar.set)
scrollingArea.configure(xscrollcommand=xscrollbar.set)
xscrollbar['command']=scrollingArea.xview
scrollingArea.bind('<Enter>',lambda event: self.mouseWheel_bind(scrollingArea))
scrollingArea.bind('<Leave>', lambda event: self.mouseWheel_unbind())
if xscrollbar and not hasattr(xscrollbar, 'onMouseWheel'):
xscrollbar.onMouseWheel = self.build_function_onMouseWheel(scrollingArea,'x', self.factor)
if yscrollbar and not hasattr(yscrollbar, 'onMouseWheel'):
yscrollbar.onMouseWheel = self.build_function_onMouseWheel(scrollingArea,'y', self.factor)
main_scrollbar = yscrollbar or xscrollbar
if main_scrollbar:
scrollingArea.onMouseWheel = main_scrollbar.onMouseWheel
for scrollbar in (xscrollbar, yscrollbar):
if scrollbar:
scrollbar.bind('<Enter>', lambda event, scrollbar=scrollbar: self.mouseWheel_bind(scrollbar) )
scrollbar.bind('<Leave>', lambda event: self.mouseWheel_unbind())
# sets right-click with copy-cut-paste actions
# http://stackoverflow.com/questions/4266566/stardand-context-menu-in-python-tkinter-text-widget-when-mouse-right-button-is-p
def rClicker(e):
## right click context menu for all Tk Entry and Text widgets
try:
def rClick_Copy(e, apnd=0):
e.widget.event_generate('<Control-c>')
def rClick_Cut(e):
e.widget.event_generate('<Control-x>')
def rClick_Paste(e):
e.widget.event_generate('<Control-v>')
e.widget.focus()
list_cmd=[
('Cut', lambda e=e: rClick_Cut(e)),
('Copy', lambda e=e: rClick_Copy(e)),
('Paste', lambda e=e: rClick_Paste(e)),
]
rmenu = Menu(None, tearoff=0, takefocus=0)
for (txt, cmd) in list_cmd:
rmenu.add_command(label=txt, command=cmd)
rmenu.tk_popup(e.x_root+40, e.y_root+10,entry="0")
#rmenu.post(e.x_root+40, e.y_root+10)
except TclError:
print ' - rClick menu, something wrong'
pass
return "break"
def rClickbinder(r):
try:
for b in ['Text', 'Entry', 'Listbox', 'Label']:
r.bind_class(b, sequence='<Button-3>', func=rClicker, add='')
except TclError:
print ' - rClickbinder, something wrong'
pass
# purge all special characters from a string
# http://stackoverflow.com/questions/23586728/django-trying-to-split-and-urlify-tags-in-models-but-code-isnt-working
def urlify(str):
s = str.strip()
# Remove all non-word characters (everything except numbers and letters)
s = re.sub(r"[^\w\s]", '', s)
# Replace all runs of whitespace with a single dash
s = re.sub(r"\s+", '-', s)
return s
# takes the first item in a list and returns it to use it in a sort function
def by_zero(a):
return a[0].lower()
# change the snapshot associated to a rom name
def change_img(e):
#print "change_img"
num = listbox.curselection()[0]
try:
rom = games_list[num]
except IndexError:
print "Invalid rom"
img = mame_logo
game_desc.configure(text = "...")
else:
for p in img_path:
p = os.path.expandvars(p)
#print p
if os.path.isfile(os.path.join(p, rom[1] + ".png")):
img = os.path.join(p, rom[1] + ".png")
else:
if os.path.isfile(os.path.join(p, rom[1] + ".jpg")):
img = os.path.join(p, rom[1] + ".jpg")
else:
img = mame_logo
#print img
cfg_files_lists[group_titles[group.current()][1]][2] = rom[1]
write_last_roms_file()
game_desc.configure(text = rom[0] + "\n" + rom[1] + "\n")
game_desc.update()
img1 = ImageTk.PhotoImage(PIL.Image.open(img))
lbl.configure(image = img1)
lbl.image = img1
lbl.update()
# for the button "play", to play the selected rom in the listbox
def game_run():
try:
rom = cfg_files_lists[group_titles[group.current()][1]][2]
except (KeyError, ValueError):
print "Invalid rom"
else:
par = parameters.get().strip().lower().split()
#print "par", par
com = [mame_exe]
com.extend(par)
com.append(rom)
#print com
try:
print "Game run", com
subprocess.check_call(com, shell=False)
except subprocess.CalledProcessError as e1:
print"MAME error with", rom, e1
# parses the mame configuration for the rompath e snapshot_directory values
def retrieve_paths():
out = subprocess.check_output((mame_exe, "-sc"), shell=False)
#out = subprocess.check_output(("ls", "-l"), shell=False)
check = [0,0]
for line in out.strip().split("\n"):
#print line
if "rompath" in line.strip().lower():
g = line.strip().split()
g_path = g[1].split(";")
#print "Games",g_path
check[0] = 1
if "snapshot_directory" in line.strip().lower():
s = line.strip().split()
s_path = s[1].split(";")
#print "Snap",s_path
check[1] = 1
if check == [1,1]:
return (g_path, s_path)
else:
if check[0] == 0 or len(g_path) == 0:
print("rompath not found in MAME configuration")
if check[1] == 0 or len(s_path) == 0:
print("snapshot_directory not found in MAME configuration")
exit(1)
# builds, or reads, the main files with all the roms and associated description and all the roms found in the rompath retrieved earlier
def first_games_listing(force):
# dictionary with roms name as keys, and every key is a list of rom descrition and rom name
global descs_list
# dictionary with cfg files as keys, and every key is a list with: list of rom description and rom name found in the cfg file, only rom names, the last rom selected
global cfg_files_lists
global last_group
gl = []
descs_list = {}
if os.path.isfile(descs_file) and not force == "force":
with open(descs_file, "rb") as r1:
rc = csv.reader(r1, delimiter=";")
descs_list = {x[1]: x for x in rc}
if os.path.isfile(all_games_file):
with open(all_games_file ,"rb") as r1:
roms = r1.read().splitlines()
del roms[0]
gl = [descs_list[i] for i in roms]
if not os.path.isfile(all_games_file):
print "Building all games file..."
roms = []
with open(all_games_file, "wb") as w:
w.write(all_games_title)
for p in games_path:
p = os.path.expandvars(p)
if os.path.isdir(p):
for f in os.listdir(p):
r = os.path.splitext(f)
rom = r[0]
if rom in descs_list and not rom in roms:
gl.append(descs_list[rom])
roms.append(rom)
w.write("\n" + rom)
if not os.path.isfile(descs_file) or force == "force":
print "Building descriptions and roms main file..."
try:
out = subprocess.check_output((mame_exe, "-ll"), shell=False)
except subprocess.CalledProcessError:
print "Error generating list"
exit(1)
else:
r = out.strip().splitlines()
del r[0]
#descs_list = []
with open(descs_file, "wb") as w:
wr = csv.writer(w, delimiter=";")
for l in r:
#print l
l = l.strip().split()
#print l
d = l[1:]
d = " ".join(d)
#print d
d = d[1:-1]
rom = [d, l[0].strip()]
descs_list[rom[1]] = rom
wr.writerow(rom)
if os.path.isfile(all_games_file) and not force == "force":
print "Building all games file..."
with open(all_games_file, "rb") as r:
roms = r.read().splitlines()
del roms[0]
gl = [descs_list[i] for i in roms]
if not os.path.isfile(all_games_file) or force == "force":
print "Building all games file..."
roms = []
with open(all_games_file, "wb") as w:
w.write(all_games_title)
for p in games_path:
p = os.path.expandvars(p)
if os.path.isdir(p):
for f in os.listdir(p):
r = os.path.splitext(f)
rom = r[0]
#print rom
if rom in descs_list and not rom in roms:
gl.append(descs_list[rom])
roms.append(rom)
w.write("\n" + rom)
if len(gl) > 0:
gl.sort(key=by_zero)
cfg_files_lists = {all_games_file: [gl, roms, gl[0][1]]}
else:
cfg_files_lists = {all_games_file: [gl, roms, "empty"]}
if os.path.isfile(last_roms_file):
with open (last_roms_file, "rb") as r1:
rc = csv.reader(r1, delimiter=";")
cfg = [x[1] for x in group_titles]
for i in rc:
if os.path.isfile(i[0]):
if last_group is None or len(last_group) == 0 or not last_group in cfg:
try:
if i[2] == "active":
if os.path.isfile(i[0]):
last_group = i[0]
print i[0], "active"
except IndexError:
#print i[0], "not active"
pass
if not i[0] == all_games_file:
#print "read", i[0]
read_conf(i[0], None, "y")
#print "list", cfg_files_lists[i[0]]
cfg_files_lists[i[0]][2] = i[1]
# reads a conf file and adds the roms found in the cfg_files_lists dict, or reads the cfg_files_lists dict if already existent
def read_conf(file, rom, desc):
try:
gl = cfg_files_lists[file][0]
gl.sort(key=by_zero)
roms = cfg_files_lists[file][1]
except KeyError:
with open(file, "rb") as r:
roms = r.read().splitlines()
#print roms[0]
del roms[0]
#gl = [descs_list[only_roms_list.index(i)] for i in roms if i in only_roms_list]
gl = [descs_list[i] for i in roms]
gl.sort(key=by_zero)
if len(gl) > 0:
cfg_files_lists[file] = [gl, roms, gl[0][1]]
else:
cfg_files_lists[file] = [gl, roms, "empty"]
if not rom is None:
if rom in roms:
return True
return False
if desc == "y":
return gl
return roms
# activated with combobox selection, to change the games list displayed in the listbox
def change_listbox():
global games_list
global last_group
print "change_listbox", group_var.get()
try:
gl = cfg_files_lists[group_titles[group.current()][1]]
#print gl
n = gl[0].index(descs_list[gl[2]])
#print n
except (KeyError, ValueError):
#print "no index"
n = 0
if active_group(last_group):
#print "Group already active"
#listbox.select_set(cfg_files_lists[group_titles[group.current()][1]][2])
listbox.select_set(n)
listbox.activate(n)
else:
#print "Loading", group_var.get()
cur = group_titles[group.current()][1]
games_list = read_conf(cur, None, "y")
#print cfg_files_lists[group_titles[group.current()][1]]
listbox.delete(0, END)
if len(games_list) > 0:
for i in games_list:
listbox.insert(END, i[0])
#n = cfg_files_lists[group_titles[group.current()][1]][2]
listbox.see(n)
listbox.activate(n)
listbox.select_set(n)
listbox.event_generate("<<ListboxSelect>>")
#write_last_roms_file()
l = len(games_list)
if l == 1:
len_roms = "1 rom"
else:
len_roms = str(l) + " roms"
else:
#listbox.insert(END, "- empty -")
img = ImageTk.PhotoImage(PIL.Image.open(mame_logo))
lbl.configure(image = img)
lbl.image = img
lbl.update()
game_desc.configure(text = "...")
game_desc.update()
len_roms = str(len(games_list)) + " roms"
last_group = cur
search_ent.delete(0, END)
search_lbl.configure(text = " ")
search_lbl.update()
rom_lbl.configure(text = len_roms)
rom_lbl.update()
listbox.focus_set()
# started by the Add to group button
# if there's no other group then all games group, it opens the entry field to insert a new rom group
# if there's only one group other then tha all games group, the rom selected is automatically added to that one group
def adding_rom(rom):
if len(group_titles) == 1:
new_group_win()
if len(group_titles) == 2:
add_to_group(1, rom)
if len(group_titles) > 2:
add_to_group_win(rom)
def add_to_group(num, rom):
p = group_titles[num][1]
#print "add_to_group", p
if read_conf(p, rom[1], None):
print("\"" + rom[0] + "\" already in group " + group_titles[num][0])
else:
with open(p, "ab") as write:
write.write("\n" + rom[1])
cfg_files_lists[p][0].append(rom)
cfg_files_lists[p][1].append(rom[1])
print rom[1], "added to", p
if len(group_titles) > 2:
win_close(agw)
listbox.focus_set()
listbox.select_set(games_list.index(rom))
#listbox.event_generate("<<ListboxSelect>>")
global last_add_to_group
last_add_to_group = num - 1
# window with a listbox with rom groups
def add_to_group_win(rom):
global agw
global last_add_to_group
try:
last_add_to_group
except NameError:
last_add_to_group = 0
agw = Toplevel(root)
agw.grab_set()
agw.wm_title(w_title)
agw.protocol('WM_DELETE_WINDOW', lambda: win_close(agw))
frame = Frame(agw, width=90, height=50, bd=1)
frame.pack()
lb = Listbox(frame, height=10, width=20)
for i in group_titles[1:]:
lb.insert(END, i[0])
lb.pack(side=LEFT, fill=X, padx=5)
lb.bind('<Return>', (lambda event: add_to_group(lb.curselection()[0] + 1, rom)))
lb.bind('<KP_Enter>', (lambda event: add_to_group(lb.curselection()[0] + 1, rom)))
lb.bind('<Double-1>', (lambda event: add_to_group(lb.curselection()[0] + 1, rom)))
sb = Scrollbar(frame, orient=VERTICAL, command=lb.yview)
sb.pack(side=LEFT, fill=Y)
lb.configure(yscrollcommand=sb.set)
lb.focus_set()
lb.activate(last_add_to_group)
lb.select_set(last_add_to_group)
lb.see(last_add_to_group)
btn = Button(frame, text="OK", command=lambda: add_to_group(lb.curselection()[0] + 1, rom))
btn.pack(side=BOTTOM, fill=Y)
btn.bind('<Return>', (lambda event: add_to_group(lb.curselection()[0] + 1, rom)))
btn.bind('<KP_Enter>', (lambda event: add_to_group(lb.curselection()[0] + 1, rom)))
agw.mainloop()
# started by the New Group button, it's just an entry field were it's possible to write a new group name
def new_group_win():
global ngw
ngw = Toplevel(root)
ngw.grab_set()
ngw.wm_title(w_title)
ngw.protocol('WM_DELETE_WINDOW', lambda: win_close(ngw))
frame = Frame(ngw, width=90, height=50, bd=1)
frame.pack()
label = Label(frame, text="New Group Name:")
label.grid(row=0, column=0, padx=5, pady=2)
entry = Entry(frame, background="white", width=20)
entry.grid(row=10, column=0, columnspan=2, padx=3, pady=3)
entry.focus_set()
entry.bind('<Button-3>',rClicker, add='')
entry.bind('<Return>', (lambda event: new_group(entry.get())))
entry.bind('<KP_Enter>', (lambda event: new_group(entry.get())))
btn = Button(frame, text="OK", command=lambda: new_group(entry.get()))
btn.grid(row=20, column=10, pady=3, padx=3)
btn.bind('<Return>', (lambda event: new_group(entry.get())))
btn.bind('<KP_Enter>', (lambda event: new_group(entry.get())))
ngw.mainloop()
# takes what's written in the entry field and creates a new file and a new entry in the combobox list
def new_group(name):
gt = [x[0].lower().strip() for x in group_titles]
if not name.lower().strip() in gt:
n = urlify(name)
f = os.path.join(script_path, "mame_launcher_" + n + ".cfg")
num = 0
while os.path.isfile(f):
str_num = str(num).zfill(3)
#print "str_num", str_num
f = os.path.join(script_path, n + "_" + str_num + ".cfg")
num = num + 1
with open(f, "wb") as w:
w.write(title_group_file + " " + name)
print f, "created"
group_titles.append([name, f])
group_titles.remove([all_games_name, all_games_file])
group_titles.sort(key=by_zero)
group_titles.insert(0, [all_games_name, all_games_file])
group['values'] = [x[0] for x in group_titles]
group.update()
else:
print("Group " + name + " already exists")
win_close(ngw)
listbox.focus_set()
def win_close(w):
w.quit()
w.destroy()
# populates the combobox list
def group_titles_combobox():
n = []
gt = []
for f in os.listdir(script_path):
cf = os.path.join(script_path, f)
if os.path.isfile(cf) and cf.lower().endswith(".cfg") and not cf.lower() == all_games_file.lower():
with open(cf, "rb") as r:
try:
first = next(r)
except StopIteration:
#print "empty file"
continue
else:
if first.strip().lower().startswith(title_group_file):
cf_t = first.split(":")
t = cf_t[1].strip()
if not t in n:
gt.append([t, cf])
n.append(t)
if len(gt) > 0:
gt.sort(key=by_zero)
gt.insert(0, [all_games_name, all_games_file])
print "Rom groups:", gt
return gt
# repeats the initial scan for roms like the main description and all games files were not present
def rescan_all_games():
first_games_listing("force")
#print group_var.get()
global last_group
last_group = None
change_listbox()
# runs the -verifyroms command for each rom present in all games group and creates a new group
def working_games_scan():
global last_group
gl = cfg_files_lists[all_games_file][1]
if len(gl) > 0:
try:
del cfg_files_lists[working_games_file]
except KeyError:
print "Working games not present"
with open(working_games_file, "wb") as w:
w.write(working_games_title)
for rom in gl:
try:
subprocess.check_call((mame_exe, "-verifyroms", rom), shell=False)
except subprocess.CalledProcessError:
#print "not valid"
gl.remove(rom)
else:
#print "rom ok"
w.write("\n" + rom)
group_titles.append([working_games_name, working_games_file])
group_titles.remove([all_games_name, all_games_file])
group_titles.sort(key=by_zero)
group_titles.insert(0, [all_games_name, all_games_file])
group['values'] = [x[0] for x in group_titles]
num = group_titles.index([working_games_name, working_games_file])
group.current(num)
group.update()
last_group = None
change_listbox()
else:
print "No roms to verify"
# checks if the active group in the combobox is associated with the path provided
def active_group(path):
#print "act",group_var.get()
#print path
for i in group_titles:
#print i
if group_var.get() == i[0] and path == i[1]:
return True
else:
return False
def delete_group():
if group_var.get() == all_games_name or group_titles[group.current()][1] == all_games_file:
print("\"" + all_games_name + "\" can\'t be deleted")
else:
global last_group
n = group_var.get()
ask = tkMessageBox.askquestion(parent=root, title=w_title, message="Delete group " + n + "?")
if ask == "yes":
p = group_titles[group.current()][1]
try:
del cfg_files_lists[p]
except KeyError:
print p, "not present in cfg_files_lists"
group_titles.remove([n, p])
group['values'] = [x[0] for x in group_titles]
last_group = None
group.current(0)
group.update()
change_listbox()
try:
os.remove(p)
except (IOError, OSError) as e:
print e
# deletes a single entry from a rom group
def delete_from_group():
if group_var.get() == all_games_name or group_titles[group.current()][1] == all_games_file:
print("\"" + all_games_name + "\" can\'t be modified")
else:
#l = len(games_list)
if len(games_list) > 0:
n = listbox.curselection()[0]
#print n
rom = games_list[n]
#print rom
p = group_titles[group.current()][1]
listbox.delete(n, n)
del games_list[n]
# deleting the item in games_list causes the item to disapper from the correspondig list in the dictionary associated, i think because they're the same list.
# I didn't know this. Anyhow, I leave the try because I'm not sure it works every time.
try:
cfg_files_lists[p][0].remove(rom)
except ValueError:
print rom, "not in rom group"
try:
cfg_files_lists[p][1].remove(rom[1])
except ValueError:
print rom, "not in rom group"
with open(p, "rb") as inp:
input = inp.read().splitlines()
input.remove(rom[1])
with open(p, "wb") as output:
for line in input:
output.write(line)
if not input.index(line) == len(input) - 1:
output.write("\n")
l = len(games_list)
if l == 1:
len_roms = "1 rom"
else:
len_roms = str(l) + " roms"
rom_lbl.configure(text = len_roms)
rom_lbl.update()
if l > 0:
if n == listbox.size():
n = n - 1
listbox.focus_set()
listbox.select_set(n)
listbox.activate(n)
listbox.event_generate("<<ListboxSelect>>")
# searches the string entered in the current rom group
def search():
global games_list
if len(games_list) > 0:
ins = search_ent.get().strip()
if len(ins) > 0:
print ins
temp_games_list = games_list
games_list = []
listbox.delete(0, END)
ins_s = ins.split()
for i in temp_games_list:
c = 0
for y in ins_s:
if y.lower() in i[0].lower():
c += 1
if c == len(ins_s):
#print i
games_list.append(i)
listbox.insert(END, i[0])
listbox.itemconfig(END, {'fg': 'blue'})
if len(games_list) == 1:
search_lbl.configure(text = "- Search: 1 result -")
search_lbl.update()
else:
search_lbl.configure(text = "- Search: " + str(len(games_list)) + " results -")
search_lbl.update()
#print games_list
#print temp_games_list
listbox.see(0)
listbox.focus_set()
listbox.select_set(0)
listbox.activate(0)
#listbox.event_generate("<<ListboxSelect>>")
# resets the search entry and displays the current rom group in full
def search_reset():
global last_group
last_group = None
change_listbox()
# writes a file with the cfg files path and the last rom selected for each path, tracks the last active group
def write_last_roms_file():
with open(last_roms_file, "wb") as w:
wc = csv.writer(w, delimiter=";")
for i in group_titles:
p = i[1]
try:
n = cfg_files_lists[p][2]
except KeyError:
#print p,"not listed yet"
continue
else:
if active_group(p):
wc.writerow([p, n, "active"])
else:
wc.writerow([p, n])
################################### Begin ###################################
w_title = "Python - MAME Launcher"
print w_title
# full path of the mame launcher script directory
script_path = os.path.dirname(os.path.realpath(__file__))
print("Script path: " + script_path)
# main cfg file for all roms detected in the rompath
all_games = "mame_launcher_all_games.cfg"
all_games_file = os.path.join(script_path, all_games)
# main cfg file with all rom names and descriptions, extracted from the mame executable with the command mame -ll
descs = "mame_launcher_descs.cfg"
descs_file = os.path.join(script_path, descs)
# cfg file for working roms only
working_games = "mame_launcher_working_games.cfg"
working_games_file = os.path.join(script_path, working_games)
cf = os.path.join(script_path, "mame_launcher.cfg")
# writing default configuration file if parameter is present
try:
if argv[1].lower() == "-wc" or argv[1].lower() == "--writeconf":
if os.path.isfile(cf):
print "Configuration file", cf, "already present."
while True:
ask = raw_input("Do you want to overwrite it? (y/n): ")
ask = ask.strip().lower()
if ask == "y":
break
if ask == "n":
print "Not overwriting"
exit(0)
print "Insert y or n"
print "Writing new configuration file...", cf
with open(cf, "wb") as conf:
conf.write("# some parameters for MAME launcher\n"
"# keep the formatting\n"
"[mame launcher]\n"
"# location of mame executable\n"
"mame_exe = /usr/games/mame\n"
"# name of picture to display as MAME logo, it has to be in the same directory as the MAME Launcher script\n"
"mame_logo = mame.png\n"
"# name of rom group with all games found in rompath, it will be displayed in the combobox\n"
"all_games_name = All Games\n"
"# name of rom group with all working games found in rompath, they're selected trough the -verifyroms command line\n"
"working_games_name = Working\n"
"# leave blank for loading the last group displayed the last time MAME Launcher was used\n"
"# or you can set a rom group cfg file name to always load it when MAME launcher starts\n"
"# e.g. last_group = mame_launcher_favorites.cfg\n"
"last_group")
print "Default configuration file written"
exit(0)
else:
print "\nUsage:\n"
print "Easy: with no arguments the script starts the MAME Launcher gui\n"
print "-wc, --writeconf : writes the standard configuration file, mame_launcher.cfg, in the current directory\n"
print "There\'s only the -wc argument available"
exit(1)
except IndexError:
#print "no arguments"
pass
print " "
# reading general configuration file if exists
if os.path.isfile(cf):
print "Parsing configuration file..."
c = ConfigParser.RawConfigParser(allow_no_value=True)
try:
c.read(cf)
except (ConfigParser.MissingSectionHeaderError, ConfigParser.ParsingError) as e1:
print "Configuration file error:", cf, e1
exit(1)
else:
try:
print "MAME executable..."
mame_exe = c.get('mame launcher', 'mame_exe')
print "OK:", mame_exe
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e2:
print e2
mame_exe = "/usr/games/mame"
print "Set default:", mame_exe
try:
print "MAME logo picture..."
mame_logo = c.get('mame launcher', 'mame_logo')
print "OK:", mame_logo
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e3:
print e3
mame_logo = os.path.join(script_path, "mame.png")
print "Set default:", mame_logo
try:
print "All Games name..."
all_games_name = c.get('mame launcher', 'all_games_name')
print "OK:", all_games_name
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e4:
print e4
all_games_name = "All games"
print "Set default:", all_games_name
try:
print "Working games name..."
working_games_name = c.get('mame launcher', 'working_games_name')
print "OK:", working_games_name
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e5:
print e5
working_games_name = "Working"
print "Set default:", working_games_name
try:
print "Load roms group..."
last_group = os.path.join(script_path, c.get('mame launcher', 'last_group'))
print "OK:", last_group
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e6:
print e6
last_group = None
print "Set default: dinamically selected from last_roms file"
except AttributeError:
last_group = None
print "Dinamically selected from last_roms file"
else:
print "Setting defaults..."
mame_exe = "/usr/games/mame"
print "MAME executable:", mame_exe
mame_logo = os.path.join(script_path, "mame.png")
print "MAME logo picture:", mame_logo
all_games_name = "All games"
print "All Games name:", all_games_name
working_games_name = "Working"
print "Working games name:", working_games_name
last_group = None
print "Load roms group: dinamically selected from last_roms file"
print " "
games_path, img_path = retrieve_paths()
print "Roms", games_path
print "Snapshots", img_path
# string to be written on the first line of every cfg file for rom group
title_group_file = "game group title:"
all_games_title = title_group_file + " " + all_games_name
working_games_title = title_group_file + " " + working_games_name
# this file contains the index number of the last game selcted and relative group
last_roms = "mame_launcher_last_roms.cfg"
last_roms_file = os.path.join(script_path, last_roms)
print " "
group_titles = group_titles_combobox()
print " "
first_games_listing(None)
cfg = [x[1] for x in group_titles]
if last_group is None or len(last_group) == 0 or not last_group in cfg:
last_group = all_games_file
#print last_group
games_list = cfg_files_lists[last_group][0]
games_list.sort(key=by_zero)
#print str(len(games_list)) + " roms"
########################### Beginning of main window ##########################
root = Tk()
root.wm_title(w_title)
root.geometry("760x650+300+200")
frame_left = Frame(root)
frame_left.pack(fill=Y, side=LEFT)
frame_right = Frame(root)
frame_right.pack(fill=BOTH, side=LEFT, expand=TRUE)
frame_search = Frame(frame_left, relief=SUNKEN, bd=1)
frame_search.pack(side=TOP, padx=4, pady=4)
search_ent = Entry(frame_search, background="white", width=20)
search_ent.grid(row=0, column=0, padx=4, pady=2)
search_ent.bind('<Return>', (lambda event: search()))
search_ent.bind('<KP_Enter>', (lambda event: search()))
search_ent.bind('<Button-3>',rClicker, add='')
search_btn = Button(frame_search, text='Search', command=search)
search_btn.grid(row=0, column=10, padx=4, pady=2)
search_btn.bind('<Return>', (lambda event: search()))
search_btn.bind('<KP_Enter>', (lambda event: search()))
reset_btn = Button(frame_search, text='Reset', command=search_reset)
reset_btn.grid(row=0, column=20, padx=4, pady=2)
reset_btn.bind('<Return>', (lambda event: search_reset()))
reset_btn.bind('<KP_Enter>', (lambda event: search_reset()))
search_lbl = Label(frame_search, text=" ")
search_lbl.grid(row=10, column=0, padx=4, pady=2)
frame_list = Frame(frame_left, relief=SUNKEN, bd=1)
listbox = Listbox(frame_list, height=60, width=36)
if len(games_list) > 0:
for i in games_list:
listbox.insert(END, i[0])
listbox.pack(side=LEFT, fill=BOTH, padx=5)
sb = Scrollbar(frame_list, orient=VERTICAL, command=listbox.yview)
sb.pack(side=LEFT, fill=Y)
listbox.configure(yscrollcommand=sb.set)
listbox.bind('<<ListboxSelect>>', change_img)
listbox.bind('<Return>', (lambda event: game_run()))
listbox.bind('<Double-1>', (lambda event: game_run()))
listbox.bind('<KP_Enter>', (lambda event: game_run()))
ScrollingArea(root).add_scrolling(listbox, yscrollbar=sb)
frame_list.pack(side=TOP, padx=4, pady=4)
# http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame I adapted it to my situation
# create a canvas object and a vertical and horizontal scrollbar for scrolling it
vscrollbar = Scrollbar(frame_right, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
hscrollbar = Scrollbar(frame_right, orient=HORIZONTAL)
hscrollbar.pack(fill=X, side=BOTTOM, expand=FALSE)
canvas = Canvas(frame_right, bd=0, highlightthickness=0, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set)
canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
vscrollbar.config(command=canvas.yview)
hscrollbar.config(command=canvas.xview)
# reset the view
canvas.xview_moveto(0)
canvas.yview_moveto(0)
# create a frame inside the canvas which will be scrolled with it
interior = Frame(canvas)
interior.pack(side=LEFT, expand=TRUE)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW)
# updates the scrollbars to match the size of the inner frame
def configure_scroll(event):
can = ((root.winfo_width() - frame_left.winfo_width())-13, root.winfo_height()-13)
#print "can",can
canvas.config(width=can[0])
canvas.config(height=can[1])
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
#print "size",size
if size[0] >= can[0] and size[1] >= can[1]:
canvas.config(scrollregion=[0, 0, size[0], size[1]])
if size[0] > can[0] and size[1] < can[1]:
canvas.config(scrollregion=[0, 0, size[0], can[1]])
if size[0] < can[0] and size[1] > can[1]:
canvas.config(scrollregion=[0, 0, can[0], size[1]])
if size[0] <= can[0] and size[1] <= can[1]:
canvas.config(scrollregion=[0, 0, can[0], can[1]])
interior.update_idletasks()
canvas.update_idletasks()
interior.bind('<Configure>', configure_scroll)
canvas.bind('<Configure>', configure_scroll)
ScrollingArea(root).add_scrolling(canvas, xscrollbar=hscrollbar, yscrollbar=vscrollbar)
#frame_img = Frame(frame_right.interior, relief=GROOVE, bd=1)
frame_img = Frame(interior, relief=GROOVE, bd=1)
frame_img.pack(fill=X, side=TOP, padx=4, pady=4)
#frame_img.pack(side=TOP, padx=4, pady=4)
img = ImageTk.PhotoImage(PIL.Image.open(mame_logo))
lbl = Label(frame_img, image=img)
lbl.image = img
lbl.pack(side=TOP, padx=4, pady=4)
game_desc = Label(frame_img, text="...")
game_desc.pack(side=TOP, padx=4, pady=4)
#frame_group = Frame(frame_right.interior, relief=GROOVE, bd=1)
frame_group = Frame(interior, relief=GROOVE, bd=1)
frame_group.pack(fill=X, side=TOP, padx=4, pady=4)
#frame_group.pack(side=TOP, padx=4, pady=4)
add_btn = Button(frame_group, text='Add to group...', command=lambda: adding_rom(games_list[listbox.curselection()[0]]))
add_btn.grid(row=0, column=0, padx=5, pady=5)
add_btn.bind('<Return>', (lambda event: adding_rom(games_list[listbox.curselection()[0]])))
add_btn.bind('<KP_Enter>', (lambda event: adding_rom(games_list[listbox.curselection()[0]])))
group_var = StringVar()
group_var.set(all_games_name)
group = ttk.Combobox(frame_group, textvariable=group_var, width=20)
group.bind("<<ComboboxSelected>>", (lambda event: change_listbox()))
group.grid(row=0, column=10, padx=5, pady=5)
group.configure(state="readonly")
group['values'] = [x[0] for x in group_titles]
group.update()
new_btn = Button(frame_group, text='New group...', command=new_group_win)
new_btn.grid(row=0, column=20, padx=5, pady=5)
new_btn.bind('<Return>', (lambda event: new_group_win()))
new_btn.bind('<KP_Enter>', (lambda event: new_group_win()))
del_rom_btn = Button(frame_group, text='Delete from group', command=delete_from_group)
del_rom_btn.grid(row=10, column=0, padx=5, pady=5)
del_rom_btn.bind('<Return>', (lambda event: delete_from_group()))
del_rom_btn.bind('<KP_Enter>', (lambda event: delete_from_group()))
rom_lbl = Label(frame_group, text=str(len(games_list)) + " roms")
rom_lbl.grid(row=10, column=10, padx=4, pady=5, sticky=N)
del_btn = Button(frame_group, text='Delete group', command=delete_group)
del_btn.grid(row=10, column=20, padx=5, pady=5)
del_btn.bind('<Return>', (lambda event: delete_group()))
del_btn.bind('<KP_Enter>', (lambda event: delete_group()))
#frame_btn = Frame(frame_right.interior, relief=GROOVE, bd=1)
frame_btn = Frame(interior, relief=GROOVE, bd=1)
frame_btn.pack(fill=X, side=TOP, padx=4, pady=4)
#frame_btn.pack(side=TOP, padx=4, pady=4)
paramters_info = Label(frame_btn, text="Additional parameters\nfor executing MAME")
paramters_info.grid(row=0, column=0, padx=3, pady=3)
clear_btn = Button(frame_btn, text='Rescan all games', command=rescan_all_games)
clear_btn.grid(row=0, column=10, padx=5, pady=3)
clear_btn.bind('<Return>', (lambda event: rescan_all_games()))
clear_btn.bind('<KP_Enter>', (lambda event: rescan_all_games()))
parameters = Entry(frame_btn, background="white", width=15)
parameters.grid(row=10, column=0, padx=3, pady=3)
parameters.bind('<Return>', (lambda event: game_run()))
parameters.bind('<KP_Enter>', (lambda event: game_run()))
parameters.bind('<Button-3>',rClicker, add='')
working_btn = Button(frame_btn, text='Scan for working games', command=working_games_scan)
working_btn.grid(row=10, column=10, padx=8, pady=3, sticky=E)
working_btn.bind('<Return>', (lambda event: working_games_scan()))
working_btn.bind('<KP_Enter>', (lambda event: working_games_scan()))
run_btn = Button(frame_btn, text='Play', command=game_run)
run_btn.grid(row=20, column=0, padx=3, pady=6)
run_btn.bind('<Return>', (lambda event: game_run()))
run_btn.bind('<KP_Enter>', (lambda event: game_run()))
for i in group_titles:
if i[1] == last_group:
group.current(group_titles.index(i))
group.update()
try:
n = games_list.index(descs_list[cfg_files_lists[last_group][2]])
except (ValueError, KeyError):
n = 0
listbox.focus_set()
listbox.see(n)
listbox.select_set(n)
listbox.activate(n)
listbox.event_generate("<<ListboxSelect>>")
root.mainloop()
write_last_roms_file()
exit(0)
Offline
Pages: 1