超时

基本用法

timeout_seconds

为了给你的页面设置一个时间限制,添加 timeout_seconds:

class Page1(Page):
    timeout_seconds = 60

在时间耗尽之后,页面会自动提交。

如果你在生产服务器(prodserver)上运行,那么页面总会被提交,即使用户关掉了浏览器。然而,如果你在开发服务器上(zipserver or devserver)运行,这将不会发生。

如果你需要超时能够被动态决定,使用 get_timeout_seconds

timeout_happened

你可以检查页面是否是因超时而提交的:

class Page1(Page):
    form_model = 'player'
    form_fields = ['xyz']
    timeout_seconds = 60

    @staticmethod
    def before_next_page(player, timeout_happened):
        if timeout_happened:
            # you may want to fill a default value for any form fields,
            # because otherwise they may be left null.
            player.xyz = False

get_timeout_seconds

有一种更加灵活的替代 timeout_seconds 的方法,使得你可以让超时取决于 playerplayer.session,等等。

举例来说:

class MyPage(Page):

    @staticmethod
    def get_timeout_seconds(player):
        return player.my_page_timeout_seconds

或者使用定制的session config参数 (参见 选择特定实验组进行游戏)。

def get_timeout_seconds(player):
    session = player.session

    return session.config['my_page_timeout_seconds']

高级技巧

因超时提交的表单

如果表单由于超时被自动提交,oTree会试着保存表单在提交时字段所填入的内容。如果表单中的字段中有一个错误如缺失或非法,oTree会自动对数值字段填入 0 ,布尔字段填入 False ,字符串字段填入 ''

如果你想要舍弃这些自动提交的值,你可以检查 timeout_happened ,如果需要的话,你可以覆写这些值。

如果 error_message() 函数失败了,那么整个表单可能是非法的,故整个表单都会被舍弃。

跨越多个页面的超时

你可以使用 get_timeout_seconds 来创建跨越多个页面甚至是整个会话的超时。技巧是定义一个固定的”到期时间”,然后在每个页面上,让 get_timeout_seconds 返回当前时间到到期时间的秒数。

首先,选择开始计时器的地方。这可以是一个名为”开始”的页面,显示如”准备好开始后按下按钮”的文本。当用户点击”下一页”按钮后, before_next_page 会被执行:

class Start(Page):

    @staticmethod
    def before_next_page(player, timeout_happened):
        participant = player.participant
        import time

        # remember to add 'expiry' to PARTICIPANT_FIELDS.
        participant.expiry = time.time() + 5*60

(你也可以在 after_all_players_arrivecreating_session 中启动计时器,并将到期时间存储在 会话字段中,如果它对会话中的所有人都相同的话。)

然后,每个页面的 get_timeout_seconds 应当为直到到期时间的秒数:

class Page1(Page):

    @staticmethod
    def get_timeout_seconds(player):
        participant = player.participant
        import time
        return participant.expiry - time.time()

当时间耗尽后, get_timeout_seconds 会返回0或者一个负数,这会导致页面立即被加载并自动提交。这意味着所有剩余页面会快速闪过参与者的屏幕,这通常不是想要的结果。所以你应该使用 is_displayed 来跳过这些页面,如果实际上参与者已经没有时间阅读这些页面。

def get_timeout_seconds(player):
    participant = player.participant
    import time
    return participant.expiry - time.time()

class Page1(Page):
    get_timeout_seconds = get_timeout_seconds

    @staticmethod
    def is_displayed(player):
        return get_timeout_seconds(player) > 3

计时器的默认文本是”完成此页面的剩余时间为:”。但是如果你的计时器是跨越多个页面的,你应当通过设定 timer_text 让其表达得更准确一些:

class Page1(Page):

    timer_text = 'Time left to complete this section:'

    @staticmethod
    def get_timeout_seconds(player):
        ...

自定义计时器

隐藏计时器

如果你希望隐藏计时器,使用下面的CSS:

.otree-timer {
    display: none;
}

改变计时器的行为

计时器的功能由 jQuery Countdown 提供。你可以通过jQuery的 .on()off() 添加和去除事件处理器来改变它的行为。

oTree为 update.countdownfinish.countdown 设置了事件处理器,故如果你想要更改它们,你可以使用 off() 解除它们,并/或通过 on() 添加你自己的事件处理器。倒计时元素为 .otree-timer__time-left

例如,隐藏计时器直到还剩10秒,

<style>
    .otree-timer {
        display: none;
    }
</style>

<script>
    document.addEventListener("DOMContentLoaded", function (event) {
        $('.otree-timer__time-left').on('update.countdown', function (event) {
            if (event.offset.totalSeconds === 10) {
                $('.otree-timer').show();
            }
        });
    });
</script>

为了避免将此代码复制到每一个页面上,将其放在一个可包含的模板中。

Note: even if you turn off the finish.countdown event handler, the page will still be submitted on the server side. So, instead you should use the technique described in 不会提交页面的超时.

不会提交页面的超时

如果你只是想用温和的超时,那么你根本不需要内置的计时器。你应该使用JavaScript创建自己的超时,例如:

setTimeout(
    function () {
        alert("Time has run out. Please make your decision.");
    },
    60*1000 // 60 seconds
);