Source

1w6_balance / 1w6-balance.py

Full commit
#!/usr/bin/env python
# encoding: utf-8

"""A simple GUI for the battle_Probs tree.

Info: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qtgui.html

TODO: Mit event manager arbeiten: Nach der letzten Anzeige direkt den nächsten Wert berechnen und erst anzeigen, sobald der Timer kommt -> besser in Zeit sein. 
TODO: Daten in einem Thread erzeugen (das GUI nicht blockieren)
"""

from PyQt4.QtGui import *
from PyQt4.QtCore import SIGNAL, SLOT, QTimer, Qt

from sys import argv, exit
from os import listdir
from os.path import join
from time import sleep
from battle_probability_tree import generate_tree, read_chars_from_files
from battle_probability_tree import triple_die as die


### Options

# Initial chars
Char1 = "chars/very_good.txt"
Char2 = "chars/average.txt"

# Number of rouns to show
MAX_TURNS = 6

# Prepare app
app = QApplication(argv)

### The GUI elements

## First the layouts: main and head
main_widget = QWidget()
main_layout = QVBoxLayout(main_widget)
head = QWidget()
head_layout = QHBoxLayout(head)
main_layout.addWidget(head)

## Now the head with the char entry lines
# char 1: 
char1_v_layout = QVBoxLayout()
char1_h_layout = QHBoxLayout()
char1_text = QLineEdit(Char1)
char1_text.setToolTip("The path to the file of the second character. Press enter to evaluate")
#char1_select = QToolButton()
char1_label = QLabel(" Char 1")
char1_h_layout.addWidget(char1_text)
#char1_h_layout.addWidget(char1_select)
char1_v_layout.addLayout(char1_h_layout)
char1_v_layout.addWidget(char1_label)
head_layout.addLayout(char1_v_layout)

char2_v_layout = QVBoxLayout()
char2_h_layout = QHBoxLayout()
char2_text = QLineEdit(Char2)
char2_text.setToolTip("The path to the file of the first character. Press enter to evaluate")
#char2_select = QToolButton()
char2_label = QLabel(" Char 2")
char2_h_layout.addWidget(char2_text)
#char2_h_layout.addWidget(char2_select)
char2_v_layout.addLayout(char2_h_layout)
char2_v_layout.addWidget(char2_label)
head_layout.addLayout(char2_v_layout)

# And the compare button
compare = QPushButton("Compare")
compare_number_of_turns = QSpinBox()
compare_number_of_turns.setRange(1, 12)
compare_number_of_turns.setValue(3)
compare_number_of_turns.setToolTip("The number of turns to calculate - Don't set too high! Eats memory to the power of 6!")
compare_lay = QVBoxLayout()
compare_lay.addWidget(compare)
compare_lay.addWidget(compare_number_of_turns)
head_layout.addLayout(compare_lay)

## Now the text output
text = QPlainTextEdit("""--- 
Select and set chars on the left. 
Edit their stats below
and start the close combat above.

By default the battle runs for 3 actions (1,5 seconds). 
You can adjust the duration 
with the field below the Compare button, 
but beware: Time and memory consumption 
rise to the power of 6! 

Also you can use different charfiles 
by typing the path to a file into 
one of the character text fields
and hitting enter. 

Happy playing!
--- """)
main_layout.addWidget(text)

## And the Dock widget to the left
dock = QDockWidget()
dock_scrolling = QScrollArea()
dock.setWidget(dock_scrolling)
dock_area = QScrollArea()

## Now the dock should have a Vertical Layout
dock_layout = QVBoxLayout(dock_scrolling)
# And I want to put a list widget and a char selector into it.
chars = [char for char in listdir("chars") if char.endswith(".txt")]
dock_list = QListWidget()
dock_layout.addWidget(dock_list)
dock_list.addItems(chars)

# The buttons to set the char
dock_button_layout = QHBoxLayout()
dock_radio_1 = QPushButton("set char 1")
dock_radio_2 = QPushButton("set char 2")
dock_layout.addLayout(dock_button_layout)
dock_button_layout.addWidget(dock_radio_1)
dock_button_layout.addWidget(dock_radio_2)

## And finally the dock widget for editing the chars. 

edit_dock = QDockWidget()
edit_dock_scrolling = QScrollArea()
edit_dock.setWidget(edit_dock_scrolling)
edit_dock_area = QScrollArea()

# horizontal layout for the text fields
edit_chars_layout = QHBoxLayout(edit_dock_scrolling)

# the text we need
def read_char(path): 
    """Read a char from the given path"""
    f = open(path)
    chartext = f.read()
    f.close()
    return chartext

chartext1 = read_char(char1_text.text())
chartext2 = read_char(char2_text.text())

# The edit fields with data
edit_char1 = QPlainTextEdit()
edit_char1.setPlainText(chartext1)
edit_char2 = QPlainTextEdit()
edit_char2.setPlainText(chartext2)

edit_chars_layout.addWidget(edit_char1)
edit_chars_layout.addWidget(edit_char2)
#main_layout.addLayout(edit_chars_layout)

### Interaction

# Change the Char paths

def switch_to_char_1 ():
	"""Select the second char for editing via the list."""
	selected_item = dock_list.item(dock_list.currentRow())
	if selected_item is not None:
		char1_text.setText(join("chars", str(selected_item.text())))
		edit_char1.setPlainText(read_char(char1_text.text()))

def switch_to_char_2 ():
	"""Select the second char for editing via the list."""
	selected_item = dock_list.item(dock_list.currentRow())
	if selected_item is not None: 
		char2_text.setText(join("chars", str(selected_item.text())))
		edit_char2.setPlainText(read_char(char2_text.text()))

dock_radio_1.connect(dock_radio_1, SIGNAL("clicked()"), switch_to_char_1)
dock_radio_2.connect(dock_radio_2, SIGNAL("clicked()"), switch_to_char_2)

# Load the char anew on presing enter in the char line edit field
def read_char1_content(): 
    """Read the contents of the file for the char and update the edit_char field."""
    edit_char1.setPlainText(read_char(char1_text.text()))

def read_char2_content(): 
    """Read the contents of the file for the char and update the edit_char field."""
    edit_char2.setPlainText(read_char(char2_text.text()))

char1_text.connect(char1_text, SIGNAL("returnPressed()"), read_char1_content)

char2_text.connect(char2_text, SIGNAL("returnPressed()"), read_char2_content)

# The main window
class MainWindow(QMainWindow): 
	def __init__(self, parent=None): 
		QWidget.__init__(self, parent)
		self.setCentralWidget(main_widget)
		self.addDockWidget(Qt.BottomDockWidgetArea, edit_dock)
		self.addDockWidget(Qt.LeftDockWidgetArea, dock)
	
		self.resize(800, 600)
		self.show()


# timer for doing 1 update per second
timer = QTimer()
timer.setInterval(1000) # milliseconds
# Stop the timer when the app closes
app.connect(app, SIGNAL("lastWindowClosed()"), timer, SLOT("stop()"))


# Updating the text

turns = 1
txt = ""
cha1 = ""
cha2 = ""
win = 1
lose = 0

# text display
def update_text():
	"""Add new results to the shown text."""
	## First all the global variables we need
	global turns
	global txt
	global cha1
	global cha2
	global win
	global lose
	global timer
	global MAX_TURNS

	MAX_TURNS = compare_number_of_turns.value()

	# Calculate at most MAX_TURNS turns
	if turns > MAX_TURNS:
		timer.stop()
		return False # enough turns

	# Only get the chars as first action once
	if turns == 1 or not cha1 or not cha2:
		try: 
			cha1 = eval(str(edit_char1.toPlainText()))
			cha2 = eval(str(edit_char2.toPlainText()))
		except:
			 text.setPlainText( "Can't find at least one of the chars" )
			 timer.stop()
			 return False

		win, lose = generate_tree(chars=[cha1, cha2], number_of_turns=turns, die=die)

	t = "After " + str(turns) + " battles: \nWin: " + str(win) + " - Lose: " + str(lose) + " - Draw: " + str(1.0 - (win+lose))
	turns += 1
	if txt: 
		txt += "\n\n" + t
	else: txt = t
	text.setPlainText( txt )
	
	# Now get the new data
	win, lose = generate_tree(chars=[cha1, cha2], number_of_turns=turns, die=die)

def start_updating():
	"""Start updating the text."""
	global txt
	global turns
	txt = ""
	turns = 1
	timer.start()
	timer.connect(timer, SIGNAL("timeout()"), update_text)

# Start the calculation when the Compare Button is clicked
compare.connect(compare, SIGNAL("clicked()"), start_updating) 


if __name__ == "__main__": 
	# Prepare window
	main_window = MainWindow()

	# run it all
	code = app.exec_()
	timer.stop()
	exit(code)