最近在用 Visual Studio 2022 社区版 编一个桌面应用程序，需要用到 Microsoft.Web.WebView2 控件。新建项目，选C#，Windows平台，桌面应用程序，选择“Windows 窗体应用（.NET Framework）”，填写项目名称，选择存储位置之后，点击“创建”按钮，等几秒钟，一个简单的Form窗体应用程序就准备完毕。在【解决方案资源管理器】中，项目名称上点击右键，弹出菜单中选择“管理 NuGET 程序包”，在打开的界面中选择【浏览】标签页，在搜索框中输入“webview2”，排在最前面的就是 Microsoft.Web.WebView2，作者是 Microsoft，选择“Microsoft.Web.WebView2”后，右侧显示版本：最新稳定版 1.9.3485.44，点击【安装】按钮，弹出一个“预览更改”窗体，点击【应用】按钮即可，程序【输出】界面会显示如下信息：正在尝试收集与目标为“.NETFramework,Version=v4.8.1”的项目“BrowserRecorder”有关的包“Microsoft.Web.WebView2.1.0.3485.44”的依赖项信息收集依赖项信息花费时间 716 毫秒正在尝试解析程序包“Microsoft.Web.WebView2.1.0.3485.44”的依赖项，DependencyBehavior 为“Lowest”解析依赖项信息花费时间 0 毫秒正在解析操作以安装程序包“Microsoft.Web.WebView2.1.0.3485.44”已解析操作以安装程序包“Microsoft.Web.WebView2.1.0.3485.44”正在将程序包“Microsoft.Web.WebView2.1.0.3485.44”添加到文件夹“D:\C#Projects\BrowserRecorder\packages”已将程序包“Microsoft.Web.WebView2.1.0.3485.44”添加到文件夹“D:\C#Projects\BrowserRecorder\packages”已将程序包“Microsoft.Web.WebView2.1.0.3485.44”添加到“packages.config”已将“Microsoft.Web.WebView2 1.0.3485.44”成功安装到 BrowserRecorder执行 nuget 操作花费时间 2.25 秒已用时间: 00:00:03.0155245========== 已完成 ==========这时打开“工具箱”，里面应该出现“WebView2 Windows Form Control”，里面有两项，一个是“指针”，另一个是“WebView2”，而这个就是可以添加到窗体中的控件。但是不知道怎么回事，工具箱里面就是没有这些内容，重新安装了 Visual Studio 也没有用，正好这两天台风“五停”，闲着也是闲着，干脆把系统重新安装一下。现在的系统里装了很多乱七八糟的软件，导致 200GB 的 C 盘只有 30GB 的空间了。用 Ventoy 软件 格式化好久未用的 64GB 的 U盘，然后把 Win10_22H2_Chinese_Simplified_x64v1.iso 考到 U盘里面。重新启动电脑，开始全新安装 Windows，安装完成，设置好网络，系统会自动查找驱动，很快一个全新的系统就准备就绪。安装 Visual Studio 2022，新建项目，通过 NuGet 添加 Microsoft.Web.WebView2，一切都很顺利，但是打开工具箱一看，还是没有 “WebView2 Windows Form Control”选项，奇了怪了，心里忽然有个念头：难道是需要刷新一下才能显示出来吗？在“工具箱”界面的空白处点击右键，弹出菜单里面没有“刷新”选项，但是有一个“重置工具箱”选项，鬼使神差之下点了它，呵呵，试了好多遍都不显示的“WebView2 Windows Form Control”选项出现了！
网上大神真多，高手在民间。居然遇到一个制作输入法的网友，首先我觉得弄个输入法是个繁杂的事情，想想一大堆编码就会头疼，就像五笔一样，需要拆解汉字形成字根，它不像拼音输入法，会拼音就可以；其次，这个输入法有一个古怪的名字“尹卂倉頡網頁輸入法”，“尹卂”是作者的名字，而仓颉造字的故事大家基本都知道，所以看到名字就觉得这是个颇有神秘感的输入法，虽然它已经出现了很多年。当然，这确实也不是一般的输入法，我打赌除了网页键盘上的字以外，你想凭感觉输入其他的字是基本不可能的。还好，作者准备了搜索功能，不会拼的字可以先查一下它的编码，至于具体的编码及使用方法可以参看下面的文章：《尹卂倉頡規則正述》《尹卂倉頡簡易教程》尹卂倉頡輸入法網頁：https://ejsoon.win/wp-content/uploads/2022/11/ejcjonline.html整个输入法全部放在了一个 html 文件里面，网页大小只有 204k，简便易携带。“尹倉遍地開花”活动https://ejsoon.win/ejcjonline/ 操作说明用鼠标点击绿色汉字，在粉红色区域生成你想要的文字后，点击该字即可；点击“Enter”键会输入英文字母，而不是生成的汉字。代码说明我是使用了 iframe 内嵌的功能，这个功能在 Markdown 里面可以直接使用。代码如下，效果如上：
之前激光雕刻出现了一些小问题：https://www.sirit.com.cn/archives/489/再三确认 LightBurn 里面绘制的图形没有问题，突发奇想，是不是生成的 GCode 文件出问题了呢？GCode 文件是文本格式，用记事本软件就可以打开。打开之前生成的 GCode 文件，太长，有点乱，干脆重新画个简单的图案来看一下 GCode 文件。软件问题在 LightBurn 里面画了 5 条线，按照 x，y 坐标定位，拼成了两个正方形，分别设置了每条线的颜色、顺序以及功率速度，然后保持成 GCode 文件。GCode 文件的内容如下：; GRBL device profile, absolute coords ; Bounds: X100 Y180 to X200 Y230 G00 G17 G40 G21 G54 G90 M4 ; Cut @ 6000 mm/min, 20% power M8 G0 X100Y230 ; Layer C01 G1 X200S200F6000 ; Cut @ 5000 mm/min, 30% power M8 G0 X200Y180 ; Layer C02 G1 X100S300F5000 ; Cut @ 4000 mm/min, 40% power M8 G0 X100Y180 ; Layer C03 G1 Y204.9S400F4000 G0 Y205.1 G1 Y230 ; Cut @ 3000 mm/min, 50% power M8 G0 X150Y230 ; Layer C04 G1 Y205.1S500F3000 G0 Y204.9 G1 Y180 ; Cut @ 175 mm/min, 100% power M8 G0 X200Y180 ; Layer C05 G1 Y204.9S1000F175 G0 Y205.1 G1 Y230 M9 G1 S0 M5 G90 ; return to user-defined finish pos G0 X0 Y0 M2硬件问题研究了半天，没发现什么问题，心里想着还是先确定硬件是否出问题吧。先把皮带松了一点，然后打印了厂家自带的老鹰图案，打的一踏糊涂，错位非常严重，很多线条都没有封闭。又用 LightBurn 作图：打印效果如下，橘黄色圆圈内都是错位。抽空问了 jd 上的厂家客服，回答是可能螺丝松了，或者皮带松紧度不合适，至于皮带的松紧度完全靠个人感觉来衡量，不能太紧，也不能太松。唉，这技术客服也太好干了吧。难道厂家就不能给出一个校正图案，打印一遍就知道哪里的问题吗？只能自己摸索了！鉴于我之前动了 Y 轴的皮带，我想最大的问题就在这里，可能皮带太松导致线条变换方向时产生错位，于是，我把 Y 轴的两根皮带用手拉到最紧，然后把固定压片上紧。再次打印老鹰图案，这一次，完美！总结其实第一次打印老鹰图案就有点错位，只是当时我以为打印的时候不小心碰到桌子，导致木板有移动而造成的错位。昨天打印时间拼图底板的时候，日期也有错位，我以为是字体丢失的原因，今天终于给解决了。我之前心里一直对软件出错存疑，其实软件出错的可能性最小。当发生类似事件时，还是先排查硬件是否出故障，最后再考虑软件的问题吧。
换了这个Moricolor主题之后，收到许多垃圾评论，都是全英文的，发的都是链接图片之类的东西，因为我开启了评论审核，所以一进后台就看到一大堆的垃圾评论。手动删除了几次，心想这也不是办法，总得解决一下，网上的解决办法，大致有几类：使用 Typecho 插件：有点麻烦，需要额外安装插件；增加汉字验证：提交时判断输入内容是否有汉字，一刀切，有点武断；是否中文浏览器：要是全英文的办公环境，还得来回切换浏览器的设置；增加验证码：在评论区增加验证码。选择了“增加验证码”，常见的就是增加一道口算题，如下图所示：代码参考文章：Typecho纯代码算术验证码加强版我根据他的代码，结合网站主题进行了部分修改（顺便说一句，Typecho后台可以直接改主题代码，真方便）：修改 functions.php 文件第一步：找到 function themeInit() 代码处：在该函数的最后增加一行代码：$comment = spam_protection_pre($comment, $post, $result); 第二步：来到 functions.php 文件末尾：增加下面两个函数的代码：function spam_protection_math() { $num1 = rand(1, 15); $num2 = rand(1, 15); echo " \n"; echo " "; echo " "; } function spam_protection_pre($comment, $post, $result) { if ($_REQUEST['text'] != null) { If($_POST['num1'] == null || $_POST['num2'] == null) { throw new Typecho_Widget_Exception(_t('验证码异常.', '评论失败')); } else { $sum = $_POST['sum']; switch ($sum) { case $_POST['num1'] + $_POST['num2'] : break; case null: throw new Typecho_Widget_Exception(_t('请输入计算结果...', '评论失败')); break; default: throw new Typecho_Widget_Exception(_t('计算错误，请重新输入计算结果.', '评论失败')); } } } return $comment; }上图中的绿色粗线是我修改过后的代码，一个是用 placeholder 把提示文字放到 input 框里面；另一个是修改 class，让它与我的网站主题保持一致。修改 comments.php 文件在 comments.php 文件中，找到 input 输入框的位置：在最后一个 input 后面，输入下面的代码，如下图所示：<?php spam_protection_math();?>最终效果：最新修改1004经过使用才发现，数字验证框没放对位置，登录之后导致看不到验证框，修改的代码如下： <?php spam_protection_math();?> <?php $this->remember('text', false); ?> 也就是把数字验证框的代码放到评论区的上方即可。
网址：https://github.com/mrdoob/three.js中文文档：https://threejs.org/docs/#manual/zh/ { "imports": { "three": "../build/three.module.js", "three/addons/": "./jsm/" } } import * as THREE from 'three'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js'; let camera, scene, renderer; let clock; const mixers = []; init(); animate(); function init() { camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.set( 2, 3, - 6 ); camera.lookAt( 0, 1, 0 ); clock = new THREE.Clock(); scene = new THREE.Scene(); scene.background = new THREE.Color( 0xa0a0a0 ); scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 ); const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 ); hemiLight.position.set( 0, 20, 0 ); scene.add( hemiLight ); const dirLight = new THREE.DirectionalLight( 0xffffff ); dirLight.position.set( - 3, 10, - 10 ); dirLight.castShadow = true; dirLight.shadow.camera.top = 4; dirLight.shadow.camera.bottom = - 4; dirLight.shadow.camera.left = - 4; dirLight.shadow.camera.right = 4; dirLight.shadow.camera.near = 0.1; dirLight.shadow.camera.far = 40; scene.add( dirLight ); // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) ); // ground const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 200, 200 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) ); mesh.rotation.x = - Math.PI / 2; mesh.receiveShadow = true; scene.add( mesh ); const loader = new GLTFLoader(); loader.load( 'models/gltf/Soldier.glb', function ( gltf ) { gltf.scene.traverse( function ( object ) { if ( object.isMesh ) object.castShadow = true; } ); const model1 = SkeletonUtils.clone( gltf.scene ); const model2 = SkeletonUtils.clone( gltf.scene ); const model3 = SkeletonUtils.clone( gltf.scene ); const mixer1 = new THREE.AnimationMixer( model1 ); const mixer2 = new THREE.AnimationMixer( model2 ); const mixer3 = new THREE.AnimationMixer( model3 ); mixer1.clipAction( gltf.animations[ 0 ] ).play(); // idle mixer2.clipAction( gltf.animations[ 1 ] ).play(); // run mixer3.clipAction( gltf.animations[ 3 ] ).play(); // walk model1.position.x = - 2; model2.position.x = 0; model3.position.x = 2; scene.add( model1, model2, model3 ); mixers.push( mixer1, mixer2, mixer3 ); animate(); } ); renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.outputEncoding = THREE.sRGBEncoding; renderer.shadowMap.enabled = true; document.body.appendChild( renderer.domElement ); window.addEventListener( 'resize', onWindowResize ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); const delta = clock.getDelta(); for ( const mixer of mixers ) mixer.update( delta ); renderer.render( scene, camera ); }
解决了 海龟画图窗口在最上 的问题之后，同学的儿子又发来新问题。他使用海龟模块绘制冰墩墩图案，准备多次绘制，结果每次第二次绘制都会报错，导致程序终止。调试环境：PyCharm 社区版 2021.3.3示例代码：flag = "Y" while(flag =="Y"): import turtle turtle.Screen().getcanvas().winfo_toplevel().attributes("-topmost", True) colors = ['red', 'purple', 'blue', 'green', 'yellow', 'orange'] t = turtle.Pen() t.shape("turtle") turtle.bgcolor('black') for x in range(72): t.pencolor(colors[x % 6]) t.width(x / 20 + 1) t.forward(x) t.left(30) turtle.done() flag = input("再来一次? （Y/N）").upper()第一遍执行完，关闭绘图窗口，当选择“再来一次”的时候就会出错：原因大概是 turtle.done() 只是暂停了程序的执行，使绘图窗口停留不关闭，当关闭绘图窗口后，turtle还在后台继续运行，因此下一次再执行 turtle 的代码就会出错。具体的解决办法见下面增加的两行代码：flag = "Y" while(flag =="Y"): import turtle turtle.Screen().getcanvas().winfo_toplevel().attributes("-topmost", True) colors = ['red', 'purple', 'blue', 'green', 'yellow', 'orange'] t = turtle.Pen() t.shape("turtle") turtle.bgcolor('black') for x in range(72): t.pencolor(colors[x % 6]) t.width(x / 20 + 1) t.forward(x) t.left(30) turtle.done() turtle.Turtle._screen = None # force recreation of singleton Screen object turtle.TurtleScreen._RUNNING = True # only set upon TurtleScreen() definition flag = input("再来一次? （Y/N）").upper()亲测有效，见下图的多次运行效果：
一大早，本科同学来电话说他儿子有 Python 编程问题解决不了，说是画图窗口老是在后面被遮挡，然后把代码用微信发来让我看看。先把代码另存到本地，文件名为 d.py。打开 Jupyter LabJupyter Lab 是我最常用的 Python 编程环境，在浏览器里面就可以编程执行，非常方便。把代码拷贝进去，执行，turtle 画图窗口出现并保持在最前，没问题。Anaconda PowerShell Prompt打开 Anaconda PowerShell Prompt 命令行界面，输入 python d.py，回车，画图窗口顺利弹出在最前，没问题。stem86.comStem86 学习平台带有 Python 的编程环境，里面的 turtle 画图是直接显示的，更没问题了。Visual Studio Code用 VS Code 打开 d.py，启动调试，发现 turtle 窗口没有在最前，需要手动切换，有问题了。PyCharm使用 PyCharm 免费的社区版打开 d.py，画图窗口也没有在最前。解决方案之前一直用 Jupyter Lab，还没有注意到这个问题，貌似在 VS Code 和 PyCharm 软件调试时会把弹出窗口放到后面，要解决的话就得从窗口的控制入手。网上搜索了一番，找到如下代码：import turtle topwindow = turtle.Screen().getcanvas().winfo_toplevel() topwindow.call('wm', 'attributes', '.', '-topmost', '1') topwindow.call('wm', 'attributes', '.', '-topmost', '0') turtle.Screen().title("我是窗口标题") # Set the window titleOK，问题解决了。上面的代码可以简化如下：# 窗口置顶 turtle.Screen().getcanvas().winfo_toplevel().attributes("-topmost", True) # 窗口最大尺寸 turtle.Screen().getcanvas().winfo_toplevel().wm_maxsize(500,400) # 窗口标题 turtle.Screen().getcanvas().winfo_toplevel().title("Helllooooooooo") # 标题的最大化按钮不可用 turtle.Screen().getcanvas().winfo_toplevel().wm_resizable(False,False) # 窗口透明度 turtle.Screen().getcanvas().winfo_toplevel().attributes("-alpha", 0.5) # 工具栏标题模式 turtle.Screen().getcanvas().winfo_toplevel().attributes("-toolwindow", False) # 窗口全屏模式 turtle.Screen().getcanvas().winfo_toplevel().attributes("-fullscreen",True) 更多使用方式参见：http://tkdocs.com/tutorial/windows.html
JavaScript是世界上最流行的脚本语言，因为你在电脑、手机、平板上浏览的所有的网页，以及无数基于HTML5的手机App，交互逻辑都是由JavaScript驱动的。简单地说，JavaScript是一种运行在浏览器中的解释型的编程语言。那么问题来了，为什么我们要学JavaScript？尤其是当你已经掌握了某些其他编程语言如Java、C++的情况下。简单粗暴的回答就是：因为你没有选择。在Web世界里，只有JavaScript能跨平台、跨浏览器驱动网页，与用户交互。Flash背后的ActionScript曾经流行过一阵子，不过随着移动应用的兴起，没有人用Flash开发手机App，所以它目前已经边缘化了。相反，随着HTML5在PC和移动端越来越流行，JavaScript变得更加重要了。并且，新兴的Node.js把JavaScript引入到了服务器端，JavaScript已经变成了全能型选手。JavaScript一度被认为是一种玩具编程语言，它有很多缺陷，所以不被大多数后端开发人员所重视。很多人认为，写JavaScript代码很简单，并且JavaScript只是为了在网页上添加一点交互和动画效果。但这是完全错误的理解。JavaScript确实很容易上手，但其精髓却不为大多数开发人员所熟知。编写高质量的JavaScript代码更是难上加难。一个合格的开发人员应该精通JavaScript和其他编程语言。如果你已经掌握了其他编程语言，或者你还什么都不会，请立刻开始学习JavaScript，不要被Web时代所淘汰。学习网址：https://www.liaoxuefeng.com/wiki/1022910821149312--------------------学习笔记--------------------JavaScript的语法和Java语言类似，每个语句以 ; 结束，语句块用{...}。JavaScript不区分整数和浮点数。十六进制用0x前缀和0-9，a-f表示。字符串是以单引号'或双引号"括起来的任意文本。由于JavaScript这个设计缺陷，不要使用 == 比较，始终坚持使用 === 比较。NaN === NaN; // falseisNaN(NaN); // true1 / 3 === (1 - 2 / 3); // falseMath.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // trueJavaScript的设计者希望用null表示一个空的值，而undefined表示值未定义。JavaScript的对象是一组由键-值组成的无序集合。变量在JavaScript中就是用一个变量名表示，变量名是大小写英文、数字、$和_的组合，且不能用数字开头。要显示变量的内容，可以用console.log(x)，打开Chrome的控制台就可以看到结果。不用var申明的变量会被视为全局变量。启用strict模式的方法是在JavaScript代码的第一行写上：'use strict';由于多行字符串用 \n 写起来比较费事，所以最新的ES6标准新增了一种多行字符串的表示方法，用反引号 ` 表示。如果要往Array的头部添加若干元素，使用unshift()方法，shift()方法则把Array的第一个元素删掉。splice()方法是修改Array的“万能方法”，它可以从指定的索引开始删除若干元素，然后再从该位置添加若干元素。在编写JavaScript代码的时候，属性名尽量使用标准的变量名，这样就可以直接通过object.prop的形式访问一个属性了。要判断一个属性是否是xiaoming自身拥有的，而不是继承得到的，可以用 hasOwnProperty() 方法。我们建议永远都要写上 {}。JavaScript把 null、undefined、0、NaN 和空字符串''视为false，其他值一概视为true。for 循环的 3 个条件都是可以省略的，如果没有退出循环的判断条件，就必须使用 break 语句退出循环，否则就是死循环。for 循环的一个变体是 for ... in 循环，它可以把一个对象的所有属性依次循环出来。用 do { ... } while()循环要小心，循环体会至少执行1次，而 for 和 while 循环则可能一次都不执行。Map 是一组键值对的结构，具有极快的查找速度。初始化 Map 需要一个二维数组，或者直接初始化一个空 Map。要创建一个 Set，需要提供一个 Array 作为输入，或者直接创建一个空 Set。具有 iterable 类型的集合可以通过新的for ... of循环来遍历。更好的方式是直接使用 iterable 内置的 forEach 方法，它接收一个函数，每次迭代就自动回调该函数。Array 的回调函数参数依次为element, index 和 array本身；Set 与 Array 类似，但 Set 没有索引，因此回调函数的前两个参数都是元素本身；Map 的回调函数参数依次为 value 、key 和 map 本身。JavaScript 关键字 arguments，它只在函数内部起作用，并且永远指向当前函数的调用者传入的所有参数。rest 参数只能写在最后，前面用 ... 标识。在JavaScript 中，用 var 申明的变量实际上是有作用域的。由于 JavaScript 的函数可以嵌套，此时，内部函数可以访问外部函数定义的变量，反过来则不行。JavaScript 的函数在查找变量时从自身函数定义开始，从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量，则内部函数的变量将“屏蔽”外部函数的变量。JavaScript 的函数定义有个特点，它会先扫描整个函数体的语句，把所有申明的变量“提升”到函数顶部。实际上，JavaScript 默认有一个全局对象 window，全局作用域的变量实际上被绑定到 window 的一个属性。JavaScript实际上只有一个全局作用域。全局变量会绑定到 window 上，不同的 JavaScript 文件如果使用了相同的全局变量，或者定义了相同名字的顶层函数，都会造成命名冲突，并且很难被发现。减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。把自己的代码全部放入唯一的名字空间中，会大大减少全局变量冲突的可能。为了解决块级作用域，ES6 引入了新的关键字let，用let替代var可以申明一个块级作用域的变量。从 ES6 开始，JavaScript 引入了解构赋值，可以同时对一组变量进行赋值。如果需要从一个对象中取出若干属性，也可以使用解构赋值，便于快速获取对象的指定属性。在一个方法内部，this 是一个特殊变量，它始终指向当前对象。用 var that = this;，你就可以放心地在方法内部定义其他函数，而不是把所有语句都堆到一个方法中。要指定函数的 this 指向哪个对象，可以用函数本身的apply方法，它接收两个参数，第一个参数就是需要绑定的 this 变量，第二个参数是 Array ，表示函数本身的参数。JavaScript的函数其实都指向某个变量。既然变量可以指向函数，函数的参数能接收变量，那么一个函数就可以接收另一个函数作为参数，这种函数就称之为高阶函数。
梦想小镇Township 我是如何一晚上赚 50 绿钞的
断断续续的在玩“梦想小镇”，大家都知道，绿钞在游戏里面绝对是硬通货，对于人民币玩家，完全可以通过购买绿钞实现快速升级。好吧，我是不会为游戏花钱的，除了我的最爱 Quake Live，花了RMB36元。steampowered 网站居然被举报为诈骗网站。嗯，确实骗了我不少钱，害得我天天玩游戏。最近游戏里面举行了一个农业大赛，也就是种庄稼就可以奖励积分，活动结束积分第一名可以奖励 50 张绿钞，哇，这可是千载难逢的好事，嗯，目前看来是。梦想小镇 (Township) 是一款比较传统的农场类模拟经营游戏，将城市建设和农场管理进行了结合。《梦想小镇》将城市建设和农场管理进行了独树一帜的结合！收获农作物、在工厂进行加工，再将货物出售以发展您的小镇。与远处的岛屿进行交易。开设餐厅、电影院和其他社区建筑，让您的小镇别具风味充满生机。探索矿井获取能源并找出古董。运营您自己的动物园并收集世界各地的动物。准备好筑造您的梦想了吗？我们开始吧！研究了一下积分情况，一个小麦1分，一个玉米2分，一个胡萝卜3分，基本上种植时间越长积分越高。不过仔细一算会发现时间和积分是不成比例的。比如，种一茬小麦需要2分钟，一茬玉米需要5分钟，也就是同样的时间，种小麦产生的积分更高，换算到其他作物也是一样，只有种小麦最划算，积分上升的也最快。玩了一个多小时，发现不对劲，这样升级是很快，但是 2 分钟一茬，也就意味着我要一直这么盯着才行。这怎么可以？我其他的事情都做不了了。还有什么事情可以让程序员为难呢？！（BGM...）是时候展现我真正的实力啦，哈哈哈说明：以下操作环境为 macOS Catalina，版本 10.15.4。网上搜索了 Mac 上的鼠标自动化操作，发现 PyAutoGUI 完全满足“鼠标点击和拖动”的要求，就它了。PyAutoGUI 官网：https://pyautogui.readthedocs.io/en/latest/Windows 下面有按键精灵可以用，很早很早以前用过。使用 PIP 安装好 PyAutoGUI ，就可以祭出我的 Python 大法，打开终端，输入：jupyter lab，开启python之旅！BUT 编程切记不要一上来就写代码，要先思考流程，模块设计，划分功能。不过，我一瞬间就明白了一切，开始直接动手写代码，嗯，就是这样的。好吧，循环流程大致有这么几步：收麦子 -> 种麦子-> 卖麦子收麦子：定位麦田位置，选中镰刀，在麦田上方挥舞镰刀收割种麦子：定位麦田位子，选中小麦种子，在麦田上方挥洒种子卖麦子：打开仓库，定位到小麦，修改销售数量，卖出小麦。【移动与点击代码】import pyautogui as pa from datetime import datetime import time #多次点击 def timesclick(x): for i in range(x): pa.click() time.sleep(0.1) #田地范围 def YiDong2(): for i in range(5): pa.moveTo(790-70*i, 255+45*i, duration=0.5) pa.moveTo(980+75*i, 255+45*i, duration=0.5) for i in range(5): pa.moveTo(380+40*i, 450+45*i, duration=0.5) pa.moveTo(1210-75*i, 450+45*i, duration=0.5) 【代码说明】不知道怎么回事，click()本身是支持多次点击的，修改clicks、interval就可以，但是 interval 不起作用，而如果点击快了，游戏里面是反应不过来的，因此只能自己写一个多次点击函数。游戏里面的坐标不是正方形，而是菱形，一开始是获取田地左上和右下坐标，然后横扫整个长方形，这样有点费时。重写代码，扫描范围改成菱形了。【收、种、卖代码】#收 麦子 def ShouMaizi(): pa.moveTo(820, 460, duration=0.25) pa.click() #鼠标在麦田中心点击一次 pa.moveTo(730, 820, duration=0.25) #镰刀位置 pa.mouseDown(730, 820)#选中镰刀 pa.moveTo(820, 460, duration=0.5) YiDong2() pa.mouseUp() #种 麦子 def Zhongmaizi(): pa.moveTo(820, 460, duration=0.25) #选块地 pa.click() pa.moveTo(400, 820, duration=0.25) #选小麦种子 pa.mouseDown(400, 820) pa.moveTo(820, 460, duration=0.25) YiDong2() pa.mouseUp() #卖 麦子 def Maimaizi(): pa.moveTo(810, 150, duration=0.25) #打开仓库 pa.click() pa.moveTo(490, 350, duration=0.5) #点麦子 pa.click() pa.moveTo(640, 570, duration=0.25) #改数量 timesclick(25) pa.moveTo(500, 695, duration=0.25) #卖麦子 pa.click() pa.moveTo(1150, 130, duration=0.5) #关闭仓库 pa.click() 【代码说明】mouseDown()表示一直按着左键，然后通过moveTo()来回移动，实现收、种效果；mouseUp()松开按键。【循环代码】dt=datetime.now() #创建一个datetime类对象 print('开始循环:',dt.year,'年',dt.month,'月',dt.day,'日',dt.hour,':',dt.minute,':',dt.second,'\n') time.sleep(20) for i in range(300): t0 = time.perf_counter() Zhongmaizi() print('种麦子耗时 Sec:',time.perf_counter()-t0) time.sleep(int(random.random()*8)) Maimaizi() time.sleep(105) #等待麦子成熟 print('卖麦子耗时 Sec:',time.perf_counter()-t0) ShouMaizi() print('收麦子耗时 Sec:',time.perf_counter()-t0-105) time.sleep(int(random.random()*3)) print('循环次数 No:',i+1,' 耗时 Sec:',time.perf_counter()-t0,'\n') 【代码说明】做了每一项的计时，这么便于后期的改进，缩减时间；每一项操作后面有个随机的等待时间，不知道游戏有没有外挂检测，先随机一下。每次都把麦子卖光，不然仓库会满，影响后续操作。【注意事项】因为是按照坐标定位，进行操作，因此自动化开始后，不能移动地图。【后记】运行代码，利用等待时间切换到游戏界面，OK，观察一两个循环看看效果，还不错，终于解放了。开了一晚上，积分上升嗖嗖滴，第二天毫无悬念的得了第一名，一晚上就收获 50 张绿钞 ，嗯，只有一晚上。种麦子不花钱，没想到卖了一晚上麦子，金币也收获颇丰，哈哈。
原文参见：https://black1ce.com/website/sum-verify.html引言最近被垃圾评论弄得很烦，开始每天才十多条，现在每天都有上百条，愈演愈烈，赶紧想办法。不喜欢插件，喜欢代码集成的方式。发现上面链接给的算术验证不错，原文写的不够详细，下面把使用方法写一下。说明针对主题： initial 2.4.3（ https://www.offodd.com/17.html ）注意：修改之前请务必备份网站！！！修改之前请务必备份网站！！！修改之前请务必备份网站！！！使用管理员身份登录之后，在后台编辑外观页面打开 functions.php 文件，在文件最后面添加下面的代码：//算术验证评论 function spam_protection_math(){ $num1=rand(1,49); $num2=rand(1,49); echo " $num1+$num2 = \n"; echo " \n"; echo " \n"; echo " "; } function spam_protection_pre($comment, $post, $result){ $sum=$_POST['sum']; switch($sum){ case $_POST['num1']+$_POST['num2']: break; case null: throw new Typecho_Widget_Exception(_t('对不起: 请输入验证码。返回上一页','评论失败')); break; default: throw new Typecho_Widget_Exception(_t('对不起: 验证码错误，请返回重试。','评论失败')); } return $comment; } 然后，在 functions.php 文件前部找到如下代码：function themeInit($archive) { $options = Helper::options(); $options->commentsAntiSpam = false; if ($options->PjaxOption || FindContents('page-whisper.php', 'commentsNum', 'd')) { $options->commentsOrder = 'DESC'; $options->commentsPageDisplay = 'first'; } if ($archive->is('single')) { $archive->content = hrefOpen($archive->content); if ($options->AttUrlReplace) { $archive->content = UrlReplace($archive->content); } if ($archive->fields->catalog) { $archive->content = createCatalog($archive->content); } } } 在里面添加一句代码 $comment = spam_protection_pre($comment);，改好的代码如下：function themeInit($archive) { $options = Helper::options(); $options->commentsAntiSpam = false; if ($options->PjaxOption || FindContents('page-whisper.php', 'commentsNum', 'd')) { $options->commentsOrder = 'DESC'; $options->commentsPageDisplay = 'first'; } if ($archive->is('single')) { $archive->content = hrefOpen($archive->content); if ($options->AttUrlReplace) { $archive->content = UrlReplace($archive->content); } if ($archive->fields->catalog) { $archive->content = createCatalog($archive->content); } } $comment = spam_protection_pre($comment); } 保存好 functions.php 文件之后，再打开 comments.php 文件，在如下图的位置处添加下面一行代码：<?php spam_protection_math();?> 算术验证代码添加的位置会让登录用户无需验证，非登录用户就需要考验心算能力了 ^_^CSS 文件的修改在 functions.php 文件里已经有部分 CSS 代码（label style="line-height:30px;" 和input style="width:130px;float:right;" ）了，现在只需要打开 style.min.css 文件，找到 textarea{height:150px;resize:vertical;overflow:auto}，高度改成 190px，即 textarea{height:190px;resize:vertical;overflow:auto}，效果如下图：
李笑来的《 自学是门手艺 》： https://github.com/selfteaching/the-craft-of-selfteaching/Python 加鸡汤，比一本正经枯燥无味的书籍更有利于阅读，也能学到一些编程之外的知识。当然，各花入各眼，有人挺反感。Python 代码仓库： https://github.com/python/Python Demo ：https://github.com/python/cpython/tree/master/Tools/demo廖雪峰的官方网站 Python 教程看完李笑来的书，可以再把这个过一遍。
beer.py Well-known programming example: Bottles of beer.
beer.py Well-known programming example: Bottles of beer.Github:https://github.com/python/cpython/blob/master/Tools/demo/beer.py#!/usr/bin/env python3 """ A Python version of the classic "bottles of beer on the wall" programming example. By Guido van Rossum, demystified after a version by Fredrik Lundh. """ import sys n = 100 if sys.argv[1:]: n = int(sys.argv[1]) def bottle(n): if n == 0: return "no more bottles of beer" if n == 1: return "one bottle of beer" return str(n) + " bottles of beer" for i in range(n, 0, -1): print(bottle(i), "on the wall,") print(bottle(i) + ".") print("Take one down, pass it around,") print(bottle(i-1), "on the wall.")在 jupyter lab 中运行报错：--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-18-d31cdec36abd> in <module> 11 n = 100 12 if sys.argv[1:]: ---> 13 n = int(sys.argv[1]) 14 15 def bottle(n): ValueError: invalid literal for int() with base 10: '-f'【解决办法】查找 sys.argv[] 的用法：https://docs.python.org/zh-cn/3/library/sys.html?highlight=argv#sys.argvsys.argv The list of command line arguments passed to a Python script.argv[0] is the script name (it is operating system dependent whetherthis is a full pathname or not). If the command was executed using the-c command line option to the interpreter, argv[0] is set to the string '-c'. If no script name was passed to the Python interpreter,argv[0] is the empty string.To loop over the standard input, or the list of files given on thecommand line, see the fileinput module. sys.argv[] 是传递给 Pythen 脚本文件的命令行参数列表。把 beer.py 下载到本地，chmod 777 beer.py$./beer.py100 bottles of beer on the wall, 100 bottles of beer. Take one down, pass it around, 99 bottles of beer on the wall. 99 bottles of beer on the wall, 99 bottles of beer. Take one down, pass it around, 98 bottles of beer on the wall. ......中间省略若干行...... 2 bottles of beer. Take one down, pass it around, one bottle of beer on the wall. one bottle of beer on the wall, one bottle of beer. Take one down, pass it around, no more bottles of beer on the wall. 如果执行：$./beer.py 3结果显示为：3 bottles of beer on the wall, 3 bottles of beer. Take one down, pass it around, 2 bottles of beer on the wall. 2 bottles of beer on the wall, 2 bottles of beer. Take one down, pass it around, one bottle of beer on the wall. one bottle of beer on the wall, one bottle of beer. Take one down, pass it around, no more bottles of beer on the wall. 自此明白， sys.argv[1]的作用是获取 Python 脚本运行时给定的命令行参数。同样，如果运行：$./beer.py 3 4则sys.argv[1] = 3sys.argv[2] = 4至于 sys.argv[0]，默认获取的是 Python 脚本文件名。【收获】学会了 sys.argv[] 的用法；不是所有的命令都可以用 jupyter lab 来测试；况且 beer.py 文件开头也注明了 #!/usr/bin/env python3多查官方文档。Python 的文档写的很不错，而且最近也推出了中文的版本。
macOS Python 环境配置强烈建议使用 Anaconda（https://www.anaconda.com/），Windows、Linux、Mac都有相应版本，而且安装包管理起来比较方便；不过 Anaconda 更新起来比较慢，建议换成国内镜像源；安装完 Anaconda 后，如果终端窗口 conda 命令不能运行，可以先配置 .zshrc 文件；打开终端，输入： vi ~/.zshrc 之后回车，然后按字母 i 进入插入编辑模式，输入下列代码： export PATH=/opt/anaconda3/bin:$PATH ，其中红色字体要替换成你自己电脑上的conda所在目录，按 ESC 键，输入： :wq 三个字符，文件被保存后退出。终端再输入：source ~/.zshrc ，使其立即生效，这时就可以用： conda --version ，查看 conda 的版本信息。安装 OpenCV打开终端，输入： sudo pip3 install opencv-python ，按提示输入密码，之后就是漫长的安装过程。切换成国内镜像源会非常快：sudo pip3 install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple直接运行 py 文件.py 文件在Windows上是不能像exe文件直接执行的。但是，在Mac和Linux上是可以的，方法是在.py文件的第一行加上一个特殊的注释：#!/usr/bin/env python3 print('hello, world')然后，通过命令给 hello.py 以执行权限：$ chmod a+x hello.py，就可以直接运行 hello.py 了，执行：Air:~ sirit$ ./hello.pyprint() 输出字符串print()函数可接受多个字符串，用逗号“,”隔开，就可以连成一串输出，print()会依次打印每个字符串，遇到逗号“,”会输出一个空格。name = input('please enter your name: ')print('hello,', name)Python 基础Python的语法采用缩进方式，应该始终坚持使用4个空格的缩进。Python程序是大小写敏感的。以#开头的语句是注释。Python3 的六个标准数据类型中：不可变数据（3 个）：Number（数字）、String（字符串）、Tuple（元组）；可变数据（3 个）：List（列表）、Dictionary（字典）、Set（集合）。整数运算永远是精确的（除法难道也是精确的？是的！），而浮点数运算则可能会有四舍五入的误差。字符串是以单引号'或双引号"括起来的任意文本。转义字符\可以转义很多字符，比如\n表示换行，\t表示制表符，字符\本身也要转义，所以\\表示的字符就是\。Python用r''表示''内部的字符串默认不转义。在Python中，可以直接用True、False表示布尔值（请注意大小写）。空值是Python里一个特殊的值，用None表示。变量本身类型不固定的语言称之为动态语言，与之对应的是静态语言。静态语言在定义变量时必须指定变量类型，如果赋值的时候类型不匹配，就会报错。在Python中，通常用全部大写的变量名表示常量。Python的整数没有大小限制，而某些语言的整数根据其存储长度是有大小限制的，例如Java对32位整数的范围限制在-2147483648-2147483647。Python的浮点数也没有大小限制，但是超出一定范围就直接表示为inf（无限大）。list [ ] 是一种有序的集合，可以随时添加和删除其中的元素。另一种有序列表叫元组：tuple ( )。tuple ( ) 和 list [ ]非常类似，但是 tuple 一旦初始化就不能修改。条件判断条件判断从上向下匹配，当满足条件时执行对应的块内语句，后续的elif和else都不再执行。使用dict和set请务必注意，dict内部存放的顺序和key放入的顺序是没有关系的。和list比较，dict有以下几个特点：查找和插入的速度极快，不会随着key的增加而变慢；需要占用大量的内存，内存浪费多。而list相反：查找和插入的时间随着元素的增加而增加；占用空间小，浪费内存很少。所以，dict是用空间来换取时间的一种方法。dict可以用在需要高速查找的很多地方，在Python代码中几乎无处不在，正确使用dict非常重要，需要牢记的第一条就是dict的key必须是不可变对象。这是因为dict根据key来计算value的存储位置，如果每次计算相同的key得出的结果不同，那dict内部就完全混乱了。这个通过key计算位置的算法称为哈希算法（Hash）。要保证hash的正确性，作为key的对象就不能变。在Python中，字符串、整数等都是不可变的，因此，可以放心地作为key。而list是可变的，就不能作为key。set可以看成数学意义上的无序和无重复元素的集合，set和dict的唯一区别仅在于没有存储对应的value。函数函数名其实就是指向一个函数对象的引用，完全可以把函数名赋给一个变量，相当于给这个函数起了一个“别名”。在Python中，定义一个函数要使用def语句，依次写出函数名、括号、括号中的参数和冒号:，然后，在缩进块中编写函数体，函数的返回值用return语句返回。空函数：如果想定义一个什么事也不做的空函数，可以用pass语句。定义函数时，需要确定函数名和参数个数；如果有必要，可以先对参数的数据类型做检查；函数体内部可以用return随时返回函数结果；函数执行完毕也没有return语句时，自动return None。函数可以同时返回多个值，但其实就是一个tuple。参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。李笑来《the-craft-of-selfteaching》（Part.2.D.2-aargs.ipynb）面对许多参数，Python 需要按照既定的规则（即，顺序）判定每个参数究竟是哪一类型的参数：Order of Arguments1.Positional2.Arbitrary Positional3.Keyword4.Arbitrary KeywordPython的函数具有非常灵活的参数形态，既可以实现简单的调用，又可以传入非常复杂的参数。默认参数一定要用不可变对象，如果是可变对象，程序运行时会有逻辑错误！要注意定义可变参数和关键字参数的语法：*args是可变参数，args接收的是一个tuple；**kw是关键字参数，kw接收的是一个dict。以及调用函数时如何传入可变参数和关键字参数的语法：可变参数既可以直接传入：func(1, 2, 3)，又可以先组装list或tuple，再通过args传入：func((1, 2, 3))；关键字参数既可以直接传入：func(a=1, b=2)，又可以先组装dict，再通过kw传入：func({'a': 1, 'b': 2})。使用 *args 和 **kw 是Python的习惯写法，当然也可以用其他参数名，但最好使用习惯用法。命名的关键字参数是为了限制调用者可以传入的参数名，同时可以提供默认值。定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符' *，'，否则定义的将是位置参数。使用递归函数的优点是逻辑简单清晰，缺点是过深的调用会导致栈溢出。针对尾递归优化的语言可以通过尾递归防止栈溢出。高级特性切片默认情况下，dict迭代的是key。 >>> isinstance('abc', Iterable) # str是否可迭代Python内置的 enumerate 函数可以把一个list变成索引-元素对。在一个列表生成式中，for 前面的 if ... else 是表达式，而 for 后面的 if 是过滤条件，不能带 else。要创建一个 generator，有很多种方法。第一种方法很简单，只要把一个列表生成式的 [] 改成 ()，就创建了一个 generator。如果一个函数定义中包含 yield 关键字，那么这个函数就不再是一个普通函数，而是一个 generator。凡是可作用于for循环的对象都是Iterable类型；凡是可作用于next()函数的对象都是Iterator类型，它们表示一个惰性计算的序列；集合数据类型如list、dict、str等是Iterable但不是Iterator，不过可以通过iter()函数获得一个Iterator对象。Python的for循环本质上就是通过不断调用next()函数实现的。函数式编程函数本身也可以赋值给变量，即：变量可以指向函数。把函数作为参数传入，这样的函数称为高阶函数，函数式编程就是指这种高度抽象的编程范式。map() 函数接收两个参数，一个是函数，一个是 Iterable， map 将传入的函数依次作用到序列的每个元素，并把结果作为新的 Iterator 返回。reduce 把一个函数作用在一个序列 [x1, x2, x3, ...] 上，这个函数必须接收两个参数，reduce 把结果继续和序列的下一个元素做累积计算。Python内建的 filter() 函数用于过滤序列。filter() 也接收一个函数和一个序列。和map()不同的是，filter()把传入的函数依次作用于每个元素，然后根据返回值是 True 还是 False 决定保留还是丢弃该元素。Python内置的 sorted() 函数就可以对 list 进行排序。它还可以接收一个 key 函数来实现自定义的排序，要进行反向排序，不必改动key函数，可以传入第三个参数 reverse=True。高阶函数除了可以接受函数作为参数外，还可以把函数作为结果值返回。返回闭包时牢记一点：返回函数不要引用任何循环变量，或者后续会发生变化的变量。关键字 lambda 表示匿名函数，冒号前面的 x 表示函数参数。匿名函数有个限制，就是只能有一个表达式，不用写 return ，返回值就是该表达式的结果。在代码运行期间动态增加功能的方式，称之为“装饰器”（Decorator）简单总结functools.partial的作用就是，把一个函数的某些参数给固定住（也就是设置默认值），返回一个新的函数，调用这个新函数会更简单。模块在 Python 中，一个.py文件就称之为一个模块（Module）。使用模块可以避免函数名和变量名冲突。为了避免模块名冲突，Python又引入了按目录来组织模块的方法，称为包（Package）。请注意，每一个包目录下面都会有一个__init__.py的文件，这个文件是必须存在的，否则，Python就把这个目录当成普通目录，而不是一个包。__init__.py可以是空文件，也可以有Python代码，因为__init__.py本身就是一个模块，而它的模块名就是mycompany。类似_xxx和__xxx这样的函数或变量就是非公开的（private），不应该被直接引用。在Python中，安装第三方模块，是通过包管理工具pip完成的。面向对象编程注：本文为在 廖雪峰的官方网站 学习Python3的学习笔记。
随着科技的进步，计算机应用越来越广。这几年人工智能、大数据的快速发展，再加上国家教育层面的鼓励，小朋友学编程的人数越来越多，各类的在线、线下培训机构如春笋般层出不穷。我们也试听了一些在线编程课，发现基本内容雷同，不外乎 Scratch 软件（或自行开发的类似 Scratch 的软件）和 Python 编程教学。下面给大家推荐的网站，适合很多年龄段的小朋友，可以先让小朋友跟着学，如果感兴趣，再报名兴趣班也不迟。不过，我看完网站的培训内容后发现，如果把网站的内容都学完了，基本也不用再报名兴趣班了，完全可以自学其他的编程知识了 ^_^ 。【网站地址】 https://code.org/【网站介绍】Code.org en® 是一个非营利组织。它致力于扩大在学校学习计算机科学的机会, 并增加妇女和代表性不足的少数群体的参与。 其愿景是：每个学校的每个学生都能像生物，化学，和代数一样学校计算机科学。 提供最广泛使用的中小学计算机科学课程，并每年组织编程一小时活动。这个活动已经吸引了全世界学生的10%参加。 Code.org 得到了亚马逊、脸书、谷歌、印孚瑟斯基金会，微软等更多慷慨捐助者的支持。【语言支持】可以通过网站页面页脚处的选择框切换成中文 “简体字”【课程介绍】课程 1 非常适合幼儿园的小朋友，里面的课程界面如下图：
pi@sirit:~ $ sudo rpi-update *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom *** Performing self-update % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 13545 100 13545 0 0 13853 0 --:--:-- --:--:-- --:--:-- 13849 *** Relaunching after update *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom *** We're running for the first time *** Backing up files (this will take a few minutes) *** Backing up firmware *** Backing up modules 4.14.79-v7+ ############################################################# WARNING: This update bumps to rpi-4.19.y linux tree Be aware there could be compatibility issues with some drivers Discussion here: https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=224931 ############################################################## Would you like to proceed? (y/N) *** Downloading specific firmware revision (this will take a few minutes) % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 168 0 168 0 0 193 0 --:--:-- --:--:-- --:--:-- 193 100 58.2M 100 58.2M 0 0 890k 0 0:01:06 0:01:06 --:--:-- 456k *** Updating firmware *** Updating kernel modules *** depmod 4.19.25+ *** depmod 4.19.25-v7+ *** Updating VideoCore libraries *** Using HardFP libraries *** Updating SDK *** Running ldconfig *** Storing current firmware revision *** Deleting downloaded files *** Syncing changes to disk *** If no errors appeared, your firmware was successfully updated to 82f49d8dfd9c000a203fb1deae64a3eb87abf3c1 *** A reboot is needed to activate the new firmware pi@sirit:~ $