How can I make udate_idletasks work on Mac - macos

I've written a tkinter script with animation, that works fine on Xubuntu, but when I run it on Mac, the animation doesn't work. Here's a little script that demonstrates the problem:
import tkinter as tk
from time import sleep
root = tk.Tk()
canvas = tk.Canvas(root, height=200, width = 200)
canvas.pack()
this = canvas.create_rectangle(25,25, 75, 75, fill='blue')
that = canvas.create_rectangle(125, 125, 175, 175, fill = 'red')
def blink(event):
global this, that
for _ in range(9):
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill = 'blue')
canvas.update_idletasks()
sleep(.4)
this, that = that, this
canvas.bind('<ButtonRelease-1>', blink)
root.mainloop()
This draws a red square and a blue square on a canvas. When the user clicks the canvas, the squares repeatedly switch colors. On Xubuntu, it works as intended.
On Mac, when I click the canvas, I get spinning beach ball, and after a few seconds, we see that squares have switched colors, because they switch colors an odd number of times in the code.
It seems to me that update_idletasks isn't working. Is there some way to fix this? I am running python 3.9.5 with Tk 8.6 on Big Sur.

I think what you can do is avoid tasks that will block the mainloop, in this case time.sleep(). So your code can be remade by emulating a for loop with after, and I see nothing that stops this general code from running OS independent:
count = 0 # Think of this as the `_` in for _ in range(9)
def blink(event=None):
global this, that, count
if count < 9: # Basically repeats everytime `count` is less than 9, like a for loop
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill='blue')
this, that = that, this
count += 1 # Increase count
root.after(400,blink) # Repeat this code block every 400 ms or 0.4 seconds
else:
count = 0 # After it is 9, set it to 0 for the next click to be processed

I found that using update instead of update_idletasks works on both platforms. It's my understanding though, that the latter is much preferred. See the accepted answer to this question for example. This solves my immediate problem, but does anyone know if update_idletasks ever works on the Mac?

Related

When I run this sample pygame code DOS window appears and suddenly disappears without waiting any user input

When I run this sample PyGame code, a command window appears and suddenly disappears without waiting any user input. What is wrong? I am a newbie and trying to learn python. I have only VBA experiance previously.
The Python version is: Python 3.8.
import pygame
import sys
pygame.init()
win = pygame.display.set_mode((500,500))
pygame.display.set_caption("First Game")
x = 50
y = 50
width = 40
height = 60
vel = 5
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
win.fill((0,0,0)) # Fills the screen with black
pygame.draw.rect(win, (255,0,0), (x, y, width, height))
pygame.display.update()
pygame.quit()
sys.exit()
Both your pygame.quit() and sys.exit() should be outside of the run loop, so that they execute only when the loop exits.
What you have now will run a single iteration, because it exits at the end of that iteration. Some people say a short game is a good game but you can take that too far :-)
What I see when I run your code is that the window with the red square appears briefly, then the whole program exits. When I fix your indentation, the window stays there and I can move the red square with the arrow keys:
In other words, this is the form you need:
run = True
while run:
do_stuff()
pygame.quit()
sys.exit() # Probably not needed since you'll exit anyway.
It also sounds like you're running it from an IDE, given that the window disappears. I always like to run it from the command line so that, should an exception occur (or important output shows up), I can see it after the program has finished.

How can I create a static title/border on a Python cmd line application

I'm using the Python cmd module to create a CLI application. Everything works great! However, I'm attempting to tailor the app to a certain type of presence: text colors, title, using alpha-numeric characters as borders, etc.
Is there a standard way to create a screen overrun of sorts: the top of the screen where I have set a border and color title remain static? And from the middle of the screen, or thereabouts, down to the bottom of the screen, any text or commands entered at the prompt will stop being visible as they reach the title/border. Basically, what I'm after is for a user to always see the title/border unless they exit the CLI app. If they type help, of course, they will see the commands below the title/border. But, as they enter commands, ideally, the command menu will disappear behind the screen title/border.
Any direction on the best way I can achieve this is appreciated.
Check curses
You should be able to decorate CLI/Terminal with colors and static borders.
I have extended example taken from HERE:
import curses
from multiprocessing import Process
p = None
def display(stdscr):
stdscr.clear()
stdscr.timeout(500)
maxy, maxx = stdscr.getmaxyx()
curses.newwin(2,maxx,3,1)
# invisible cursor
curses.curs_set(0)
if (curses.has_colors()):
# Start colors in curses
curses.start_color()
curses.use_default_colors()
curses.init_pair(1, curses.COLOR_RED, -1)
stdscr.refresh()
curses.init_pair(1, 0, -1)
curses.init_pair(2, 1, -1)
curses.init_pair(3, 2, -1)
curses.init_pair(4, 3, -1)
bottomBox = curses.newwin(8,maxx-2,maxy-8,1)
bottomBox.box()
bottomBox.addstr("BottomBox")
bottomBox.refresh()
bottomwindow = curses.newwin(6,maxx-4,maxy-7,2)
bottomwindow.addstr("This is my bottom view", curses.A_UNDERLINE)
bottomwindow.refresh()
stdscr.addstr("{:20s}".format("Hello world !"), curses.color_pair(4))
stdscr.refresh()
while True:
event = stdscr.getch()
if event == ord("q"):
break
def hang():
while True:
temp = 1 + 1
if __name__ == '__main__':
p = Process(target = hang)
curses.wrapper(display)

Link a tkinter button to seperate script

I have a tkinter interface with a few entry widgets as inputs. Upon clicking a button I would like those inputs to be sent to a separate script to be processed and a value printed and potentially returned back to the button (I am looking at this for a dual accuracy assessment statistic)
This is a lower scale example of what I have so far and am looking to accomplish
Example Secondary Script: GUI_ConnectorScript
def calculate():
global result
result = int(entry.get())
result += 1
print result
Primary Script: GUI_ConnectorScript
from Tkinter import *
import GUI_ConnectorScript
background = "#A8A8A8"
master = Tk()
screen_width = master.winfo_screenwidth()
screen_height = master.winfo_screenheight()
width = int(screen_width*0.7)
height = int(screen_height*0.7)
size = "%sx%s"%(width,height)
master.geometry(size)
master.title("GIS Display")
text = Text(master, width = 80, height = 40, background = background)
text.pack(expand = TRUE, fill = BOTH)
entry = Entry(master, width=5).place(x=100,y=100)
button = Button(master, text="Calculate", command=GUI_ConnectorScript).place(x=500,y=500)
mainloop()
I have been trying to figure this out for awhile and have look around a lot for an answer. I have found examples similar but I am having an issue getting it to work for my application.
I agree with Parviz, whenever GUI programs get too complicated you should use Object-Oriented Programming.
I can further advice that you use kivy (if possible) instead of tkinter, its much better for bigger projects

psychopy polygon on top of image

using psychopy ver 1.81.03 on a mac I want to draw a polygon (e.g. a triangle) on top of an image.
So far, my image stays always on top and thus hides the polygon, no matter the order I put them in. This also stays true if I have the polygon start a frame later than the image.
e.g. see inn the code below (created with the Builder before compiling) how both a blue square and a red triangle are supposed to start at frame 0, but when you run it the blue square always covers the red triangle!?
Is there a way to have the polygon on top? Do I somehow need to merge the image and polygon before drawing them?
Thank you so much for your help!!
Sebastian
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
This experiment was created using PsychoPy2 Experiment Builder (v1.81.03), Sun Jan 18 20:44:26 2015
If you publish work using this script please cite the relevant PsychoPy publications
Peirce, JW (2007) PsychoPy - Psychophysics software in Python. Journal of Neuroscience Methods, 162(1-2), 8-13.
Peirce, JW (2009) Generating stimuli for neuroscience using PsychoPy. Frontiers in Neuroinformatics, 2:10. doi: 10.3389/neuro.11.010.2008
"""
from __future__ import division # so that 1/3=0.333 instead of 1/3=0
from psychopy import visual, core, data, event, logging, sound, gui
from psychopy.constants import * # things like STARTED, FINISHED
import numpy as np # whole numpy lib is available, prepend 'np.'
from numpy import sin, cos, tan, log, log10, pi, average, sqrt, std, deg2rad, rad2deg, linspace, asarray
from numpy.random import random, randint, normal, shuffle
import os # handy system and path functions
# Ensure that relative paths start from the same directory as this script
_thisDir = os.path.dirname(os.path.abspath(__file__))
os.chdir(_thisDir)
# Store info about the experiment session
expName = u'test_triangle_over_square' # from the Builder filename that created this script
expInfo = {'participant':'', 'session':'001'}
dlg = gui.DlgFromDict(dictionary=expInfo, title=expName)
if dlg.OK == False: core.quit() # user pressed cancel
expInfo['date'] = data.getDateStr() # add a simple timestamp
expInfo['expName'] = expName
# Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
filename = _thisDir + os.sep + 'data/%s_%s_%s' %(expInfo['participant'], expName, expInfo['date'])
# An ExperimentHandler isn't essential but helps with data saving
thisExp = data.ExperimentHandler(name=expName, version='',
extraInfo=expInfo, runtimeInfo=None,
originPath=None,
savePickle=True, saveWideText=True,
dataFileName=filename)
#save a log file for detail verbose info
logFile = logging.LogFile(filename+'.log', level=logging.EXP)
logging.console.setLevel(logging.WARNING) # this outputs to the screen, not a file
endExpNow = False # flag for 'escape' or other condition => quit the exp
# Start Code - component code to be run before the window creation
# Setup the Window
win = visual.Window(size=(1280, 800), fullscr=True, screen=0, allowGUI=False, allowStencil=False,
monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
blendMode='avg', useFBO=True,
)
# store frame rate of monitor if we can measure it successfully
expInfo['frameRate']=win.getActualFrameRate()
if expInfo['frameRate']!=None:
frameDur = 1.0/round(expInfo['frameRate'])
else:
frameDur = 1.0/60.0 # couldn't get a reliable measure so guess
# Initialize components for Routine "trial"
trialClock = core.Clock()
ISI = core.StaticPeriod(win=win, screenHz=expInfo['frameRate'], name='ISI')
square = visual.ImageStim(win=win, name='square',units='pix',
image=None, mask=None,
ori=0, pos=[0, 0], size=[200, 200],
color=u'blue', colorSpace='rgb', opacity=1,
flipHoriz=False, flipVert=False,
texRes=128, interpolate=True, depth=-1.0)
polygon = visual.ShapeStim(win=win, name='polygon',units='pix',
vertices = [[-[200, 300][0]/2.0,-[200, 300][1]/2.0], [+[200, 300][0]/2.0,-[200, 300][1]/2.0], [0,[200, 300][1]/2.0]],
ori=0, pos=[0, 0],
lineWidth=1, lineColor=[1,1,1], lineColorSpace='rgb',
fillColor=u'red', fillColorSpace='rgb',
opacity=1,interpolate=True)
# Create some handy timers
globalClock = core.Clock() # to track the time since experiment started
routineTimer = core.CountdownTimer() # to track time remaining of each (non-slip) routine
#------Prepare to start Routine "trial"-------
t = 0
trialClock.reset() # clock
frameN = -1
# update component parameters for each repeat
# keep track of which components have finished
trialComponents = []
trialComponents.append(ISI)
trialComponents.append(square)
trialComponents.append(polygon)
for thisComponent in trialComponents:
if hasattr(thisComponent, 'status'):
thisComponent.status = NOT_STARTED
#-------Start Routine "trial"-------
continueRoutine = True
while continueRoutine:
# get current time
t = trialClock.getTime()
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
# update/draw components on each frame
# *square* updates
if frameN >= 0 and square.status == NOT_STARTED:
# keep track of start time/frame for later
square.tStart = t # underestimates by a little under one frame
square.frameNStart = frameN # exact frame index
square.setAutoDraw(True)
# *polygon* updates
if frameN >= 0 and polygon.status == NOT_STARTED:
# keep track of start time/frame for later
polygon.tStart = t # underestimates by a little under one frame
polygon.frameNStart = frameN # exact frame index
polygon.setAutoDraw(True)
# *ISI* period
if t >= 0.0 and ISI.status == NOT_STARTED:
# keep track of start time/frame for later
ISI.tStart = t # underestimates by a little under one frame
ISI.frameNStart = frameN # exact frame index
ISI.start(0.5)
elif ISI.status == STARTED: #one frame should pass before updating params and completing
ISI.complete() #finish the static period
# check if all components have finished
if not continueRoutine: # a component has requested a forced-end of Routine
routineTimer.reset() # if we abort early the non-slip timer needs reset
break
continueRoutine = False # will revert to True if at least one component still running
for thisComponent in trialComponents:
if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
continueRoutine = True
break # at least one component has not yet finished
# check for quit (the Esc key)
if endExpNow or event.getKeys(keyList=["escape"]):
core.quit()
# refresh the screen
if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
win.flip()
else: # this Routine was not non-slip safe so reset non-slip timer
routineTimer.reset()
#-------Ending Routine "trial"-------
for thisComponent in trialComponents:
if hasattr(thisComponent, "setAutoDraw"):
thisComponent.setAutoDraw(False)
win.close()
core.quit()
As per Jonas' comment above, PsychoPy uses a layering system in which subsequent stimuli are drawn on top of previous stimuli (as in his code examples).
In the graphical Builder environment, drawing order is represented by the vertical order of stimulus components: stimuli at the top are drawn first, and ones lower down are progressively layered upon them.
You can change the order of stimulus components by right-clicking on them and selecting "Move up", "move down", etc as required.
Sebastian, has, however, identified a bug here, in that the intended drawing order is not honoured between ImageStim and ShapeStim components. As a work-around, you might be able to replace your ShapeStim with a bitmap representation, displayed using an ImageStim. Multiple ImageStims should draw correctly (as do multiple ShapeStims). To get it to draw correctly on top of another image, be sure to save it as a .png file, which supports transparency. That way, only the actual shape will be drawn on top, as its background pixels can be set to be transparent and will not mask the the underlying image.
For a long-term solution, I've added your issue as a bug report to the PsychoPy GitHub project here:
https://github.com/psychopy/psychopy/issues/795
It turned out to be a bug in the Polygon component in Builder.
This is fixed in the upcoming release (1.82.00). The changes needed to make the fix can be seen at
https://github.com/psychopy/psychopy/commit/af1af9a7a85cee9b4ec8ad5e2ff1f03140bd1a36
which you can add to your own installation if you like.
cheers,
Jon

MATLAB has encountered an internal error and needs to close

I've just installed the new MATLAB R2013b for 64bit Mac, and my OS is OS X 10.8.4. I'm running into a consistent problem that I never had with R2013a. When I run one of my scripts (see below), the entire script goes through OK, but then I keep getting an error message "MATLAB has encountered an internal problem and needs to close." Then I have to shut MATLAB down.
I have a feeling that I've goofed somewhere on the installation since I'm new to MATLAB, but I'm not sure.
This exact same script still runs fine on R2013a, which I've yet to uninstall. The script (making use of Psychtoolbox) is an experiment which opens a screen, presents some text, presents an audio file, and requires the participant to respond with 6 keystrokes. This script only presents two audio files, since I'm just testing it.
All the loops seem to work on both versions of MATLAB, and the screen closes at the end (which only happens after 2 passes through the main loop). I take this to mean the script is working, but something in a post stage is causing issues.
Any and all ideas are much appreciated!
-Josh
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% SCREEN & AUDIO SETUP %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%
%% SCREEN %%
%%%%%%%%%%%%
% Set up a nice blank screen.
whichScreen = 0;
% Full Screen mode
%window = Screen(whichScreen, 'OpenWindow');
% Small screen mode used for code testing
window = Screen('OpenWindow',0, [450 450 500], [5,5, 550,550]);
white = WhiteIndex(window); % pixel value for white
Screen(window, 'FillRect', white);
Screen(window, 'Flip');
% Set global text size for the display.
Screen('TextSize', window, 15);
Screen(window,'TextFont','Arial');
Screen('TextStyle', window, 0)
%%%%%%%%%%%
%% AUDIO %%
%%%%%%%%%%%
% Set initial audio parameters:
nrchannels = 1; % All stimuli are mono-sounds.
freq = 44100;
% Initialize sound driver.
InitializePsychSound;
try
pahandle = PsychPortAudio('Open', [], [], 3, freq, nrchannels);
catch
% If the above fails, use the audio device's standard frequency.
psychlasterror('reset'); % I believe this some reseting of the error made in 'try'.
pahandle = PsychPortAudio('Open', [], [], 3, [], nrchannels);
end
%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%
%%%%% MAIN LOOP %%%%%
%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%
home = '/Users/josh/Dropbox/Beverlab/Fall_2013_Study/Design/Matlab/'
SampleExperiment = {strcat(home,'Stimuli/tokensA/gipa_mono.wav'),...
strcat(home,'Stimuli/tokensB/gabo_mono.wav')};
timeLimit = 10; % Set up the time limit.
ans = 0; % This is used to track whether or not the participant answered.
numStim = 2; % Just using 2 right now to test the code
ListenChar(0);
for i=1:numStim;
token = char(SampleExperiment(1,randi([1,2]))); % randomly select a wave file from 'SampleExperiment' and assign it to 'token'
[y,freq] = wavread(token); % Read the current wav file.
wavedata = y'; % Transpose wav data.
PsychPortAudio('FillBuffer', pahandle, wavedata); % fill the buffer, ready to play
t1 = PsychPortAudio('Start', pahandle, 1, 0, 1); % play the wave file and get timestamp in one go
while GetSecs<t1+timeLimit
if ans<6
[secs, keyCode, deltaSecs] = KbWait([],2,t1+timeLimit);
if isempty(find(keyCode,1))
break
end
if ~isempty(find(keyCode,1))
ans=ans+1;
end
end
if ans==6
WaitSecs(rand*3+1);
break
end
end
if ans<6
DrawFormattedText(window, 'Lets try that again...press 6 times', 'center', 'center');
Screen(window, 'Flip');
WaitSecs(1);
Screen(window, 'FillRect', white);
Screen(window, 'Flip');
[y,freq] = wavread(token); % Read the current wav file.
wavedata = y'; % Transpose wav data.
PsychPortAudio('FillBuffer', pahandle, wavedata); % fill the buffer, ready to play
t1 = PsychPortAudio('Start', pahandle, 1, 0, 1); % play the wave file and get timestamp in one go
while GetSecs<t1+timeLimit
if ans<6
[secs, keyCode, deltaSecs] = KbWait([],2,t1+timeLimit);
if isempty(find(keyCode,1))
break
end
if ~isempty(find(keyCode,1))
ans=ans+1;
end
end
if ans==6
WaitSecs(rand*3+1);
break
end
end
end
end
Screen('CloseAll')
This sort of error usually indicates something drastic and unrecoverable has happened. It appears that the Psychtoolbox contains MEX files, that is probably the most likely culprit. I would either attempt to rebuild those using R2013b, or contact the authors to see if they have a version compatible with R2013b.
This crashing is likely due to a new bug in OSX 10.8.4, causing PsychToolBox to crash on closing the connection to the display server (and apparently bringing MATLAB with it). See here for discussion and resolution.
Anybody still having this issue, please update to the latest version of PsychToolBox (which is always a good idea!)

Resources