This is my Hangman game that I created using PyQt5 and Python 3.5 on my Windows 7 machine. What do you think? Is there anything you thing I could change/improve in terms of code or visually (font, font size, font color, image size, button size)?
(Images)
from sys import (exit, argv)
from PyQt5.QtCore import (Qt, QRegExp)
from PyQt5.QtWidgets import (QToolTip, QPushButton, QApplication, QWidget, QLabel, QLineEdit)
from PyQt5.QtGui import (QIcon, QPixmap, QFont, QRegExpValidator)
from random import choice
from time import sleep
WORDS = ['Captivity', 'America', 'Europe', 'Federal', 'Gluten', 'Ridiculous', 'Automatic', 'Television', 'Difficult', 'Severe', 'Interesting', 'Indonesia', 'Industrial',
'Automotive', 'President', 'Terrestrial', 'Academic', 'Comedic', 'Comical', 'Genuine', 'Suitcase', 'Vietnam', 'Achievement', 'Careless', 'Monarchy', 'Monetary',
'Quarantine', 'Supernatural', 'Illuminate', 'Optimal', 'Application', 'Scientist', 'Software', 'Hardware', 'Program', 'Colonial', 'Algorithm', 'Intelligent',
'Electricity', 'Verification', 'Broadband', 'Quality', 'Validation', 'Online', 'Telephone', 'Dictionary', 'Keyboard', 'China', 'London', 'Jamaica', 'Biology',
'Chemistry', 'History', 'Historian', 'Africa', 'Mathematics', 'Computer', 'Literature', 'Gravity', 'Guitar', 'Violin', 'Illuminate', 'England', 'China', 'Japan',
'Canada', 'Suitcase', 'Wireless', 'Internet']
HANGMAN_PARMS = 100, 200, Qt.KeepAspectRatio, Qt.FastTransformation
class hangman(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.background = QLabel(self)
self.background.setPixmap(QPixmap('background.jpg').scaled(201, 352, Qt.IgnoreAspectRatio, Qt.FastTransformation))
self.background.move(-1, -1)
self.number = 1
self.image = QLabel(self)
self.image.setPixmap(QPixmap('hangman_{}.png'.format(self.number)).scaled(*HANGMAN_PARMS))
self.image.move(60, 0.5)
self.word = choice(WORDS)
blank_word = '_ ' * len(self.word)
blank_word.rstrip()
self.blank_word_label = QLabel(blank_word, self)
font1 = self.blank_word_label.font()
font1.setPointSize(14)
self.blank_word_label.setFont(font1)
self.blank_word_label.setFixedWidth(200)
self.blank_word_label.setToolTip('Attempt to fill in the blanks')
self.blank_word_label.move(0,200)
self.blank_word_label.setAlignment(Qt.AlignCenter)
self.guessed_letters = ''
self.btn = QPushButton('Check', self)
self.btn.setFont(QFont('SansSerif', 20))
self.btn.setToolTip('Click to check if the entered letter is in the word')
self.btn.clicked.connect(self.check_letter)
self.btn.resize(102, 43)
self.btn.move(99, 228)
self.entered_letter = QLineEdit(self)
regex = QRegExp("[a-z-A-Z_]+")
validator = QRegExpValidator(regex)
self.entered_letter.setValidator(validator)
font2 = self.entered_letter.font()
font2.setPointSize(24)
self.entered_letter.setFont(font2)
self.entered_letter.setMaxLength(1)
self.entered_letter.setToolTip('Enter a letter and check if it is in the word')
self.entered_letter.setFocus(True)
self.entered_letter.returnPressed.connect(self.check_letter)
self.entered_letter.resize(100, 43)
self.entered_letter.move(0.5, 228)
self.correct_or_incorrect = QLabel(self)
self.correct_or_incorrect.move(1, 232)
self.correct_or_incorrect.setVisible(False)
self.you_lose = QLabel(self)
self.you_lose.setPixmap(QPixmap('game_over.jpg').scaled(200, 160, Qt.IgnoreAspectRatio, Qt.FastTransformation))
self.you_lose.move(0.5, 0.5)
self.you_lose.setVisible(False)
self.you_win = QLabel(self)
self.you_win.setPixmap(QPixmap('congratulations.jpg').scaled(200, 160, Qt.IgnoreAspectRatio, Qt.FastTransformation))
self.you_win.move(0.5, 0.5)
self.you_win.setVisible(False)
self.correct_word = QLabel('The word was:', self)
font1 = self.correct_word.font()
font1.setPointSize(14)
self.correct_word.setFont(font1)
self.correct_word.setFixedWidth(200)
self.correct_word.move(0,170)
self.correct_word.setAlignment(Qt.AlignCenter)
self.correct_word.setVisible(False)
self.replay_btn = QPushButton('Play Again', self)
self.replay_btn.setFont(QFont('SansSerif', 15))
self.replay_btn.setToolTip('Click to play another game of Hangman')
self.replay_btn.clicked.connect(self.replay)
self.replay_btn.resize(202, 33)
self.replay_btn.move(-1, 239.8)
self.replay_btn.setVisible(False)
QToolTip.setFont(QFont('SansSerif', 10))
self.setGeometry(1390, 30, 200, 270)
self.setFixedSize(self.size())
self.setWindowTitle('Hangman')
self.setWindowIcon(QIcon('icon.png'))
self.show()
def check_letter(self):
if self.entered_letter.text().lower() in self.word.lower():
self.guessed_letters += self.entered_letter.text().lower()
self.correct_or_incorrect.setPixmap(QPixmap('correct.png').scaled(40, 40, Qt.IgnoreAspectRatio, Qt.FastTransformation))
self.correct_or_incorrect.setVisible(True)
QApplication.processEvents()
sleep(0.1)
self.correct_or_incorrect.setVisible(False)
QApplication.processEvents()
else:
self.number += 1
self.image.setPixmap(QPixmap('hangman_{}.png'.format(self.number)).scaled(*HANGMAN_PARMS))
self.correct_or_incorrect.setPixmap(QPixmap('incorrect.png').scaled(40, 40, Qt.IgnoreAspectRatio, Qt.FastTransformation))
self.correct_or_incorrect.setVisible(True)
QApplication.processEvents()
sleep(0.1)
self.correct_or_incorrect.setVisible(False)
QApplication.processEvents()
blank_word = ''
for i in self.word:
if i.lower() in self.guessed_letters:
blank_word += i
else:
blank_word += '_ '
blank_word.rstrip()
self.blank_word_label.setText(blank_word)
self.entered_letter.setText('')
self.entered_letter.setFocus(True)
if self.number == 7:
self.blank_word_label.setText(self.word)
self.image.setVisible(False)
self.entered_letter.setVisible(False)
self.btn.setVisible(False)
self.you_lose.setVisible(True)
self.correct_word.setVisible(True)
self.replay_btn.setVisible(True)
if blank_word == self.word:
self.image.setVisible(False)
self.entered_letter.setVisible(False)
self.btn.setVisible(False)
self.you_win.setVisible(True)
self.correct_word.setVisible(True)
self.replay_btn.setVisible(True)
def replay(self):
self.number = 1
self.image.setPixmap(QPixmap('hangman_{}.png'.format(self.number)).scaled(*HANGMAN_PARMS))
self.word = choice(WORDS)
blank_word = '_ ' * len(self.word)
blank_word.rstrip()
self.blank_word_label.setText(blank_word)
self.guessed_letters = ''
self.you_lose.setVisible(False)
self.you_win.setVisible(False)
self.correct_word.setVisible(False)
self.replay_btn.setVisible(False)
self.image.setVisible(True)
self.entered_letter.setVisible(True)
self.btn.setVisible(True)
self.entered_letter.setFocus(True)
if __name__ == '__main__':
app = QApplication(argv)
ex = hangman()
ex.show()
exit(app.exec_())
4 Answers 4
I would like to point out some minor things about your style. Following PEP8 style guide makes code easier to read :)
Class names should normally use the CapWords convention.
Imports should be grouped in the following order:
standard library imports
related third party imports
local application/library specific imports
So your imports would be:
from sys import exit, argv
from random import choice
from time import sleep
from PyQt5.QtCore import Qt, QRegExp
from PyQt5.QtWidgets import QToolTip, QPushButton, QApplication, QWidget,
QLabel, QLineEdit
from PyQt5.QtGui import QIcon, QPixmap, QFont, QRegExpValidator
You should also add a few docstrings. You can check for other minor mistakes with this tool: http://pep8online.com/
-
1\$\begingroup\$ I personally also order imports alphabetically most of the time within each of the categories you listed in 1, 2, 3 \$\endgroup\$airstrike– airstrike2017年08月12日 19:36:59 +00:00Commented Aug 12, 2017 at 19:36
Very nice, there are no many things to improve.
I guess that this part of your code
WORDS = ['Captivity', 'America', 'Europe', 'Federal', 'Gluten', 'Ridiculous', 'Automatic', 'Television', 'Difficult', 'Severe', 'Interesting', 'Indonesia', 'Industrial',
'Automotive', 'President', 'Terrestrial', 'Academic', 'Comedic', 'Comical', 'Genuine', 'Suitcase', 'Vietnam', 'Achievement', 'Careless', 'Monarchy', 'Monetary',
'Quarantine', 'Supernatural', 'Illuminate', 'Optimal', 'Application', 'Scientist', 'Software', 'Hardware', 'Program', 'Colonial', 'Algorithm', 'Intelligent',
'Electricity', 'Verification', 'Broadband', 'Quality', 'Validation', 'Online', 'Telephone', 'Dictionary', 'Keyboard', 'China', 'London', 'Jamaica', 'Biology',
'Chemistry', 'History', 'Historian', 'Africa', 'Mathematics', 'Computer', 'Literature', 'Gravity', 'Guitar', 'Violin', 'Illuminate', 'England', 'China', 'Japan',
'Canada', 'Suitcase', 'Wireless', 'Internet']
was much time-consuming for typing many of apostrophes and commas - the faster approach is
WORDS = ("Captivity America Europe Federal Gluten Ridiculous "
"Automatic Television Difficult Severe Interesting Indonesia "
"Industrial Automotive President Terrestrial Academic Comedic "
"Comical Genuine Suitcase Vietnam Achievement Careless "
"Monarchy Monetary Quarantine Supernatural Illuminate Optimal "
"Application Scientist Software Hardware Program Colonial "
"Algorithm Intelligent Electricity Verification Broadband Quality "
"Validation Online Telephone Dictionary Keyboard China "
"London Jamaica Biology Chemistry History Historian "
"Africa Mathematics Computer Literature Gravity Guitar "
"Violin Illuminate England China Japan Canada "
"Suitcase Wireless Internet "
).split()
Note that it is one very long literal string written part by part (an closed in parentheses for avoiding \
at the ends of the lines).
-
3\$\begingroup\$ Or put them in a separate file entirely. CSV or DB, etc. \$\endgroup\$Quelklef– Quelklef2017年08月12日 19:53:49 +00:00Commented Aug 12, 2017 at 19:53
-
\$\begingroup\$ Why not use one triple-quoted string spanning multiple lines? \$\endgroup\$200_success– 200_success2017年08月13日 13:36:17 +00:00Commented Aug 13, 2017 at 13:36
It looks good, but I'm not a Qt expert.
I would have used model/view separation: the game code would be in one file and the UI code would be in a logically completely separated file. For example, from the same game code you can write a pure text interface, or a Qt interface.
Also I would put the word list in a text file.
I don't much like HANGMAN_PARMS
as a tuple. Why not separate variables or a dictionary? That way their meanings would be more explicit.
I don't see the point in initUI
- why isn't that code in __init__
.
blank_word.rstrip()
does nothing, as.rstrip()
does not modify the original string. You could doblank_word = blank_word.rstrip()
, or simply put the.rstrip()
on the line above. \$\endgroup\$