The buttons in my pygame menu only work on hold and not on push [duplicate] - button

This question already has answers here:
Pygame mouse clicking detection
(4 answers)
Closed 5 days ago.
i am working through a pygame code and am in the process of creating a second menu sequentially, however unlike the first menu the second one differs in that it only works when holding down on the position of the button.
i am wondering if anyone can help differentiate what makes the two work and if there is a fix for it.
import random
import time
import pygame
pygame.init()
WIDTH = 500
HEIGHT = 500
screen = pygame.display.set_mode([WIDTH, HEIGHT])
fps = 60
timer = pygame.time.Clock()
main_menu = False
font = pygame.font.Font('freesansbold.ttf', 24)
menu_command = 0
p1score = 0
p2score = 0
class Button:
def __init__(self, txt, pos):
self.text = txt
self.pos = pos
self.button = pygame.rect.Rect((self.pos[0], self.pos[1]), (200, 40))
def draw(self):
pygame.draw.rect(screen, 'light gray', self.button, 0, 5)
pygame.draw.rect(screen, 'dark gray', self.button, 5, 5)
text = font.render(self.text, True, 'black')
screen.blit(text, (self.pos[0] + 15, self.pos[1] + 7))
def check_clicked(self):
if self.button.collidepoint(pygame.mouse.get_pos()) and pygame.mouse.get_pressed()[0]:
return True
else:
return False
def draw_game():
button = Button('Main Menu', (120, 450))
button.draw()
menu = button.check_clicked()
return menu
def draw_menu():
command = -1
pygame.draw.rect(screen, 'black', [100, 100, 300, 300])
#exit menu button
menu_button = Button('Exit Menu', (120, 350))
btn1 = Button('Local Match', (120, 180))
btn2 = Button('Online Match', (120, 240))
btn3 = Button('Settings', (120, 300))
menu_button.draw()
btn1.draw()
btn2.draw()
btn3.draw()
if menu_button.check_clicked():
command = 0
if btn1.check_clicked():
command = 1
if btn2.check_clicked():
command = 2
if btn3.check_clicked():
command = 3
return command
def turndecide():
time.sleep(0.1)
firstturn = -1
p1button = Button('player 1', (120, 120))
p2button = Button('player 2', (120, 180))
ranbutton = Button('Random', (120, 240))
firstturntext = font.render(f'please select who will take the first turn', True, 'black')
screen.blit(firstturntext, (20, 20))
p1button.draw()
p2button.draw()
ranbutton.draw()
if p1button.check_clicked():
firstturn = 1
if p2button.check_clicked():
firstturn = 2
if ranbutton.check_clicked():
firstturn = random.randint(1, 2)
return firstturn
def localgame():
screen.fill('white')
turn = turndecide()
if turn > 0:
screen.fill('red')
outputtext = font.render(f'player {turn} will move first', True, 'black')
screen.blit(outputtext, (20, 20))
run = True
while run:
screen.fill('white')
timer.tick(fps)
if main_menu:
menu_command = draw_menu()
if menu_command != -1:
main_menu = False
else:
main_menu = draw_game()
if menu_command > 0:
if menu_command == 1:
localgame()
if menu_command == 2:
onlinegame()
if menu_command == 3:
settings()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.display.flip()
pygame.quit()
i tried adding a while loop to ensure the code does not move backward as it does
def localgame():
screen.fill('white')
turn = turndecide()
testvariable = True
while testvariable:
if turn > 0:
screen.fill('red')
outputtext = font.render(f'player {turn} will move first', True, 'black')
screen.blit(outputtext, (20, 20))
but this causes the program to crash.

In your check_clicked function, it will return True only if the user is actively clicking on the button. Whether or not you show the menu is, therefore, also linked to whether or not the user is actively clicking/holding the button.
Instead, each time the button is clicked, toggle the menu option. Only toggle the menu again once the player releases the mouse and presses it again. This avoids the menu being toggled really quickly constantly while holding the button down.
Perhaps, have a can_click variable that is set to False every time a click is registered and reset to True when the mouse is released.

Related

I want to show a pygame button, which is inherited from pygame.sprite.Sprite, but it is not getting blitted on the display

All
I am building a game with the PYGame library.
I am struggeling with this piece of code, where I want to schow a button. The button is inherited from the pygame.sprite.Sprite class.
I have searched around but I could not find any example wiht a button generated from the pygame.sprite.Sprite class.
#!/usr/bin/env python
import os, sys
import pygame
import numpy
# initialize the pygame module
pygame.init();
if not pygame.font: logging.warning(' Fonts disabled');
class Button(pygame.sprite.Sprite):
def __init__(self, gameplayCode, gameplayLbl, gameplaycolorRGB):
# Call the parent class (Sprite) constructor
super().__init__();
self.gameplayCode = gameplayCode;
self.gameplayLbl = gameplayLbl;
self.gameplaycolorRGB = gameplaycolorRGB;
self.buttondims = self.width, self.height = 190, 60;
self.smallText = pygame.font.SysFont('comicsansms',15);
# calculating a lichter color, needs to be used when hoovering over button
self.color = numpy.array(gameplaycolorRGB);
self.white = numpy.array(pygame.color.THECOLORS['white']);
self.vector = self.white - self.color;
self.gameplaycolorRGBFaded = self.color + self.vector *0.6;
def setCords(self,x,y):
self.textSurf = self.smallText.render(self.gameplayLbl, 1,
pygame.color.THECOLORS['black']);
self.image = pygame.Surface((self.width, self.height));
self.image.fill(self.gameplaycolorRGB);
self.rect = self.image.get_rect();
self.rect.topleft = x,y;
self.rect.center = (x+(x/2),y+(y/2));
def pressed(self,mouse):
if mouse.get_pos()[0] > self.rect.topleft[0]:
if mouse.get_pos()[1] > self.rect.topleft[1]:
if mouse.get_pos()[0] < self.rect.bottomright[0]:
if mouse.get_pos()[1] < self.rect.bottomright[1]:
if mouse.get_pressed()[0] == 1:
return True;
else:
return False;
else:
return False;
else:
return False;
else:
return False;
else:
return False;
def getGamePlayCode(self):
return self.gameplayCode;
def getGamePlayLbl(self):
return self.gameplayLbl;
def getGamePlayColorRGB(self):
return self.gameplaycolorRGB;
def getGamePlayColorRGBFaded(self):
return self.gameplaycolorRGBFaded;
def getButtonWidth(self):
return self.buttondims[0];
def getButtonHeight(self):
return self.buttondims[1];
def getButtonDims(self):
return self.buttondims;
button=Button('CODE','LABEL',pygame.color.THECOLORS['darkturquoise']);
os.environ['SDL_VIDEO_CENTERED'] = '1';
display_size = display_width, display_height = 1300,600;
gameDisplay = pygame.display.set_mode(display_size);
display_xcenter = gameDisplay.get_width()/2;
display_ycenter = gameDisplay.get_height()/2;
# create a background
background = pygame.display.get_surface();
background.fill(pygame.color.THECOLORS['white']);
# put background on the surface
backgroundPos = xcoord, ycoord = 0,0;
gameDisplay.blit(background, backgroundPos);
pygame.display.update();
title='Testing to show a button which is inherited form
pygame.sprite.Sprite. When pressing the button code must react. When
hoovering color must be brighter.';
textSurface = pygame.font.SysFont('comicsansms',15).render(title, True,
pygame.color.THECOLORS['black']);
textRect = textSurface.get_rect();
gameDisplay.blit(textSurface, textRect);
pygame.display.update();
clock = pygame.time.Clock();
FPS = 60;
game_loop = True;
button.setCords(display_xcenter,display_ycenter);
while game_loop:
mouse = pygame.mouse;
for event in pygame.event.get():
if event.type == pygame.QUIT:
print('Quiting');
game_loop = False;
if button.pressed(mouse):
print('Gameplay pressed');
pygame.display.update();
clock.tick(FPS);
# ending the pygame module
pygame.quit();
I want to blit the button, react on the pressed method, when hoovering over the button the color must be brighter.
Any input is highly appreciated.
In the beginning my game didnot had any classes. Now I am rebuilding the game with the use of classes.
Kind Regards
Olivier
-A Python beginner-
Pygame sprites should usually be added to a sprite group (there are several different group types). Then you can update and draw all sprites in your main loop by calling group.update() and group.draw(gameDisplay).
# Create a sprite group and add the button.
buttons = pygame.sprite.Group(button)
# You could also add it afterwards.
# buttons.add(button)
while game_loop:
mouse = pygame.mouse
for event in pygame.event.get():
if event.type == pygame.QUIT:
print('Quiting')
game_loop = False
if button.pressed(mouse):
print('Gameplay pressed')
# This will call the `update` methods of all sprites in
# this group (not necessary here).
buttons.update()
# The display is usually filled each frame (or blit a background image).
gameDisplay.fill(pygame.color.THECOLORS['white'])
# This will draw each sprite `self.image` at the `self.rect` coords.
buttons.draw(gameDisplay)
pygame.display.update()
clock.tick(FPS)

Can't set separate Siri shortcut phrase through INUIAddVoiceShortcutButton if there are two buttons in a single view

I am unable to set separate Siri shortcut phrases through INUIAddVoiceShortcutButton if there are two buttons in a single view. As soon as I record phrase for one button the other button gets changed to edit mode along with first button. How can I resolve that issue?
Screen before recording phrase
Screen after recording phrase
func firstShortcut() {
let activity1 = NSUserActivity(activityType: "com.test.first")
activity1.title = "Log first activity"
activity1.isEligibleForSearch = true
activity1.suggestedInvocationPhrase = "Log my first activity"
activity1.isEligibleForPrediction = true
activity1.persistentIdentifier = "com.test.first"
view.userActivity = activity1
activity1.becomeCurrent()
let addShortcutButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
addShortcutButton.shortcut = INShortcut(userActivity: activity1)
addShortcutButton.delegate = self
addShortcutButton.translatesAutoresizingMaskIntoConstraints = false
addSiriShortcutView1.addSubview(addShortcutButton)
addSiriShortcutView1.centerXAnchor.constraint(equalTo: addShortcutButton.centerXAnchor).isActive = true
addSiriShortcutView1.centerYAnchor.constraint(equalTo: addShortcutButton.centerYAnchor).isActive = true
}
func secondShortcut() {
let activity2 = NSUserActivity(activityType: "com.test.second")
activity2.title = "Log second activity"
activity2.isEligibleForSearch = true
activity2.suggestedInvocationPhrase = "Log my second activity"
activity2.isEligibleForPrediction = true
activity2.persistentIdentifier = "com.test.second"
view.userActivity = activity2
activity2.becomeCurrent()
let addShortcutButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
addShortcutButton.shortcut = INShortcut(userActivity: activity2)
addShortcutButton.delegate = self
addShortcutButton.translatesAutoresizingMaskIntoConstraints = false
addSiriShortcutView2.addSubview(addShortcutButton)
addSiriShortcutView2.centerXAnchor.constraint(equalTo: addShortcutButton.centerXAnchor).isActive = true
addSiriShortcutView2.centerYAnchor.constraint(equalTo: addShortcutButton.centerYAnchor).isActive = true
}

IDL: Button Widget stops updating after one push

I'm pretty new to IDL and as a way to learn I have tried to create a number guessing game. I have a Widget with three buttons: One that tells the program the number you are thinking of is larger than the one the computer asks about, one if it's smaller and one if it's correct.
My problem is that once you have pushed i.e. the larger button, if you press it again it won't do anything. E.g. the program starts to guess 500, if I press larger it guesses 750. If I now press larger again, the program doesn't respond.
My code is like this:
PRO test1_event, ev
WIDGET_CONTROL, ev.top, GET_UVALUE = stash
minimum = 0
maximum = 1000
IF (ev.Id EQ largerbutton) THEN BEGIN
minimum = (minimum+maximum)/2
maximum = maximum
ENDIF
IF (ev.Id EQ smallerbutton) THEN BEGIN
maximum = (minimum+maximum)/2
minimum = minimum
ENDIF
IF (ev.Id EQ correctbutton) THEN BEGIN
help2 = string('I got it!') ;This prints to a text widget
ENDIF
END
PRO test1
wBase = WIDGET_BASE(X_SCROLL_SIZE = 512, Y_SCROLL_SIZE = 512)
;wDraw = WIDGET_WINDOW(wBase, X_SCROLL_SIZE = 512, Y_SCROLL_SIZE = 512)
Lower = WIDGET_BUTTON(wBase, VALUE = 'Smaller', XOFFSET = 60, YOFFSET = 250)
Higher = WIDGET_BUTTON(wBase, VALUE = 'Larger', XOFFSET = 225, YOFFSET = 250)
Correct = WIDGET_BUTTON(wBase, VALUE = 'Correct', XOFFSET = 380, YOFFSET = 250)
minimum = 0
maximum = 1000
help1 = string('Please think of a number between' + string(minimum) + ' and ' + string(maximum))
help2 = string('Is your number ' + string((minimum + maximum)/2) + '?')
wText = WIDGET_TEXT(wBase, VALUE = ['Welcome to my little game. I will now try and guess a number you are thinking of.'], XSIZE = 63,XOFFSET = 50, YOFFSET = 100)
wText1 = WIDGET_TEXT(wBase, VALUE = help1, XSIZE = 63,XOFFSET = 50, YOFFSET = 120)
wText2 = WIDGET_TEXT(wBase, VALUE = help2, XSIZE = 63,XOFFSET = 50, YOFFSET = 140)
stash = {text1:wText, text2:wText1, text3:wText2, $
lower:Lower, higher:Higher, correct:Correct, minimum:minimum, maximum:maximum}
WIDGET_CONTROL, wBase, SET_UVALUE = stash, /REALIZE
XMANAGER, 'test1', wBase
end
I have tried using a while loop and also REPEAT, but then the program just goes right up to 999 if I press the larger button and to 0 if I press the smaller.
Any ideas to what I can do?
EDIT: Added the rest of the program
I think the buttons are working fine but your event handler doesn't actually do anything. First, I needed to change largerbutton, smallerbutton, and correctbutton to be stash.higher, stash.lower, stash.correct. Then, your code calculates the new minimum & maximum but it doesn't actually do anything with them.
I put a print statement into the event code and it's definitely getting the button presses.
In your event handler you probably want to use widget_control to update the text box with the new guess.

QScrollArea won't acknowledge set widget's size

I'm trying to implement a pinterest-like UI and I've been experimenting with the FlowLayout example for PySide.
The problem is that I'm trying to use a QScrollArea and it won't let me scroll down enough. In other words, I can't get to the bottom of the widget I'm trying to scroll.
You can get the hole file here. If you download it and run it, the problem will become pretty obvious.
My code was taken from here, with these modifications:
Added this class: (it just let's me generate colored and numerated rectangles)
class ColoredRectangle(QLabel):
count = 0
def __init__(self, height, color):
super(ColoredRectangle, self).__init__()
palette = QPalette()
palette.setColor(QPalette.Background, color)
self.setAutoFillBackground(True)
self.setPalette(palette)
self.setFixedSize(200, height)
self.setText(str(ColoredRectangle.count))
ColoredRectangle.count += 1
Now I'm adding rectangles instead of buttons...
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
flowLayout = FlowLayout()
flowLayout.addWidget(ColoredRectangle(450, Qt.white))
flowLayout.addWidget(ColoredRectangle(200, Qt.green))self.setLayout(flowLayout)
self.setLayout(flowLayout)
self.setWindowTitle("Flow Layout")
Changed the doLayout method like this:
def doLayout(self, rect):
if not any(self.itemList):
return
firstItem = self.itemList[0]
x = rect.x()
y = rect.y()
wid = firstItem.widget()
spaceX = self.spacing() + wid.style().layoutSpacing(QtGui.QSizePolicy.PushButton,
QtGui.QSizePolicy.PushButton, QtCore.Qt.Horizontal)
spaceY = self.spacing() + wid.style().layoutSpacing(QtGui.QSizePolicy.PushButton,
QtGui.QSizePolicy.PushButton, QtCore.Qt.Vertical)
itemWidth = firstItem.sizeHint().width()
# calculate column count
columnCount = (rect.width() + spaceX) / (itemWidth + spaceX)
availablePositions = [None] * columnCount
for i in range(columnCount):
availablePositions[i] = QPoint(x + i * (itemWidth + spaceX), y)
for item in self.itemList:
currentPosition = availablePositions[0]
currentPositionColumn = 0
positionColumn = 0
for position in availablePositions:
if min(currentPosition.y(), position.y()) != currentPosition.y():
currentPositionColumn = positionColumn
currentPosition = position
positionColumn += 1
# update available position in column
itemSize = item.sizeHint()
availablePositions[currentPositionColumn] = QPoint(currentPosition.x(),
currentPosition.y() + itemSize.height() + spaceY)
# place item
item.setGeometry(QtCore.QRect(currentPosition, itemSize))
And finally, I added the QScrollArea:
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
mainWin = Window()
scrollArea = QScrollArea()
scrollArea.setWidgetResizable(True)
scrollArea.setWidget(mainWin)
scrollArea.show()
sys.exit(app.exec_())
Thanks in advance!
Well, found the answer on my own...
Apparently the layout has to define the method heightForWidth, which, as far as I understand, returns the height of the layout which results after a width resize. So... I had to store the new height at the end of doLayout so I'm able to return it when asked for.
This is what I did after doLayout:
self.heightForWidthValue = max(map(lambda position : position.y(), availablePositions))
Also, I implemented these functions in FlowLayout:
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
return self.heightForWidthValue
And set 0 as the default value (initialized in FlowLayout->__init__) for heightForWidthValue.
Now it works like a charm :)
Here's the proper code, if anyone is interested.

how to override qscrollbar onclick default behaviour

When you click on QScrollBar's "page control area" ('c' on the image), it will scroll one page. What I want is to make it scroll to the full, just like when you choose "Scroll here" context menu item.
That's quite an interesting question. To find out an answer, we need to take a look at QScrollBar source and find out two things:
How to determine which part of the scroll bar has been clicked;
How to trigger "Scroll Here" behavior.
The answer to the first question lies in QScrollBar::mousePressEvent implementation. It turns out that QStyle::hitTestComplexControl does just what we need. What for the second question, just search "Scroll here" and you'll see that QScrollBarPrivate::pixelPosToRangeValue is used to convert event position to slider value. Unfortunately, we don't have access to functions of this private class, so we're forced to reimplement it. Now let's apply gained knowledge and implement new behavior in a subclass:
import sys
from PyQt4 import QtCore, QtGui
class ModifiedScrollBar(QtGui.QScrollBar):
def __init__(self, parent = None):
super(ModifiedScrollBar, self).__init__(parent)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
control = self.style().hitTestComplexControl(QtGui.QStyle.CC_ScrollBar, opt,
event.pos(), self)
if (control == QtGui.QStyle.SC_ScrollBarAddPage or
control == QtGui.QStyle.SC_ScrollBarSubPage):
# scroll here
gr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt,
QtGui.QStyle.SC_ScrollBarGroove, self)
sr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt,
QtGui.QStyle.SC_ScrollBarSlider, self)
if self.orientation() == QtCore.Qt.Horizontal:
pos = event.pos().x()
sliderLength = sr.width()
sliderMin = gr.x()
sliderMax = gr.right() - sliderLength + 1
if (self.layoutDirection() == QtCore.Qt.RightToLeft):
opt.upsideDown = not opt.upsideDown
else:
pos = event.pos().y()
sliderLength = sr.height()
sliderMin = gr.y()
sliderMax = gr.bottom() - sliderLength + 1
self.setValue(QtGui.QStyle.sliderValueFromPosition(
self.minimum(), self.maximum(), pos - sliderMin,
sliderMax - sliderMin, opt.upsideDown))
return
return super(ModifiedScrollBar, self).mousePressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
edit = QtGui.QTextEdit()
#uncomment for testing horizontal scrollbar
#edit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
edit.setPlainText("Lorem ipsum...")
edit.setVerticalScrollBar(ModifiedScrollBar())
edit.setHorizontalScrollBar(ModifiedScrollBar())
edit.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Works for me, python 3.4 pyqt 5.4.
pixelPosToRangeValue is taken from qt source
def wrapEF(ef):
w = QObject()
w.eventFilter = ef
return w
def sbEventFilter(s, e):
q = s
if (e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton
or e.type() == QEvent.MouseButtonDblClick):
#pixelPosToRangeValue(pos)
opt = QStyleOptionSlider()
q.initStyleOption(opt)
gr = q.style().subControlRect(QStyle.CC_ScrollBar, opt,
QStyle.SC_ScrollBarGroove, q)
sr = q.style().subControlRect(QStyle.CC_ScrollBar, opt,
QStyle.SC_ScrollBarSlider, q)
if q.orientation() == Qt.Horizontal:
sliderLength = sr.width()
sliderMin = gr.x()
sliderMax = gr.right() - sliderLength + 1
if q.layoutDirection() == Qt.RightToLeft:
opt.upsideDown = not opt.upsideDown
dt = sr.width()/2
pos = e.pos().x()
else:
sliderLength = sr.height()
sliderMin = gr.y()
sliderMax = gr.bottom() - sliderLength + 1
dt = sr.height()/2
pos = e.pos().y()
r = QStyle.sliderValueFromPosition(q.minimum(), q.maximum(),
pos - sliderMin - dt,
sliderMax - sliderMin, opt.upsideDown)
#pixelPosToRangeValue,
q.setValue(r)
return q.eventFilter(s, e)
self.scrollBarEF = wrapEF(sbEventFilter)
self.hscrollbar.installEventFilter(self.scrollBarEF)
self.vscrollbar.installEventFilter(self.scrollBarEF)

Resources