psychopy 2024.2.1__py3-none-any.whl → 2024.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of psychopy might be problematic. Click here for more details.
- psychopy/.DS_Store +0 -0
- psychopy/GIT_SHA +1 -1
- psychopy/VERSION +1 -1
- psychopy/__init__.py +10 -1
- psychopy/__init__.py.orig +65 -0
- psychopy/app/{locale/ar_001/.DS_Store → .DS_Store} +0 -0
- psychopy/app/Resources/.DS_Store +0 -0
- psychopy/app/_psychopyApp.py +11 -3
- psychopy/app/appData.spec +1 -1
- psychopy/app/builder/builder.py +1 -1
- psychopy/app/builder/builder.py.orig +3932 -0
- psychopy/app/builder/dialogs/__init__.py.orig +1679 -0
- psychopy/app/builder/dialogs/paramCtrls.py +1 -1
- psychopy/app/builder/dialogs/paramCtrls.py.orig +713 -0
- psychopy/app/colorpicker/__init__.py.orig +411 -0
- psychopy/app/cortex.log +0 -0
- psychopy/app/jobs.py +8 -1
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.po +2452 -1731
- psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN.po +6127 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/zh_CN_allFlagged.po +7366 -0
- psychopy/app/plugin_manager/dialog.py +9 -7
- psychopy/app/ribbon.py +2 -1
- psychopy/app/runner/runner.py +7 -5
- psychopy/clock.py +8 -4
- psychopy/core.py.orig +169 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/index.html +23 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks-legacy-browsers.js +423 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/randomisedBlocks.js +427 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/chooseBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/facesBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/housesBlock.xlsx +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face01.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face02.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/face03.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house01.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house02.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/html/resources/stims/house03.jpg +0 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks.py +330 -0
- psychopy/demos/builder/Design Templates/randomisedBlocks/randomisedBlocks_lastrun.py +330 -0
- psychopy/demos/builder/Feature Demos/eyetracking/eyetracking.xml +298 -0
- psychopy/demos/builder/Feature Demos/eyetracking/eyetracking.xsd +120 -0
- psychopy/demos/builder/Tools/.DS_Store +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/.DS_Store +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.csv +38 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.log +3418 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/_gamma_correction_visual_2022-05-18_14h18.29.439.psydat +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.csv +2 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.log +15 -0
- psychopy/demos/builder/Tools/gammaCalibration/data/x1_gamma_correction_visual_2022-05-17_13h59.42.928.psydat +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.psyexp +323 -0
- psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual.py +562 -0
- psychopy/demos/builder/Tools/gammaCalibration/gamma_correction_visual_lastrun.py +562 -0
- psychopy/demos/builder/Tools/gammaCalibration/questStairs.xlsx +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/readme.md +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/resources/low_contrast.png +0 -0
- psychopy/demos/builder/Tools/gammaCalibration/resources/make_2nd_order_tex.py +59 -0
- psychopy/demos/builder/Tools/gammaCalibration/resources/second_order_tex.png +0 -0
- psychopy/demos/coder/.DS_Store +0 -0
- psychopy/demos/coder/experiment control/info_gamma.pickle +0 -0
- psychopy/demos/coder/iohub/.iohpid +1 -0
- psychopy/demos/coder/iohub/eyetracking/.iohpid +1 -0
- psychopy/demos/coder/iohub/wintab/.DS_Store +0 -0
- psychopy/demos/coder/stimuli/.DS_Store +0 -0
- psychopy/demos/coder/stimuli/radialGratingContracting.py +29 -0
- psychopy/experiment/_experiment.py.orig +1032 -0
- psychopy/experiment/components/.DS_Store +0 -0
- psychopy/experiment/components/_base.py +13 -4
- psychopy/experiment/components/_base.py.orig +823 -0
- psychopy/experiment/components/form/.DS_Store +0 -0
- psychopy/experiment/components/microphone/__init__.py +10 -1
- psychopy/experiment/components/microphone/__init__.py.orig +490 -0
- psychopy/experiment/components/polygon/__init__.py +21 -22
- psychopy/experiment/components/settings/__init__.py +13 -14
- psychopy/experiment/components/settings/__init__.py.orig +1337 -0
- psychopy/experiment/components/textbox/__init__.py.orig +310 -0
- psychopy/experiment/components/webcam/.DS_Store +0 -0
- psychopy/experiment/components/webcam/light/.DS_Store +0 -0
- psychopy/experiment/flow.py +10 -8
- psychopy/experiment/loops.py.orig +829 -0
- psychopy/experiment/params.py +8 -3
- psychopy/experiment/params.py.orig +408 -0
- psychopy/experiment/routine.py.orig +503 -0
- psychopy/experiment/routines/_base.py +15 -6
- psychopy/experiment/routines/counterbalance/__init__.py +1 -0
- psychopy/gui/qtgui.py +14 -7
- psychopy/gui/util.py +10 -14
- psychopy/gui/wxgui.py +10 -4
- psychopy/hardware/.DS_Store +0 -0
- psychopy/hardware/brainproducts.py.orig +680 -0
- psychopy/hardware/iolab.py.orig +238 -0
- psychopy/hardware/manager.py +1 -1
- psychopy/hardware/photodiode.py +59 -27
- psychopy/hardware/serialport.py +51 -0
- psychopy/hardware/speaker.py +4 -4
- psychopy/iohub/datastore/__init__.py.orig +443 -0
- psychopy/iohub/datastore/util.py.orig +692 -0
- psychopy/iohub/devices/mouse/darwin.py.orig +427 -0
- psychopy/iohub/devices/mouse/linux2.py.orig +198 -0
- psychopy/preferences/.DS_Store +0 -0
- psychopy/projects/pavlovia.py +10 -3
- psychopy/projects/pavlovia.py.orig +1295 -0
- psychopy/sound/backend_ptb.py +22 -5
- psychopy/sound/transcribe.py +24 -4
- psychopy/tests/.DS_Store +0 -0
- psychopy/tests/data/.DS_Store +0 -0
- psychopy/tests/data/TestCircle_fill_local.png +0 -0
- psychopy/tests/data/__test.png +0 -0
- psychopy/tests/data/aperture1_normHexbackground_local.png +0 -0
- psychopy/tests/data/aperture1_norm_local.png +0 -0
- psychopy/tests/data/aperture2_normHexbackground_local.png +0 -0
- psychopy/tests/data/beatandrcos_height_local.png +0 -0
- psychopy/tests/data/beatandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/beatandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/beatandrcos_norm_local.png +0 -0
- psychopy/tests/data/beatandrcos_stencil_local.png +0 -0
- psychopy/tests/data/blend_add_height_local.png +0 -0
- psychopy/tests/data/blend_add_normAddBlend_local.png +0 -0
- psychopy/tests/data/blend_add_normHexbackground_local.png +0 -0
- psychopy/tests/data/blend_add_normNoShade_local.png +0 -0
- psychopy/tests/data/blend_add_norm_local.png +0 -0
- psychopy/tests/data/blend_add_stencil_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_height_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normAddBlend_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normHexbackground_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_normNoShade_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_norm_local.png +0 -0
- psychopy/tests/data/bufferimg_gabor_stencil_local.png +0 -0
- psychopy/tests/data/circleHex_height_local.png +0 -0
- psychopy/tests/data/circleHex_normAddBlend_local.png +0 -0
- psychopy/tests/data/circleHex_normHexbackground_local.png +0 -0
- psychopy/tests/data/circleHex_normNoShade_local.png +0 -0
- psychopy/tests/data/circleHex_norm_local.png +0 -0
- psychopy/tests/data/circleHex_stencil_local.png +0 -0
- psychopy/tests/data/color_comparison_local.png +0 -0
- psychopy/tests/data/corrFullRandom_local.csv +16 -0
- psychopy/tests/data/corrFullRandom_local.tsv +6 -0
- psychopy/tests/data/correctScript/.DS_Store +0 -0
- psychopy/tests/data/dots_height_local.png +0 -0
- psychopy/tests/data/dots_normAddBlend_local.png +0 -0
- psychopy/tests/data/dots_normHexbackground_local.png +0 -0
- psychopy/tests/data/dots_normNoShade_local.png +0 -0
- psychopy/tests/data/dots_norm_local.png +0 -0
- psychopy/tests/data/dots_stencil_local.png +0 -0
- psychopy/tests/data/elarray1_height_local.png +0 -0
- psychopy/tests/data/elarray1_normAddBlend_local.png +0 -0
- psychopy/tests/data/elarray1_normHexbackground_local.png +0 -0
- psychopy/tests/data/elarray1_norm_local.png +0 -0
- psychopy/tests/data/elarray1_stencil_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_height_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopeandrcos_stencil_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_height_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_norm_local.png +0 -0
- psychopy/tests/data/envelopepowerandrcos_stencil_local.png +0 -0
- psychopy/tests/data/gabor1_height_local.png +0 -0
- psychopy/tests/data/gabor1_normAddBlend_local.png +0 -0
- psychopy/tests/data/gabor1_normHexbackground_local.png +0 -0
- psychopy/tests/data/gabor1_normNoShade_local.png +0 -0
- psychopy/tests/data/gabor1_norm_local.png +0 -0
- psychopy/tests/data/gabor1_stencil_local.png +0 -0
- psychopy/tests/data/greyscale_normHexbackground_local.png +0 -0
- psychopy/tests/data/imageAndGauss_height_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normAddBlend_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normHexbackground_local.png +0 -0
- psychopy/tests/data/imageAndGauss_normNoShade_local.png +0 -0
- psychopy/tests/data/imageAndGauss_norm_local.png +0 -0
- psychopy/tests/data/imageAndGauss_stencil_local.png +0 -0
- psychopy/tests/data/movFrame1_stencil_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_height_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_normNoShade_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseAndRcos_stencil_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_height_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normAddBlend_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normHexbackground_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_normNoShade_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_norm_local.png +0 -0
- psychopy/tests/data/noiseFiltersAndRcos_stencil_local.png +0 -0
- psychopy/tests/data/numpyImage_height_local.png +0 -0
- psychopy/tests/data/numpyImage_normAddBlend_local.png +0 -0
- psychopy/tests/data/numpyImage_normHexbackground_local.png +0 -0
- psychopy/tests/data/numpyImage_normNoShade_local.png +0 -0
- psychopy/tests/data/numpyImage_norm_local.png +0 -0
- psychopy/tests/data/numpyImage_stencil_local.png +0 -0
- psychopy/tests/data/shape2_1_normAddBlend_local.png +0 -0
- psychopy/tests/data/shape2_1_normHexbackground_local.png +0 -0
- psychopy/tests/data/shape2_1_normNoShade_local.png +0 -0
- psychopy/tests/data/shape2_1_norm_local.png +0 -0
- psychopy/tests/data/shape2_1_stencil_local.png +0 -0
- psychopy/tests/data/testLoopsBlocks.psyexp_local.py +328 -0
- psychopy/tests/data/text1_height_local.png +0 -0
- psychopy/tests/data/text1_normAddBlend_local.png +0 -0
- psychopy/tests/data/text1_normHexbackground_local.png +0 -0
- psychopy/tests/data/text1_norm_local.png +0 -0
- psychopy/tests/data/text1_stencil_local.png +0 -0
- psychopy/tests/data/text2_height.png +0 -0
- psychopy/tests/data/text2_normAddBlend.png +0 -0
- psychopy/tests/data/text2_normHexbackground.png +0 -0
- psychopy/tests/data/text2_stencil.png +0 -0
- psychopy/tests/data/wedge1_height_local.png +0 -0
- psychopy/tests/data/wedge1_normAddBlend_local.png +0 -0
- psychopy/tests/data/wedge1_normHexbackground_local.png +0 -0
- psychopy/tests/data/wedge1_normNoShade_local.png +0 -0
- psychopy/tests/data/wedge1_norm_local.png +0 -0
- psychopy/tests/data/wedge1_stencil_local.png +0 -0
- psychopy/tests/test_app/.DS_Store +0 -0
- psychopy/tests/test_app/test_builder/.DS_Store +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.csv +9 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.log +177 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.psydat +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1206.xlsx +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.csv +9 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.log +168 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.psydat +0 -0
- psychopy/tests/test_app/test_builder/data/_2021_ 5_03_1324.xlsx +0 -0
- psychopy/tests/test_data/.DS_Store +0 -0
- psychopy/tests/test_hardware/test_CRS_BitsSharp.py.orig +68 -0
- psychopy/tests/test_tools/test_arraytools.py +112 -0
- psychopy/tests/test_visual/test_image.py.orig +219 -0
- psychopy/tools/arraytools.py +47 -0
- psychopy/tools/versionchooser.py +1 -1
- psychopy/visual/backends/pygletbackend.py +26 -8
- psychopy/visual/basevisual.py.orig +1723 -0
- psychopy/visual/form.py.orig +1181 -0
- psychopy/visual/text.py.orig +752 -0
- psychopy/visual/textbox2/textbox2.py.orig +1315 -0
- psychopy/visual/window.py +13 -5
- psychopy/visual/windowwarp.py.orig +463 -0
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/METADATA +9 -9
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/RECORD +244 -78
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/WHEEL +1 -1
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/entry_points.txt +2 -0
- psychopy/app/locale/ar_001/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/cs_CZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/da_DK/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/de_DE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/el_GR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_NZ/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/en_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_CO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_ES/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/es_US/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/et_EE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fa_IR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fi_FI/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/fr_FR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/he_IL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hi_IN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/hu_HU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/it_IT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ja_JP/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ko_KR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ms_MY/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nl_NL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/nn_NO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pl_PL/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/pt_PT/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ro_RO/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/ru_RU/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/sv_SE/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/tr_TR/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_CN/LC_MESSAGE/messages.mo +0 -0
- psychopy/app/locale/zh_TW/LC_MESSAGE/messages.mo +0 -0
- psychopy-2024.2.1.dist-info/licenses/AUTHORS.md +0 -138
- /psychopy/{app/locale/ar_001/LC_MESSAGE → demos/builder}/.DS_Store +0 -0
- /psychopy/{app/locale/es_ES/LC_MESSAGE → demos/builder/Experiments}/.DS_Store +0 -0
- /psychopy/{visual → demos/builder/Tools/gammaCalibration/data}/.DS_Store +0 -0
- {psychopy-2024.2.1.dist-info → psychopy-2024.2.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
'''Class of text stimuli to be displayed in a :class:`~psychopy.visual.Window`
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
# Part of the PsychoPy library
|
|
8
|
+
# Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2021 Open Science Tools Ltd.
|
|
9
|
+
# Distributed under the terms of the GNU General Public License (GPL).
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import glob
|
|
16
|
+
import warnings
|
|
17
|
+
|
|
18
|
+
# Ensure setting pyglet.options['debug_gl'] to False is done prior to any
|
|
19
|
+
# other calls to pyglet or pyglet submodules, otherwise it may not get picked
|
|
20
|
+
# up by the pyglet GL engine and have no effect.
|
|
21
|
+
# Shaders will work but require OpenGL2.0 drivers AND PyOpenGL3.0+
|
|
22
|
+
import pyglet
|
|
23
|
+
pyglet.options['debug_gl'] = False
|
|
24
|
+
import ctypes
|
|
25
|
+
GL = pyglet.gl
|
|
26
|
+
|
|
27
|
+
import psychopy # so we can get the __path__
|
|
28
|
+
from psychopy import logging
|
|
29
|
+
|
|
30
|
+
# tools must only be imported *after* event or MovieStim breaks on win32
|
|
31
|
+
# (JWP has no idea why!)
|
|
32
|
+
from psychopy.tools.monitorunittools import cm2pix, deg2pix, convertToPix
|
|
33
|
+
from psychopy.tools.attributetools import attributeSetter, setAttribute
|
|
34
|
+
<<<<<<< HEAD
|
|
35
|
+
from psychopy.visual.basevisual import (BaseVisualStim, ColorMixin,
|
|
36
|
+
ContainerMixin, WindowMixin)
|
|
37
|
+
=======
|
|
38
|
+
from psychopy.visual.basevisual import (BaseVisualStim, ForeColorMixin,
|
|
39
|
+
ContainerMixin)
|
|
40
|
+
>>>>>>> release
|
|
41
|
+
from psychopy.colors import Color
|
|
42
|
+
|
|
43
|
+
# for displaying right-to-left (possibly bidirectional) text correctly:
|
|
44
|
+
from bidi import algorithm as bidi_algorithm # sufficient for Hebrew
|
|
45
|
+
# extra step needed to reshape Arabic/Farsi characters depending on
|
|
46
|
+
# their neighbours:
|
|
47
|
+
try:
|
|
48
|
+
import arabic_reshaper
|
|
49
|
+
haveArabic = True
|
|
50
|
+
except ImportError:
|
|
51
|
+
haveArabic = False
|
|
52
|
+
|
|
53
|
+
import numpy
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
import pygame
|
|
57
|
+
havePygame = True
|
|
58
|
+
except Exception:
|
|
59
|
+
havePygame = False
|
|
60
|
+
|
|
61
|
+
defaultLetterHeight = {'cm': 1.0,
|
|
62
|
+
'deg': 1.0,
|
|
63
|
+
'degs': 1.0,
|
|
64
|
+
'degFlatPos': 1.0,
|
|
65
|
+
'degFlat': 1.0,
|
|
66
|
+
'norm': 0.1,
|
|
67
|
+
'height': 0.2,
|
|
68
|
+
'pix': 20,
|
|
69
|
+
'pixels': 20}
|
|
70
|
+
defaultWrapWidth = {'cm': 15.0,
|
|
71
|
+
'deg': 15.0,
|
|
72
|
+
'degs': 15.0,
|
|
73
|
+
'degFlatPos': 15.0,
|
|
74
|
+
'degFlat': 15.0,
|
|
75
|
+
'norm': 1,
|
|
76
|
+
'height': 1,
|
|
77
|
+
'pix': 500,
|
|
78
|
+
'pixels': 500}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TextStim(BaseVisualStim, ForeColorMixin, ContainerMixin):
|
|
82
|
+
"""Class of text stimuli to be displayed in a
|
|
83
|
+
:class:`~psychopy.visual.Window`
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, win,
|
|
87
|
+
text="Hello World",
|
|
88
|
+
font="",
|
|
89
|
+
pos=(0.0, 0.0),
|
|
90
|
+
depth=0,
|
|
91
|
+
rgb=None,
|
|
92
|
+
color=(1.0, 1.0, 1.0),
|
|
93
|
+
colorSpace='rgb',
|
|
94
|
+
opacity=1.0,
|
|
95
|
+
contrast=1.0,
|
|
96
|
+
units="",
|
|
97
|
+
ori=0.0,
|
|
98
|
+
height=None,
|
|
99
|
+
antialias=True,
|
|
100
|
+
bold=False,
|
|
101
|
+
italic=False,
|
|
102
|
+
alignHoriz=None,
|
|
103
|
+
alignVert=None,
|
|
104
|
+
alignText='center',
|
|
105
|
+
anchorHoriz='center',
|
|
106
|
+
anchorVert='center',
|
|
107
|
+
fontFiles=(),
|
|
108
|
+
wrapWidth=None,
|
|
109
|
+
flipHoriz=False,
|
|
110
|
+
flipVert=False,
|
|
111
|
+
languageStyle='LTR',
|
|
112
|
+
name=None,
|
|
113
|
+
autoLog=None):
|
|
114
|
+
"""
|
|
115
|
+
**Performance OBS:** in general, TextStim is slower than many other
|
|
116
|
+
visual stimuli, i.e. it takes longer to change some attributes.
|
|
117
|
+
In general, it's the attributes that affect the shapes of the letters:
|
|
118
|
+
``text``, ``height``, ``font``, ``bold`` etc.
|
|
119
|
+
These make the next .draw() slower because that sets the text again.
|
|
120
|
+
You can make the draw() quick by calling re-setting the text
|
|
121
|
+
(``myTextStim.text = myTextStim.text``) when you've changed the
|
|
122
|
+
parameters.
|
|
123
|
+
|
|
124
|
+
In general, other attributes which merely affect the presentation of
|
|
125
|
+
unchanged shapes are as fast as usual. This includes ``pos``,
|
|
126
|
+
``opacity`` etc.
|
|
127
|
+
|
|
128
|
+
The following attribute can only be set at initialization (see
|
|
129
|
+
further down for a list of attributes which can be changed after
|
|
130
|
+
initialization):
|
|
131
|
+
|
|
132
|
+
**languageStyle**
|
|
133
|
+
Apply settings to correctly display content from some languages
|
|
134
|
+
that are written right-to-left. Currently there are three (case-
|
|
135
|
+
insensitive) values for this parameter:
|
|
136
|
+
|
|
137
|
+
- ``'LTR'`` is the default, for typical left-to-right, Latin-style
|
|
138
|
+
languages.
|
|
139
|
+
- ``'RTL'`` will correctly display text in right-to-left languages
|
|
140
|
+
such as Hebrew. By applying the bidirectional algorithm, it
|
|
141
|
+
allows mixing portions of left-to-right content (such as numbers
|
|
142
|
+
or Latin script) within the string.
|
|
143
|
+
- ``'Arabic'`` applies the bidirectional algorithm but additionally
|
|
144
|
+
will _reshape_ Arabic characters so they appear in the cursive,
|
|
145
|
+
linked form that depends on neighbouring characters, rather than
|
|
146
|
+
in their isolated form. May also be applied in other scripts,
|
|
147
|
+
such as Farsi or Urdu, that use Arabic-style alphabets.
|
|
148
|
+
|
|
149
|
+
:Parameters:
|
|
150
|
+
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
# what local vars are defined (these are the init params) for use by
|
|
154
|
+
# __repr__
|
|
155
|
+
self._initParams = dir()
|
|
156
|
+
self._initParams.remove('self')
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
October 2018:
|
|
160
|
+
In place to remove the deprecation warning for pyglet.font.Text.
|
|
161
|
+
Temporary fix until pyglet.text.Label use is identical to pyglet.font.Text.
|
|
162
|
+
"""
|
|
163
|
+
warnings.filterwarnings(message='.*text.Label*', action='ignore')
|
|
164
|
+
|
|
165
|
+
super(TextStim, self).__init__(
|
|
166
|
+
win, units=units, name=name, autoLog=False)
|
|
167
|
+
|
|
168
|
+
if win.blendMode=='add':
|
|
169
|
+
logging.warning("Pyglet text does not honor the Window setting "
|
|
170
|
+
"`blendMode='add'` so 'avg' will be used for the "
|
|
171
|
+
"text (but objects drawn after can be added)")
|
|
172
|
+
self._needUpdate = True
|
|
173
|
+
self._needVertexUpdate = True
|
|
174
|
+
# use shaders if available by default, this is a good thing
|
|
175
|
+
self.__dict__['antialias'] = antialias
|
|
176
|
+
self.__dict__['font'] = font
|
|
177
|
+
self.__dict__['bold'] = bold
|
|
178
|
+
self.__dict__['italic'] = italic
|
|
179
|
+
# NB just a placeholder - real value set below
|
|
180
|
+
self.__dict__['text'] = ''
|
|
181
|
+
self.__dict__['depth'] = depth
|
|
182
|
+
self.__dict__['ori'] = ori
|
|
183
|
+
self.__dict__['flipHoriz'] = flipHoriz
|
|
184
|
+
self.__dict__['flipVert'] = flipVert
|
|
185
|
+
self.__dict__['languageStyle'] = languageStyle
|
|
186
|
+
self._pygletTextObj = None
|
|
187
|
+
self.pos = pos
|
|
188
|
+
# deprecated attributes
|
|
189
|
+
if alignVert:
|
|
190
|
+
self.__dict__['alignVert'] = alignVert
|
|
191
|
+
logging.warning("TextStim.alignVert is deprecated. Use the "
|
|
192
|
+
"anchorVert attribute instead")
|
|
193
|
+
# for compatibility, alignText was historically 'left'
|
|
194
|
+
anchorVert = alignHoriz
|
|
195
|
+
if alignHoriz:
|
|
196
|
+
self.__dict__['alignHoriz'] = alignHoriz
|
|
197
|
+
logging.warning("TextStim.alignHoriz is deprecated. Use alignText "
|
|
198
|
+
"and anchorHoriz attributes instead")
|
|
199
|
+
# for compatibility, alignText was historically 'left'
|
|
200
|
+
alignText, anchorHoriz = alignHoriz, alignHoriz
|
|
201
|
+
# alignment and anchors
|
|
202
|
+
self.alignText = alignText
|
|
203
|
+
self.anchorHoriz = anchorHoriz
|
|
204
|
+
self.anchorVert = anchorVert
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# generate the texture and list holders
|
|
208
|
+
self._listID = GL.glGenLists(1)
|
|
209
|
+
# pygame text needs a surface to render to:
|
|
210
|
+
if not self.win.winType in ["pyglet", "glfw"]:
|
|
211
|
+
self._texID = GL.GLuint()
|
|
212
|
+
GL.glGenTextures(1, ctypes.byref(self._texID))
|
|
213
|
+
|
|
214
|
+
# Color stuff
|
|
215
|
+
self.colorSpace = colorSpace
|
|
216
|
+
self.color = color
|
|
217
|
+
if rgb != None:
|
|
218
|
+
logging.warning("Use of rgb arguments to stimuli are deprecated. Please "
|
|
219
|
+
"use color and colorSpace args instead")
|
|
220
|
+
self.color = Color(rgb, 'rgb')
|
|
221
|
+
self.__dict__['fontFiles'] = []
|
|
222
|
+
self.fontFiles = list(fontFiles) # calls attributeSetter
|
|
223
|
+
self.setHeight(height, log=False) # calls setFont() at some point
|
|
224
|
+
# calls attributeSetter without log
|
|
225
|
+
setAttribute(self, 'wrapWidth', wrapWidth, log=False)
|
|
226
|
+
self.opacity = opacity
|
|
227
|
+
self.contrast = contrast
|
|
228
|
+
# self.width and self._fontHeightPix get set with text and
|
|
229
|
+
# calcSizeRendered is called
|
|
230
|
+
self.setText(text, log=False)
|
|
231
|
+
self._needUpdate = True
|
|
232
|
+
|
|
233
|
+
# set autoLog now that params have been initialised
|
|
234
|
+
wantLog = autoLog is None and self.win.autoLog
|
|
235
|
+
self.__dict__['autoLog'] = autoLog or wantLog
|
|
236
|
+
if self.autoLog:
|
|
237
|
+
logging.exp("Created %s = %s" % (self.name, str(self)))
|
|
238
|
+
|
|
239
|
+
def __del__(self):
|
|
240
|
+
if GL: # because of pytest fail otherwise
|
|
241
|
+
try:
|
|
242
|
+
GL.glDeleteLists(self._listID, 1)
|
|
243
|
+
except (ImportError, ModuleNotFoundError, TypeError):
|
|
244
|
+
pass # if pyglet no longer exists
|
|
245
|
+
|
|
246
|
+
@attributeSetter
|
|
247
|
+
def height(self, height):
|
|
248
|
+
"""The height of the letters (Float/int or None = set default).
|
|
249
|
+
|
|
250
|
+
Height includes the entire box that surrounds the letters
|
|
251
|
+
in the font. The width of the letters is then defined by the font.
|
|
252
|
+
|
|
253
|
+
:ref:`Operations <attrib-operations>` supported."""
|
|
254
|
+
# height in pix (needs to be done after units which is done during
|
|
255
|
+
# _Base.__init__)
|
|
256
|
+
if height is None:
|
|
257
|
+
if self.units in defaultLetterHeight:
|
|
258
|
+
height = defaultLetterHeight[self.units]
|
|
259
|
+
else:
|
|
260
|
+
msg = ("TextStim does now know a default letter height "
|
|
261
|
+
"for units %s")
|
|
262
|
+
raise AttributeError(msg % repr(self.units))
|
|
263
|
+
self.__dict__['height'] = height
|
|
264
|
+
self._heightPix = convertToPix(pos=numpy.array([0, 0]),
|
|
265
|
+
vertices=numpy.array([0, self.height]),
|
|
266
|
+
units=self.units, win=self.win)[1]
|
|
267
|
+
|
|
268
|
+
# need to update the font to reflect the change
|
|
269
|
+
self.setFont(self.font, log=False)
|
|
270
|
+
return self.__dict__['height']
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def size(self):
|
|
274
|
+
self.size = (self.height*len(self.text), self.height)
|
|
275
|
+
return WindowMixin.size.fget(self)
|
|
276
|
+
|
|
277
|
+
@size.setter
|
|
278
|
+
def size(self, value):
|
|
279
|
+
WindowMixin.size.fset(self, value)
|
|
280
|
+
self.height = getattr(self._size, self.units)[1]
|
|
281
|
+
|
|
282
|
+
def setHeight(self, height, log=None):
|
|
283
|
+
"""Usually you can use 'stim.attribute = value' syntax instead,
|
|
284
|
+
but use this method if you need to suppress the log message. """
|
|
285
|
+
setAttribute(self, 'height', height, log)
|
|
286
|
+
|
|
287
|
+
@attributeSetter
|
|
288
|
+
def font(self, font):
|
|
289
|
+
"""String. Set the font to be used for text rendering. font should
|
|
290
|
+
be a string specifying the name of the font (in system resources).
|
|
291
|
+
"""
|
|
292
|
+
self.__dict__['font'] = None # until we find one
|
|
293
|
+
if self.win.winType in ["pyglet", "glfw"]:
|
|
294
|
+
self._font = pyglet.font.load(font, int(self._heightPix),
|
|
295
|
+
dpi=72, italic=self.italic,
|
|
296
|
+
bold=self.bold)
|
|
297
|
+
self.__dict__['font'] = font
|
|
298
|
+
else:
|
|
299
|
+
if font is None or len(font) == 0:
|
|
300
|
+
self.__dict__['font'] = pygame.font.get_default_font()
|
|
301
|
+
elif font in pygame.font.get_fonts():
|
|
302
|
+
self.__dict__['font'] = font
|
|
303
|
+
elif type(font) == str:
|
|
304
|
+
# try to find a xxx.ttf file for it
|
|
305
|
+
# check for possible matching filenames
|
|
306
|
+
fontFilenames = glob.glob(font + '*')
|
|
307
|
+
if len(fontFilenames) > 0:
|
|
308
|
+
for thisFont in fontFilenames:
|
|
309
|
+
if thisFont[-4:] in ['.TTF', '.ttf']:
|
|
310
|
+
# take the first match
|
|
311
|
+
self.__dict__['font'] = thisFont
|
|
312
|
+
break # stop at the first one we find
|
|
313
|
+
# trhen check if we were successful
|
|
314
|
+
if self.font is None and font != "":
|
|
315
|
+
# we didn't find a ttf filename
|
|
316
|
+
msg = ("Found %s but it doesn't end .ttf. "
|
|
317
|
+
"Using default font.")
|
|
318
|
+
logging.warning(msg % fontFilenames[0])
|
|
319
|
+
self.__dict__['font'] = pygame.font.get_default_font()
|
|
320
|
+
|
|
321
|
+
if self.font is not None and os.path.isfile(self.font):
|
|
322
|
+
self._font = pygame.font.Font(self.font, int(
|
|
323
|
+
self._heightPix), italic=self.italic, bold=self.bold)
|
|
324
|
+
else:
|
|
325
|
+
try:
|
|
326
|
+
self._font = pygame.font.SysFont(
|
|
327
|
+
self.font, int(self._heightPix), italic=self.italic,
|
|
328
|
+
bold=self.bold)
|
|
329
|
+
self.__dict__['font'] = font
|
|
330
|
+
logging.info('using sysFont ' + str(font))
|
|
331
|
+
except Exception:
|
|
332
|
+
self.__dict__['font'] = pygame.font.get_default_font()
|
|
333
|
+
msg = ("Couldn't find font %s on the system. Using %s "
|
|
334
|
+
"instead! Font names should be written as "
|
|
335
|
+
"concatenated names all in lower case.\ne.g. "
|
|
336
|
+
"'arial', 'monotypecorsiva', 'rockwellextra', ...")
|
|
337
|
+
logging.error(msg % (font, self.font))
|
|
338
|
+
self._font = pygame.font.SysFont(
|
|
339
|
+
self.font, int(self._heightPix), italic=self.italic,
|
|
340
|
+
bold=self.bold)
|
|
341
|
+
# re-render text after a font change
|
|
342
|
+
self._needSetText = True
|
|
343
|
+
|
|
344
|
+
def setFont(self, font, log=None):
|
|
345
|
+
"""Usually you can use 'stim.attribute = value' syntax instead,
|
|
346
|
+
but use this method if you need to suppress the log message.
|
|
347
|
+
"""
|
|
348
|
+
setAttribute(self, 'font', font, log)
|
|
349
|
+
|
|
350
|
+
@attributeSetter
|
|
351
|
+
def text(self, text):
|
|
352
|
+
"""The text to be rendered. Use \\\\n to make new lines.
|
|
353
|
+
|
|
354
|
+
Issues: May be slow, and pyglet has a memory leak when setting text.
|
|
355
|
+
For these reasons, this function checks so that it only updates the
|
|
356
|
+
text if it has changed. So scripts can safely set the text on every
|
|
357
|
+
frame, with no need to check if it has actually altered.
|
|
358
|
+
"""
|
|
359
|
+
if text == self.text: # only update for a change
|
|
360
|
+
return
|
|
361
|
+
if text is not None:
|
|
362
|
+
text = str(text) # make sure we have unicode object to render
|
|
363
|
+
|
|
364
|
+
# deal with some international text issues. Only relevant for Python:
|
|
365
|
+
# online experiments use web technologies and handle this seamlessly.
|
|
366
|
+
style = self.languageStyle.lower() # be flexible with case
|
|
367
|
+
if style == 'arabic' and haveArabic:
|
|
368
|
+
# reshape Arabic characters from their isolated form so that
|
|
369
|
+
# they flow and join correctly to their neighbours:
|
|
370
|
+
text = arabic_reshaper.reshape(text)
|
|
371
|
+
if style == 'rtl' or style == 'arabic' and haveArabic:
|
|
372
|
+
# deal with right-to-left text presentation by applying the
|
|
373
|
+
# bidirectional algorithm:
|
|
374
|
+
text = bidi_algorithm.get_display(text)
|
|
375
|
+
# no action needed for default 'ltr' (left-to-right) option
|
|
376
|
+
|
|
377
|
+
self.__dict__['text'] = text
|
|
378
|
+
|
|
379
|
+
self._setTextShaders(text)
|
|
380
|
+
|
|
381
|
+
self._needSetText = False
|
|
382
|
+
return self.__dict__['text']
|
|
383
|
+
|
|
384
|
+
def setText(self, text=None, log=None):
|
|
385
|
+
"""Usually you can use 'stim.attribute = value' syntax instead,
|
|
386
|
+
but use this method if you need to suppress the log message.
|
|
387
|
+
"""
|
|
388
|
+
setAttribute(self, 'text', text, log)
|
|
389
|
+
|
|
390
|
+
def _setTextShaders(self, value=None):
|
|
391
|
+
"""Set the text to be rendered using the current font
|
|
392
|
+
"""
|
|
393
|
+
if self.win.winType in ["pyglet", "glfw"]:
|
|
394
|
+
rgba255 = self._foreColor.rgba255
|
|
395
|
+
rgba255[3] = rgba255[3]*255
|
|
396
|
+
rgba255 = [int(c) for c in rgba255]
|
|
397
|
+
self._pygletTextObj = pyglet.text.Label(
|
|
398
|
+
self.text, self.font, int(self._heightPix*0.75),
|
|
399
|
+
italic=self.italic,
|
|
400
|
+
bold=self.bold,
|
|
401
|
+
anchor_x=self.anchorHoriz,
|
|
402
|
+
anchor_y=self.anchorVert, # the point we rotate around
|
|
403
|
+
align=self.alignText,
|
|
404
|
+
color=rgba255,
|
|
405
|
+
multiline=True, width=self._wrapWidthPix) # width of the frame
|
|
406
|
+
self.width = self._pygletTextObj.width
|
|
407
|
+
self._fontHeightPix = self._pygletTextObj.height
|
|
408
|
+
else:
|
|
409
|
+
self._surf = self._font.render(value, self.antialias,
|
|
410
|
+
[255, 255, 255])
|
|
411
|
+
self.width, self._fontHeightPix = self._surf.get_size()
|
|
412
|
+
|
|
413
|
+
if self.antialias:
|
|
414
|
+
smoothing = GL.GL_LINEAR
|
|
415
|
+
else:
|
|
416
|
+
smoothing = GL.GL_NEAREST
|
|
417
|
+
# generate the textures from pygame surface
|
|
418
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
419
|
+
# bind that name to the target
|
|
420
|
+
GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)
|
|
421
|
+
GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, 4, self.width,
|
|
422
|
+
self._fontHeightPix,
|
|
423
|
+
GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
|
|
424
|
+
pygame.image.tostring(self._surf, "RGBA", 1))
|
|
425
|
+
# linear smoothing if texture is stretched?
|
|
426
|
+
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
|
|
427
|
+
smoothing)
|
|
428
|
+
# but nearest pixel value if it's compressed?
|
|
429
|
+
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
|
|
430
|
+
smoothing)
|
|
431
|
+
|
|
432
|
+
self._needSetText = False
|
|
433
|
+
self._needUpdate = True
|
|
434
|
+
|
|
435
|
+
def _updateListShaders(self):
|
|
436
|
+
"""Only used with pygame text - pyglet handles all from the draw()
|
|
437
|
+
"""
|
|
438
|
+
if self._needSetText:
|
|
439
|
+
self.setText(log=False)
|
|
440
|
+
GL.glNewList(self._listID, GL.GL_COMPILE)
|
|
441
|
+
# GL.glPushMatrix()
|
|
442
|
+
|
|
443
|
+
# setup the shaderprogram
|
|
444
|
+
# no need to do texture maths so no need for programs?
|
|
445
|
+
# If we're using pyglet then this list won't be called, and for pygame
|
|
446
|
+
# shaders aren't enabled
|
|
447
|
+
GL.glUseProgram(0) # self.win._progSignedTex)
|
|
448
|
+
# GL.glUniform1i(GL.glGetUniformLocation(self.win._progSignedTex,
|
|
449
|
+
# "texture"), 0) # set the texture to be texture unit 0
|
|
450
|
+
|
|
451
|
+
# coords:
|
|
452
|
+
if self.alignHoriz in ['center', 'centre']:
|
|
453
|
+
left = -self.width/2.0
|
|
454
|
+
right = self.width/2.0
|
|
455
|
+
elif self.alignHoriz == 'right':
|
|
456
|
+
left = -self.width
|
|
457
|
+
right = 0.0
|
|
458
|
+
else:
|
|
459
|
+
left = 0.0
|
|
460
|
+
right = self.width
|
|
461
|
+
# how much to move bottom
|
|
462
|
+
if self.alignVert in ['center', 'centre']:
|
|
463
|
+
bottom = -self._fontHeightPix/2.0
|
|
464
|
+
top = self._fontHeightPix/2.0
|
|
465
|
+
elif self.alignVert == 'top':
|
|
466
|
+
bottom = -self._fontHeightPix
|
|
467
|
+
top = 0
|
|
468
|
+
else:
|
|
469
|
+
bottom = 0.0
|
|
470
|
+
top = self._fontHeightPix
|
|
471
|
+
# there seems to be a rounding err in pygame font textures
|
|
472
|
+
Btex, Ttex, Ltex, Rtex = -0.01, 0.98, 0, 1.0
|
|
473
|
+
|
|
474
|
+
# unbind the mask texture regardless
|
|
475
|
+
GL.glActiveTexture(GL.GL_TEXTURE1)
|
|
476
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
477
|
+
GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
|
|
478
|
+
if self.win.winType in ["pyglet", "glfw"]:
|
|
479
|
+
# unbind the main texture
|
|
480
|
+
GL.glActiveTexture(GL.GL_TEXTURE0)
|
|
481
|
+
# GL.glActiveTextureARB(GL.GL_TEXTURE0_ARB)
|
|
482
|
+
# the texture is specified by pyglet.font.GlyphString.draw()
|
|
483
|
+
GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
|
|
484
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
485
|
+
else:
|
|
486
|
+
# bind the appropriate main texture
|
|
487
|
+
GL.glActiveTexture(GL.GL_TEXTURE0)
|
|
488
|
+
GL.glBindTexture(GL.GL_TEXTURE_2D, self._texID)
|
|
489
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
490
|
+
|
|
491
|
+
if self.win.winType in ["pyglet", "glfw"]:
|
|
492
|
+
GL.glActiveTexture(GL.GL_TEXTURE0)
|
|
493
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
494
|
+
self._pygletTextObj.draw()
|
|
495
|
+
else:
|
|
496
|
+
# draw a 4 sided polygon
|
|
497
|
+
GL.glBegin(GL.GL_QUADS)
|
|
498
|
+
# right bottom
|
|
499
|
+
GL.glMultiTexCoord2f(GL.GL_TEXTURE0, Rtex, Btex)
|
|
500
|
+
GL.glVertex3f(right, bottom, 0)
|
|
501
|
+
# left bottom
|
|
502
|
+
GL.glMultiTexCoord2f(GL.GL_TEXTURE0, Ltex, Btex)
|
|
503
|
+
GL.glVertex3f(left, bottom, 0)
|
|
504
|
+
# left top
|
|
505
|
+
GL.glMultiTexCoord2f(GL.GL_TEXTURE0, Ltex, Ttex)
|
|
506
|
+
GL.glVertex3f(left, top, 0)
|
|
507
|
+
# right top
|
|
508
|
+
GL.glMultiTexCoord2f(GL.GL_TEXTURE0, Rtex, Ttex)
|
|
509
|
+
GL.glVertex3f(right, top, 0)
|
|
510
|
+
GL.glEnd()
|
|
511
|
+
|
|
512
|
+
GL.glDisable(GL.GL_TEXTURE_2D)
|
|
513
|
+
GL.glUseProgram(0)
|
|
514
|
+
# GL.glPopMatrix()
|
|
515
|
+
|
|
516
|
+
GL.glEndList()
|
|
517
|
+
self._needUpdate = False
|
|
518
|
+
|
|
519
|
+
@attributeSetter
|
|
520
|
+
def flipHoriz(self, value):
|
|
521
|
+
"""If set to True then the text will be flipped left-to-right. The
|
|
522
|
+
flip is relative to the original, not relative to the current state.
|
|
523
|
+
"""
|
|
524
|
+
self.__dict__['flipHoriz'] = value
|
|
525
|
+
|
|
526
|
+
def setFlipHoriz(self, newVal=True, log=None):
|
|
527
|
+
"""Usually you can use 'stim.attribute = value' syntax instead,
|
|
528
|
+
but use this method if you need to suppress the log message.
|
|
529
|
+
"""
|
|
530
|
+
setAttribute(self, 'flipHoriz', newVal, log)
|
|
531
|
+
|
|
532
|
+
@attributeSetter
|
|
533
|
+
def flipVert(self, value):
|
|
534
|
+
"""If set to True then the text will be flipped top-to-bottom. The
|
|
535
|
+
flip is relative to the original, not relative to the current state.
|
|
536
|
+
"""
|
|
537
|
+
self.__dict__['flipVert'] = value
|
|
538
|
+
|
|
539
|
+
def setFlipVert(self, newVal=True, log=None):
|
|
540
|
+
"""Usually you can use 'stim.attribute = value' syntax instead,
|
|
541
|
+
but use this method if you need to suppress the log message
|
|
542
|
+
"""
|
|
543
|
+
setAttribute(self, 'flipVert', newVal, log)
|
|
544
|
+
|
|
545
|
+
def setFlip(self, direction, log=None):
|
|
546
|
+
"""(used by Builder to simplify the dialog)
|
|
547
|
+
"""
|
|
548
|
+
if direction == 'vert':
|
|
549
|
+
self.setFlipVert(True, log=log)
|
|
550
|
+
elif direction == 'horiz':
|
|
551
|
+
self.setFlipHoriz(True, log=log)
|
|
552
|
+
|
|
553
|
+
@attributeSetter
|
|
554
|
+
def antialias(self, value):
|
|
555
|
+
"""Allow antialiasing the text (True or False). Sets text, slow.
|
|
556
|
+
"""
|
|
557
|
+
self.__dict__['antialias'] = value
|
|
558
|
+
self._needSetText = True
|
|
559
|
+
|
|
560
|
+
@attributeSetter
|
|
561
|
+
def bold(self, value):
|
|
562
|
+
"""Make the text bold (True, False) (better to use a bold font name).
|
|
563
|
+
"""
|
|
564
|
+
self.__dict__['bold'] = value
|
|
565
|
+
self.font = self.font # call attributeSetter
|
|
566
|
+
|
|
567
|
+
@attributeSetter
|
|
568
|
+
def italic(self, value):
|
|
569
|
+
"""True/False.
|
|
570
|
+
Make the text italic (better to use a italic font name).
|
|
571
|
+
"""
|
|
572
|
+
self.__dict__['italic'] = value
|
|
573
|
+
self.font = self.font # call attributeSetter
|
|
574
|
+
|
|
575
|
+
@attributeSetter
|
|
576
|
+
def alignHoriz(self, value):
|
|
577
|
+
"""Deprecated in PsychoPy 3.3. Use `alignText` and `anchorHoriz`
|
|
578
|
+
instead
|
|
579
|
+
"""
|
|
580
|
+
self.__dict__['alignHoriz'] = value
|
|
581
|
+
self._needSetText = True
|
|
582
|
+
|
|
583
|
+
@attributeSetter
|
|
584
|
+
def alignVert(self, value):
|
|
585
|
+
"""Deprecated in PsychoPy 3.3. Use `anchorVert`
|
|
586
|
+
"""
|
|
587
|
+
self.__dict__['alignVert'] = value
|
|
588
|
+
self._needSetText = True
|
|
589
|
+
|
|
590
|
+
@attributeSetter
|
|
591
|
+
def alignText(self, value):
|
|
592
|
+
"""Aligns the text content within the bounding box ('left', 'right' or
|
|
593
|
+
'center')
|
|
594
|
+
See also `anchorX` to set alignment of the box itself relative to pos
|
|
595
|
+
"""
|
|
596
|
+
self.__dict__['alignText'] = value
|
|
597
|
+
self._needSetText = True
|
|
598
|
+
|
|
599
|
+
@attributeSetter
|
|
600
|
+
def anchorHoriz(self, value):
|
|
601
|
+
"""The horizontal alignment ('left', 'right' or 'center')
|
|
602
|
+
"""
|
|
603
|
+
self.__dict__['anchorHoriz'] = value
|
|
604
|
+
self._needSetText = True
|
|
605
|
+
|
|
606
|
+
@attributeSetter
|
|
607
|
+
def anchorVert(self, value):
|
|
608
|
+
"""The vertical alignment ('top', 'bottom' or 'center') of the box
|
|
609
|
+
relative to the text `pos`.
|
|
610
|
+
"""
|
|
611
|
+
self.__dict__['anchorVert'] = value
|
|
612
|
+
self._needSetText = True
|
|
613
|
+
|
|
614
|
+
@attributeSetter
|
|
615
|
+
def fontFiles(self, fontFiles):
|
|
616
|
+
"""A list of additional files if the font is not in the standard
|
|
617
|
+
system location (include the full path).
|
|
618
|
+
|
|
619
|
+
OBS: fonts are added every time this value is set. Previous are
|
|
620
|
+
not deleted.
|
|
621
|
+
|
|
622
|
+
E.g.::
|
|
623
|
+
|
|
624
|
+
stim.fontFiles = ['SpringRage.ttf'] # load file(s)
|
|
625
|
+
stim.font = 'SpringRage' # set to font
|
|
626
|
+
"""
|
|
627
|
+
self.__dict__['fontFiles'] += fontFiles
|
|
628
|
+
for thisFont in fontFiles:
|
|
629
|
+
pyglet.font.add_file(thisFont)
|
|
630
|
+
|
|
631
|
+
@attributeSetter
|
|
632
|
+
def wrapWidth(self, wrapWidth):
|
|
633
|
+
"""Int/float or None (set default).
|
|
634
|
+
The width the text should run before wrapping.
|
|
635
|
+
|
|
636
|
+
:ref:`Operations <attrib-operations>` supported.
|
|
637
|
+
"""
|
|
638
|
+
if wrapWidth is None:
|
|
639
|
+
if self.units in defaultWrapWidth:
|
|
640
|
+
wrapWidth = defaultWrapWidth[self.units]
|
|
641
|
+
else:
|
|
642
|
+
msg = "TextStim does now know a default wrap width for units %s"
|
|
643
|
+
raise AttributeError(msg % repr(self.units))
|
|
644
|
+
self.__dict__['wrapWidth'] = wrapWidth
|
|
645
|
+
verts = numpy.array([self.wrapWidth, 0])
|
|
646
|
+
self._wrapWidthPix = convertToPix(pos=numpy.array([0, 0]),
|
|
647
|
+
vertices=verts,
|
|
648
|
+
units=self.units, win=self.win)[0]
|
|
649
|
+
self._needSetText = True
|
|
650
|
+
|
|
651
|
+
@property
|
|
652
|
+
def boundingBox(self):
|
|
653
|
+
"""(read only) attribute representing the bounding box of the text
|
|
654
|
+
(w,h). This differs from `width` in that the width represents the
|
|
655
|
+
width of the margins, which might differ from the width of the text
|
|
656
|
+
within them.
|
|
657
|
+
|
|
658
|
+
NOTE: currently always returns the size in pixels
|
|
659
|
+
(this will change to return in stimulus units)
|
|
660
|
+
"""
|
|
661
|
+
if hasattr(self._pygletTextObj, 'content_width'):
|
|
662
|
+
w, h = (self._pygletTextObj.content_width,
|
|
663
|
+
self._pygletTextObj.content_height)
|
|
664
|
+
else:
|
|
665
|
+
w, h = (self._pygletTextObj._layout.content_width,
|
|
666
|
+
self._pygletTextObj._layout.content_height)
|
|
667
|
+
return w, h
|
|
668
|
+
|
|
669
|
+
@property
|
|
670
|
+
def posPix(self):
|
|
671
|
+
"""This determines the coordinates in pixels of the position for the
|
|
672
|
+
current stimulus, accounting for pos and units. This property should
|
|
673
|
+
automatically update if `pos` is changed"""
|
|
674
|
+
# because this is a property getter we can check /on-access/ if it
|
|
675
|
+
# needs updating :-)
|
|
676
|
+
if self._needVertexUpdate:
|
|
677
|
+
self.__dict__['posPix'] = self._pos.pix
|
|
678
|
+
self._needVertexUpdate = False
|
|
679
|
+
return self.__dict__['posPix']
|
|
680
|
+
|
|
681
|
+
def updateOpacity(self):
|
|
682
|
+
self._setTextShaders(value=self.text)
|
|
683
|
+
|
|
684
|
+
def draw(self, win=None):
|
|
685
|
+
"""
|
|
686
|
+
Draw the stimulus in its relevant window. You must call
|
|
687
|
+
this method after every MyWin.flip() if you want the
|
|
688
|
+
stimulus to appear on that frame and then update the screen
|
|
689
|
+
again.
|
|
690
|
+
|
|
691
|
+
If win is specified then override the normal window of this stimulus.
|
|
692
|
+
"""
|
|
693
|
+
if win is None:
|
|
694
|
+
win = self.win
|
|
695
|
+
self._selectWindow(win)
|
|
696
|
+
blendMode = win.blendMode # keep track for reset later
|
|
697
|
+
|
|
698
|
+
GL.glPushMatrix()
|
|
699
|
+
# for PyOpenGL this is necessary despite pop/PushMatrix, (not for
|
|
700
|
+
# pyglet)
|
|
701
|
+
GL.glLoadIdentity()
|
|
702
|
+
#scale and rotate
|
|
703
|
+
prevScale = win.setScale('pix') # to units for translations
|
|
704
|
+
# NB depth is set already
|
|
705
|
+
GL.glTranslatef(self.posPix[0], self.posPix[1], 0)
|
|
706
|
+
GL.glRotatef(-self.ori, 0.0, 0.0, 1.0)
|
|
707
|
+
# back to pixels for drawing surface
|
|
708
|
+
win.setScale('pix', None, prevScale)
|
|
709
|
+
GL.glScalef((1, -1)[self.flipHoriz], (1, -1)
|
|
710
|
+
[self.flipVert], 1) # x,y,z; -1=flipped
|
|
711
|
+
|
|
712
|
+
# setup color
|
|
713
|
+
GL.glColor4f(*self._foreColor.render('rgba1'))
|
|
714
|
+
|
|
715
|
+
GL.glUseProgram(self.win._progSignedTexFont)
|
|
716
|
+
# GL.glUniform3iv(GL.glGetUniformLocation(
|
|
717
|
+
# self.win._progSignedTexFont, "rgb"), 1,
|
|
718
|
+
# desiredRGB.ctypes.data_as(ctypes.POINTER(ctypes.c_float)))
|
|
719
|
+
# # set the texture to be texture unit 0
|
|
720
|
+
GL.glUniform3f(
|
|
721
|
+
GL.glGetUniformLocation(self.win._progSignedTexFont, b"rgb"),
|
|
722
|
+
*self._foreColor.render('rgb1'))
|
|
723
|
+
|
|
724
|
+
# should text have a depth or just on top?
|
|
725
|
+
GL.glDisable(GL.GL_DEPTH_TEST)
|
|
726
|
+
# update list if necss and then call it
|
|
727
|
+
if win.winType in ["pyglet", "glfw"]:
|
|
728
|
+
if self._needSetText:
|
|
729
|
+
self.setText()
|
|
730
|
+
|
|
731
|
+
# unbind the mask texture regardless
|
|
732
|
+
GL.glActiveTexture(GL.GL_TEXTURE1)
|
|
733
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
734
|
+
GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
|
|
735
|
+
# unbind the main texture
|
|
736
|
+
GL.glActiveTexture(GL.GL_TEXTURE0)
|
|
737
|
+
GL.glEnable(GL.GL_TEXTURE_2D)
|
|
738
|
+
# then allow pyglet to bind and use texture during drawing
|
|
739
|
+
|
|
740
|
+
self._pygletTextObj.draw()
|
|
741
|
+
GL.glDisable(GL.GL_TEXTURE_2D)
|
|
742
|
+
else:
|
|
743
|
+
# for pygame we should (and can) use a drawing list
|
|
744
|
+
if self._needUpdate:
|
|
745
|
+
self._updateList()
|
|
746
|
+
GL.glCallList(self._listID)
|
|
747
|
+
|
|
748
|
+
# pyglets text.draw() method alters the blend func so reassert ours
|
|
749
|
+
win.setBlendMode(blendMode, log=False)
|
|
750
|
+
GL.glUseProgram(0)
|
|
751
|
+
# GL.glEnable(GL.GL_DEPTH_TEST) # Enables Depth Testing
|
|
752
|
+
GL.glPopMatrix()
|