<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>项目 &#8211; 李辉 / Grey Li</title>
	<atom:link href="https://greyli.com/tag/%e9%a1%b9%e7%9b%ae/feed/" rel="self" type="application/rss+xml" />
	<link>https://greyli.com</link>
	<description>一个编程和写作爱好者的在线记事本</description>
	<lastBuildDate>Sat, 15 Nov 2025 10:55:15 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.9.26</generator>

<image>
	<url>https://greyli.com/wp-content/uploads/2025/03/avatar-500-compressed-144x144.jpg</url>
	<title>项目 &#8211; 李辉 / Grey Li</title>
	<link>https://greyli.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Flask实践：计时器</title>
		<link>https://greyli.com/flask-practice-timer/</link>
		<comments>https://greyli.com/flask-practice-timer/#respond</comments>
		<pubDate>Wed, 09 Nov 2016 04:45:11 +0000</pubDate>
		<dc:creator><![CDATA[李辉]]></dc:creator>
				<category><![CDATA[计算机与编程]]></category>
		<category><![CDATA[Flask]]></category>
		<category><![CDATA[项目]]></category>

		<guid isPermaLink="false">http://withlihui.com/?p=1212</guid>
		<description><![CDATA[Demo体验：计时器 &#8211; Hello, Flask!难度：1涉及知识点：URL变量 &#8211; [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Demo体验：<a href="http://timertimer.herokuapp.com/" data-editable="true" data-title="计时器 - Hello, Flask!">计时器 &#8211; Hello, Flask!</a><br />难度：1<br />涉及知识点：URL变量</p>
<div id="attachment_1213" style="width: 1610px" class="wp-caption aligncenter"><a href="http://greyli.com/wp-content/uploads/2016/11/timer2.png" rel="attachment wp-att-1213"><img class="size-full wp-image-1213" src="http://greyli.com/wp-content/uploads/2016/11/timer2.png" alt="计时界面" width="1600" height="761" srcset="https://greyli.com/wp-content/uploads/2016/11/timer2.png 1600w, https://greyli.com/wp-content/uploads/2016/11/timer2-150x71.png 150w, https://greyli.com/wp-content/uploads/2016/11/timer2-300x143.png 300w, https://greyli.com/wp-content/uploads/2016/11/timer2-1024x487.png 1024w, https://greyli.com/wp-content/uploads/2016/11/timer2-624x297.png 624w" sizes="(max-width: 1600px) 100vw, 1600px" /></a><p class="wp-caption-text">计时界面</p></div>
<p>&#8211; &#8211; &#8211; &#8211; &#8211;</p>
<p>我们经常在一些网站上看到倒计时，比如购物网站上的秒杀倒计时，或是考试网站上的距离高考还剩多少天……</p>
<p>我们今天就用Flask和JavaScript（jQuery）来实现一个在线计时器，具体的User Strory：</p>
<ul>
<li>可以在首页点击不同的时间按钮进入计时</li>
<li>计时结束后会有弹窗和铃声提示</li>
<li>可以在输入框里输入参数进入相应的计时，比如“34、23s、20m、2h”</li>
<li>可以通过在url里传入时间来开始计时，比如：
<ul>
<li>“<a href="http://timertimer.herokuapp.com/10s" target="_blank">timertimer.herokuapp.com/10s</a>”会进入一个10秒的计时。</li>
<li>“<a href="http://timertimer.herokuapp.com/25m" target="_blank">timertimer.herokuapp.com/25m</a>”会进入一个25分钟的计时。</li>
<li>“<a href="http://timertimer.herokuapp.com/2h" target="_blank">timertimer.herokuapp.com/2h</a>”会进入一个2小时的计时。</li>
</ul>
</li>
</ul>
<h2><b>项目结构</b></h2>
<pre lang="text">|-Timer-Flask 项目名称
    |-app.py
    |-templates/  模板文件夹
        |-index.html  
    |-static/
        |-beep.mp3  计时结束铃声
        |-favicon.ico  站点图标
        |-style.css
        |-js/
            |-progressbar.js
            |-jquery.min.js
            
    |-venv/  虚拟环境</pre>
<h2><b>实现代码</b></h2>
<p>主程序：app.py</p>
<pre lang="python">import re
from flask import Flask, render_template, url_for, redirect, request, flash

app = Flask(__name__)
app.config['SECRET_KEY'] = 'a very secret string'


@app.route('/')
def index():
    return redirect(url_for('timer', num=11*60+11))


@app.route('/&lt;int:num&gt;s')
@app.route('/&lt;int:num&gt;')
def timer(num):
    return render_template('index.html', num=num)


@app.route('/custom', methods=['GET', 'POST'])
def custom():
    time = request.form.get('time', 180)
    # 使用正则表达式来验证输入的字符
    m = re.match('\d+[smh]?$', time)
    if m is None:
        flash(u'请输入一个有效的时间，例如34、20s、15m、2h')
        return redirect(url_for('index'))
    if time[-1] not in 'smh':
        return redirect(url_for('timer', num=int(time)))
    else:
        type = {'s': 'timer', 'm': 'minutes', 'h': 'hours'}
        return redirect(url_for(type[time[-1]], num=int(time[:-1])))


@app.route('/&lt;int:num&gt;m')
def minutes(num):
    return redirect(url_for('timer', num=num*60))


@app.route('/&lt;int:num&gt;h')
def hours(num):
    return redirect(url_for('timer', num=num*3600))


@app.errorhandler(404)
def page_not_fouond(e):
    flash(u'访问地址出错了，鼠标放在问号上了解更多: )')
    return redirect(url_for('timer', num=244))
</pre>
<p>计时的功能主要用JavaScript（jQuery）实现，在index.html，传递变量给JavaScript：</p>
<pre lang="html">{% block scripts %}
&lt;script&gt;
    var Minutes = {{ num }};
&lt;/script&gt;
{% endblock %}
</pre>
<p>另外，在这个APP里，因为表单很小，所以没有使用Flask-WTF。表单部分：</p>
<pre lang="html">&lt;form method="POST" action="{{ url_for('custom') }}" style="display:inline"&gt;
    &lt;input name="time" class="time-input" placeholder="example: 12/30s/20m/2h"&gt;
    &lt;input type="submit" class="startButton" value="START"&gt;
&lt;/form&gt;</pre>
<p>然后在视图函数里，我使用request来获取数据，使用正则表达式验证数据：</p>
<pre lang="python">@app.route('/custom', methods=['GET', 'POST'])
def custom():
    # 设置180为默认值，避免提交空白表单产生400错误
    time = request.form.get('time', 180) 
    # 使用正则表达式验证数据
    m = re.match('\d+[smh]?$', time)
    if m is None:
        flash(u'请输入一个有效的时间，例如34、20s、15m、2h')
        return redirect(url_for('index'))
    if time[-1] not in 'smh':
        return redirect(url_for('timer', num=int(time)))
    else:
        type = {'s': 'timer', 'm': 'minutes', 'h': 'hours'}
        return redirect(url_for(type[time[-1]], num=int(time[:-1])))</pre>
<p>下一次会谈一下表单的几种不同的验证方式和一些处理技巧。</p>
<p><i><b>完整的实现见源码（链接在底部），欢迎fork和patch（或是star：）。</b></i></p>
<h2><b>相关知识点</b></h2>
<ul>
<li>URL变量</li>
</ul>
<p>大部分现代的网站（app）都会有一个美观简洁的URL，比如<a class="" href="http://www.example.com/user/kitty" data-editable="true" data-title="example.com 的页面">http://www.example.com/user/kitty</a>。在Flask里，我们通过在URL里设置变量来实现这个功能，比如说上面的URL，对应的路由就是：</p>
<pre lang="python">app.route('/user/&lt;username&gt;')</pre>
<p>这个<b>&lt;variable_name&gt;</b>可以作为参数传递到视图函数里，我们还可以使用Flask提供的转换器，以<code class=""><span class=""><b>&lt;converter:variable_name&gt;</b>的形式来转换变量：</span></code></p>
<pre><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/user/&lt;username&gt;'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">show_user_profile</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
    <span class="c1"># show the user profile for that user</span>
    <span class="k">return</span> <span class="s1">'User </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="n">username</span>

<span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/post/&lt;int:post_id&gt;'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">show_post</span><span class="p">(</span><span class="n">post_id</span><span class="p">):</span>
    <span class="c1"># show the post with the given id, the id is an integer</span>
    <span class="k">return</span> <span class="s1">'Post </span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="n">post_id</span></pre>
<p>下面是Flask支持的转换器：</p>
<table border="1">
<colgroup>
<col />
<col /></colgroup>
<tbody valign="top">
<tr class="">
<td><cite>string</cite></td>
<td>accepts any text without a slash (the default)</td>
</tr>
<tr class="">
<td><cite>int</cite></td>
<td>accepts integers</td>
</tr>
<tr class="">
<td><cite>float</cite></td>
<td>like <cite>int</cite> but for floating point values</td>
</tr>
<tr class="">
<td><cite>path</cite></td>
<td>like the default but also accepts slashes</td>
</tr>
<tr class="">
<td><cite>any</cite></td>
<td>matches one of the items provided</td>
</tr>
<tr class="">
<td><cite>uuid</cite></td>
<td>accepts UUID strings</td>
</tr>
</tbody>
</table>
<p>使用这个特性，计时器实现了在地址后填入参数就可以进入相应的计时。</p>
<h2><strong>相关链接</strong></h2>
<p>DEMO：<a class="" href="http://timertimer.herokuapp.com/" data-editable="true" data-title="计时器 - Hello, Flask!">http://timertimer.herokuapp.com/</a></p>
<p>源码：<a class="" href="https://github.com/helloflask/timer-flask" data-editable="true" data-title="GitHub - helloflask/timer-flask: A simple countdown timer made with Flask and JavaScript.">https://github.com/helloflask/timer-flask</a></p>
<p>&#8211; &#8211; &#8211; &#8211; &#8211;</p>
<p>更多关于Flask的优质内容，欢迎关注<a class="" href="https://zhuanlan.zhihu.com/flask" data-editable="true" data-title="Hello, Flask! - 知乎专栏">Hello, Flask! &#8211; 知乎专栏</a>。</p>
]]></content:encoded>
			<wfw:commentRss>https://greyli.com/flask-practice-timer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
