How do I get buttons to do things for gui hangman - user-interface

I am new to python and have decided to create a hangman game as a GUI with tkinter. I have stumbled across this problem where I can't get my buttons to do what I want. Specifically, I want them to check if the letter is in the mystery word and print it in the position of the letter in the word if it is. Any tips or suggestions will be helpful.
from tkinter import *
import random
from tkinter import Label, Canvas
root = Tk()
root.title("Hangman Project")
c: Canvas = Canvas(root, height=500, width=500, bg="white")
c.pack()
height = 400
width = 400
head = c.create_oval(70, 70, 130, 130)
neck = c.create_line(100, 130, 100, 150)
body = c.create_rectangle(70, 150, 130, 220, fill="orange")
arm1 = c.create_line(130, 170, 150, 120)
arm2 = c.create_line(70, 170, 50, 120)
leg1 = c.create_line(110, 220, 145, 275)
leg2 = c.create_line(90, 220, 55, 275)
def gallows():
c.create_line(width / 40, height / 8, width / 4, height / 8)
c.create_line(width / 40, height / 8, width / 40, height / 1.6)
c.create_line(0, height / 1.6, width / 20, height / 1.6)
c.create_line(width / 40, height / 5, width / 4, height / 8)
c.create_line(width / 4, height / 8, width / 4, height / 5.7)
line = """Antiquated outdated fashioned Choleric easily angered Diorama
model scene Fecund fertile Inebriation drunkenness intoxication Marshal gather
together Parity equality Profound meaning Servile overly submissive groveling Usurp
"""
line.lower()
word_list = list(line.split(" "))
correct_ans = word_list
prize_word = random.choice(word_list)
spaces = " ".join(prize_word)
mystery_word = " ".join("_" * len(prize_word))
y = Label(root, text=mystery_word, font="Times, 30")
y.place(x=190, y=300)
def makebuttons():
count = 0
lst = []
if count <= 27:
for letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
lst.append(letter)
for letter in lst:
Buttons = Button(master=root, text=letter, font="Times, 20")
Buttons.pack()
Buttons.place(x=20 * count, y=460)
count += 1
makebuttons()
gallows()
root.mainloop()

There are several issues in your code:
line.lower() will not modify line, so use line = """...""".lower() instead
line.split() will contain "" and "\n" which need to be filtered out
redundant code in makebuttons(): if statement is not necessary and the two for loops can be merged into one
Below is a modified version of part of your code with a new function check_letter() which will be executed when a letter button is clicked:
line = """Antiquated outdated fashioned Choleric easily angered Diorama
model scene Fecund fertile Inebriation drunkenness intoxication Marshal gather
together Parity equality Profound meaning Servile overly submissive groveling Usurp
""".lower()
word_list = [x for x in line.split(" ") if x != "" and x != "\n"] # filter out all "" and "\n"
prize_word = list(random.choice(word_list)) # use list instead of string for easy modification
print(prize_word)
mystery_word = ["_"] * len(prize_word) # use list instead of string for easy modification
y = Label(root, text=mystery_word, font=("Times", 30))
y.place(x=190, y=300)
def check_letter(c):
found = False
for i, letter in enumerate(prize_word):
if c == letter:
prize_word[i] = "-"
mystery_word[i] = c # update the guess result
found = True
if found:
y['text'] = mystery_word
else:
# do whatever you want if letter is not found
pass
def makebuttons():
for i,letter in enumerate("abcdefghijklmnopqrstuvwxyz"):
Button(root, text=letter, font=("Times", 14), width=2, command=lambda c=letter: check_letter(c)).place(x=30*i+10, y=460)
You need to modify check_letter() function to cater two situations:
when all letters in the mystery word are found
when the letter is not found in the mystery word
Hope this help.

Related

How to add relevant scale bars on inset maps using tmap

I used tmap to create the plot attached. However, I would like to add a scale bar to the inset map, but I haven't been able to figured out how to do that. Can someone please help me?
Here are the codes that I used to create the attached map:
main_map <- tmap::tm_shape(main_map_df) +
tmap::tm_polygons(
col = "var.q5",
palette = c("#CCCCCC", "#999999", "#666666", "#333333", "#000000"),
#alpha = 0.7,
lwd = 0.5,
title = "") +
tmap::tm_layout(
frame = FALSE,
legend.outside = TRUE,
legend.hist.width = 5,
legend.text.size = 0.5,
fontfamily = "Verdana") +
tmap::tm_scale_bar(
position = c("LEFT", "BOTTOM"),
breaks = c(0, 10, 20),
text.size = 0.5
) +
tmap::tm_compass(position = c("LEFT", "TOP"))
inset_map <- tmap::tm_shape(inset_map_df) +
tmap::tm_polygons() +
tmap::tm_shape(main_map_df) +
tm_fill("grey50") +
tmap::tm_scale_bar(
position = c("LEFT", "BOTTOM"),
breaks = c(0, 10, 20),
text.size = 0.5
)
# Combine crude rate map (inset + main) =====
tiff(
"main_map_w_iset.tiff",
height = 1200,
width = 1100,
compression = "lzw",
res = 300
)
main_map
print(
inset_map,
vp = viewport(
x = 0.7,
y = 0.18,
width = 0.3,
height = 0.3,
clip = "off")
)
dev.off()
Thank you!
Here's a simple example using the World data set:
library(tidyverse)
library(tmap)
library(grid)
data("World")
# main map
tm_main <- World %>%
filter(name == "Australia") %>%
tm_shape() +
tm_polygons(col = "red",
alpha = .5) +
tm_scale_bar()
# inset map
tm_inset <- tm_shape(World) +
tm_polygons(col = "gray",
alpha = .5) +
tm_scale_bar()
vp <- viewport(x = .615, y = .5, width = .6, height = .6, just = c("right", "top"))
# final map
tmap_save(tm_main, filename = "test_inset.png", insets_tm = tm_inset, insets_vp = vp,
height = 200, width = 200, units = "mm")

RuntimeError:shape ‘[4, 98304]’ is invalid for input of size 113216

I am learning to train a basic nn model for image classification, the error happened when I was trying to feed in image data into the model. I understand that I should input correct size of image data. My image data is 128*256 with 3 channels,4 classes, and the batch size is 4. What I don't understand is where does the size 113216 come from? I checked all related parameters or image meta data, but didn't find a clue. Here is my code:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(3*128*256, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(4, 3*128*256)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
for epoch in range(2): # loop over the dataset multiple times
print('round start')
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
print(inputs.shape)
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
Thanks for your help!
Shapes
Conv2d changes width and height of image without padding. Rule of thumb (if you want to keep the same image size with stride=1 (default)): padding = kernel_size // 2
You are changing number of channels, while your linear layer has 3 for some reason?
Use print(x.shape) after each step if you want to know how your tensor data is transformed!
Commented code
Fixed code with comments about shapes after each step:
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = torch.nn.Conv2d(3, 6, 5)
self.pool = torch.nn.MaxPool2d(2, 2)
self.conv2 = torch.nn.Conv2d(6, 16, 5)
# Output shape from convolution is input shape to fc
self.fc1 = torch.nn.Linear(16 * 29 * 61, 120)
self.fc2 = torch.nn.Linear(120, 84)
self.fc3 = torch.nn.Linear(84, 10)
def forward(self, x):
# In: (4, 3, 128, 256)
x = F.relu(self.conv1(x))
# (4, 3, 124, 252) because kernel_size=5 takes 2 pixels
x = self.pool(x)
# (4, 6, 62, 126) # Because pooling halving the size
x = F.relu(self.conv2(x))
# (4, 16, 58, 122) # Same reason as above
x = self.pool(x)
# (4, 16, 29, 61) Because pooling halving the size
# Better use torch.flatten(x, dim=1) so you don't have to input size here
x = x.view(-1, 16 * 29 * 61) # Use -1 to be batch size independent
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
Other things that might help
Try torch.nn.AdaptiveMaxPool2d(1) before ReLU, it will make your network width and height independent
Use flatten (or torch.nn.Flatten() layer) after this pooling
If so, pass num_channels set in last convolution as in_features for nn.Linear

Pygal: Change dot type/ symbol

I want to change the dots in my pygal chart from the default circles to rectangles (sounds weird but makes sense in my case) and be able to define the size of the rectangles. I couldn't find a solution in the docs. With the config module I can show/ hide the dots and change the dots size but as far as I can see I can't change the dot icon. I also coulndn't find a solution in the style module.
Is there an easy way to do it?
Thanks a lot
There's no way to achieve this using styles or configuration: the circular dots are hard-coded into the function that renders line charts. But, you can easily extend the line chart class and override this function to create a chart with any shape of dot.
If you view the source code of the Line class you will see the following code in the line function:
alter(
self.svg.transposable_node(
dots,
'circle',
cx=x,
cy=y,
r=serie.dots_size,
class_='dot reactive tooltip-trigger'
), metadata
)
This creates a circle for each dot and adds it to the SVG data that will be used to generate the chart.
Copy the whole function into your new class and replace those lines with the following code. This will add squares instead of circles, using the dots_size configuration to determine the width and height:
alter(
self.svg.transposable_node(
dots,
'rect',
x=x - serie.dots_size / 2,
y=y - serie.dots_size / 2,
width=serie.dots_size,
height=serie.dots_size,
class_='dot reactive tooltip-trigger'
), metadata
)
The complete class would look something like this (it looks like a lot of code, but most of it is copy-pasted):
from pygal.util import alter, decorate
class SquareDots(pygal.Line):
def __init__(self, *args, **kwargs):
super(SquareDots, self).__init__(*args, **kwargs)
def line(self, serie, rescale=False):
serie_node = self.svg.serie(serie)
if rescale and self.secondary_series:
points = self._rescale(serie.points)
else:
points = serie.points
view_values = list(map(self.view, points))
if serie.show_dots:
for i, (x, y) in enumerate(view_values):
if None in (x, y):
continue
if self.logarithmic:
if points[i][1] is None or points[i][1] <= 0:
continue
if (serie.show_only_major_dots and self.x_labels
and i < len(self.x_labels)
and self.x_labels[i] not in self._x_labels_major):
continue
metadata = serie.metadata.get(i)
classes = []
if x > self.view.width / 2:
classes.append('left')
if y > self.view.height / 2:
classes.append('top')
classes = ' '.join(classes)
self._confidence_interval(
serie_node['overlay'], x, y, serie.values[i], metadata
)
dots = decorate(
self.svg,
self.svg.node(serie_node['overlay'], class_="dots"),
metadata
)
val = self._format(serie, i)
# This is the part that needs to be changed.
alter(self.svg.transposable_node(
dots,
'rect',
x=x - serie.dots_size / 2,
y=y - serie.dots_size / 2,
width=serie.dots_size,
height=serie.dots_size,
class_='dot reactive tooltip-trigger'
), metadata
)
self._tooltip_data(
dots, val, x, y, xlabel=self._get_x_label(i)
)
self._static_value(
serie_node, val, x + self.style.value_font_size,
y + self.style.value_font_size, metadata
)
if serie.stroke:
if self.interpolate:
points = serie.interpolated
if rescale and self.secondary_series:
points = self._rescale(points)
view_values = list(map(self.view, points))
if serie.fill:
view_values = self._fill(view_values)
if serie.allow_interruptions:
sequences = []
cur_sequence = []
for x, y in view_values:
if y is None and len(cur_sequence) > 0:
sequences.append(cur_sequence)
cur_sequence = []
elif y is None:
continue
else:
cur_sequence.append((x, y))
if len(cur_sequence) > 0:
sequences.append(cur_sequence)
else:
sequences = [view_values]
if self.logarithmic:
for seq in sequences:
for ele in seq[::-1]:
y = points[seq.index(ele)][1]
if y is None or y <= 0:
del seq[seq.index(ele)]
for seq in sequences:
self.svg.line(
serie_node['plot'],
seq,
close=self._self_close,
class_='line reactive' +
(' nofill' if not serie.fill else '')
)
Your new class can then be used like any other pygal chart.
chart = SquareDots(dots_size=50)
chart.add("line", [1, 2, 3, 4, 3, 2])
chart.render_to_png("chart.png")

Getting RMagick/ImageMagick gravity with text

Here is Ruby code:
require 'rmagick'
include Magick
img = Image.new(300, 300)
draw = Draw.new
draw.line(0, 150, 300, 150)
draw.line(150, 0, 150, 300)
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
# set gravity to this value...
draw.gravity Magick.const_get(g)
# ...and draw text with this constant name
draw.text 150, 150, g
}
draw.draw(img)
img.write('tmp/gravity.png')
Here is image which it produces:
For SouthEast/NorthWest and similar gravities result is as expected (text is near 150,150, moved in desired direction). But for South, North and others result is really pretty weird.
As far as I can understand from code, RMagick just translates gravity and text commands into corresponding ImageMagick drawing primitives, so, I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
The key to understanding what's going on is to locate the CenterGravity text.
Shifted left by 150px, and down by 150px.
Now compare compare NorthWestGravity position.
Also translated left & down by 150px respectively. Seeing a trend?
Your issue is with this line...
draw.text 150, 150, g
The Magick::Draw API maps to MVG spec. Use Magick::Draw.push & Magick::Draw.pop to control drawing context.
Edit from comments...
For setting the origin of text to be drawing, you'll need to calculate the position after evaluation the text/type metrics.
Example.
require 'rmagick'
include Magick
img = Image.new(300, 300) {
self.background_color = "palegreen"
}
draw = Draw.new
dotes = Draw.new # Dotes added for point of origin
dotes.fill = "red"
cursor = 1
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
offsetX = 150
offsetY = cursor * 25
dotes.circle offsetX, offsetY, offsetX+2, offsetY+2
# Get metrics of text
metrics = draw.get_type_metrics(img, g)
# Full width
if %w[NorthWestGravity WestGravity SouthWestGravity].include? g then
offsetX -= metrics[:width]
end
# Full height
if %w[SouthWestGravity SouthGravity SouthEastGravity].include? g then
offsetY += metrics[:ascent]
end
# Half width
if %w[NorthGravity SouthGravity CenterGravity].include? g then
offsetX -= metrics[:width] / 2
end
# Half height
if %w[WestGravity CenterGravity EastGravity].include? g then
offsetY += metrics[:ascent] / 2
end
draw.text offsetX, offsetY, g
cursor += 1
}
dotes.draw(img)
draw.draw(img)
img.write('output.png')

MATLAB coding problem

Hey guys, I got this error message when I tried to trigger the function below. Can anybody help me out? Thanks!
>> changeYuv('tilt.yuv',352,288,1:40,40);
??? Index exceeds matrix dimensions.
Error in ==> changeYuv at 32
j=histogram(imgYuv(:,:,1,k+1));
>> [x,y,z,a]=size(imgYuv)
x =
288
y =
352
z =
3
a =
40
The source code:
function [imgYuv, S]= changeYuv(fileName, width, height, idxFrame, nFrames)
% load RGB movie [0, 255] from YUV 4:2:0 file
fileId = fopen(fileName, 'r');
subSampleMat = [1, 1; 1, 1];
nrFrame = length(idxFrame);
for f = 1 : 1 : nrFrame
% search fileId position
sizeFrame = 1.5 * width * height;
fseek(fileId, (idxFrame(f) - 1) * sizeFrame, 'bof');
% read Y component
buf = fread(fileId, width * height, 'uchar');
imgYuv(:, :, 1,f) = reshape(buf, width, height).';
% read U component
buf = fread(fileId, width / 2 * height / 2, 'uchar');
imgYuv(:, :, 2,f) = kron(reshape(buf, width / 2, height / 2).', subSampleMat); % reshape and upsample
% read V component
buf = fread(fileId, width / 2 * height / 2, 'uchar');
imgYuv(:, :, 3,f) = kron(reshape(buf, width / 2, height / 2).', subSampleMat); % reshape and upsample
%histogram difference of Y component
for k=1:(nFrames-1)
h=histogram(imgYuv(:,:,1,k));
j=histogram(imgYuv(:,:,1,k+1));
X=abs(h-j)/256;
S(k)=sum(X);
end
end
fclose(fileId);
On every iteration of the outer loop, you appear to be growing imgYuv by one in the 4th dimension, starting from empty. But your inner loop always loops from 1 to nFrames-1. Therefore, it would seem to me like you're trying to access beyond the extent of imgYuv.
On an unrelated note, growing an array like this is typically very slow. You're much better off initialising imgYuv before you start, i.e. imgYuv = zeros([height,width,3,nFrames]).

Resources