超时

基本用法

timeout_seconds

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

class Page1(Page):
    timeout_seconds = 60

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

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

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

timeout_happened

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

class Page1(Page):
    timeout_seconds = 60

    @staticmethod
    def before_next_page(player, timeout_happened):
        if timeout_happened:
            player.xyz = True

get_timeout_seconds

This is a more flexible alternative to timeout_seconds, so that you can make the timeout depend on player, player.session, etc.

举例来说:

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 is_displayed(player):
        return player.round_number == 1

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

        # user has 5 minutes to complete as many pages as possible
        participant.vars['expiry'] = time.time() + 5*60

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

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

class Page1(Page):

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

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

def get_timeout_seconds(player):
    participant = player.participant
    import time
    return participant.vars['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>
    $(function () {
        $('.otree-timer__time-left').on('update.countdown', function (event) {
            if (event.offset.totalSeconds === 10) {
                $('.otree-timer').show();
            }
        });
    });
</script>

To avoid copy-pasting this code on every page, put it in an includable template.

注意:即使你关闭 finish.countdown 事件处理器来避免提交页面,如果你仍运行着timeoutworker,页面仍会在服务器端被提交。所以你应当使用 不会提交页面的超时 中的技巧而不是这样做。

不会提交页面的超时

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

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

最小页面停留时间

如果你想让用户在页面上 至少 花上一定量的时间,你可以使用一些简单的JavaScript:隐藏下一步按钮(使用 .otree-btn-next 选择器),然后使用 setTimeout 来在一段时间后重新显示按钮。