Python + Tkinter:图片浏览器(二)

  • 前言
  • 说明
    • 改进
  • 功能
    • 图片导航
      • 显示上一张高清图片
      • 显示下一张高清图片
    • 缩略图导航
      • 缩略图列表
      • 缩略图上一页
      • 缩略图下一页
      • 缩略图指引
    • 图片管理
      • 高清图片
      • 删除图片
    • 图库管理
      • 浏览图库
      • 重载图库
    • 调整窗口
      • 窗口标题栏
      • 窗口自适应布局
        • 工具栏自适应
        • 高清图自适应
        • 导航按钮自适应
        • 缩略图自适应
      • GC
  • 模块
    • os
    • tkinter
    • PIL
    • 自定义
      • imageutil.py
      • imageviewer.py
  • 界面
  • 附录

前言

结合前面写的两版图片浏览器(附录),重新改写第二版。第一版可以查看缩略图和翻页,第二版结构简单,界面简洁清爽。结合两者的优点的同时,改进和增加翻页方式。在第一版中反复修改几次,一直不满意界面的风格。在写第二版时,就已经决定融合两者,最终形成当前的版本。在软件开发中,“循环翻页”一直深植人心。网站首页或栏目首页的图片轮换(轮播)就是一个很好的例子。在软件交互界面轻量化——简洁轻巧且高效的同时,尽量包含更多的功能。在有限的程序界面,精简关联密切的组件且保留其功能,是很有必要的。尤其是在值得重视的用户体验方面。融合关联密切的组件,自然而然地切换用户场景。这样可以极大释放有限的界面资源的同时,又可以显著提升用户体验。

说明

改进

在图片导航中,除了高清图片导航,还加入了图片列表的导航。在翻看图片的过程中,无缝地接入翻页的功能。用户在查看上一张或下一张图片,超过图片列表界限即列表开端和末尾时,就会自动翻页更新缩略图列表。图片列表同样具有类似的功能设计。点击缩略图列表的第一张和最后一张图片,都会进行翻页并触发列表的更新。

功能

图片导航

显示上一张高清图片

打开当前索引前一张图片。如果当前索引图片为第一张,则逆向翻页并显示最后一张图片。

显示下一张高清图片

打开当前索引下一张图片。如果当前索引图片为图片列表最后一张,则回到图片列表的第一页。

缩略图导航

缩略图列表

生成当前列表页面索引指向的5张图的缩略图列表。

缩略图上一页

生成当前列表索引(包含)前面5张图的缩略图列表。

缩略图下一页

生成当前列表索引(包含)后面5张图的缩略图列表。

缩略图指引

在缩略图列表中指示当前高清大图是哪一张。

图片管理

高清图片

打开图片列表中的任意一张的高清图片。

删除图片

删除当前显示的高清图片。

图库管理

浏览图库

打开文件夹浏览根目录下的所有图片。

重载图库

重新显示当前图库的所有图片。

调整窗口

窗口标题栏

初始化窗口标题栏为当前显示图片的文件名称。

窗口自适应布局

工具栏自适应

维持工具栏在按钮画布初始化时的相对位置不变。

高清图自适应

根据窗体大小变化,调节画布大小和居中显示图片。

导航按钮自适应

保持上一张和下一张在按钮画布初始化时的相对位置。

缩略图自适应

缩略图宽高预设不变,窗口大小变化时,调整缩略图列表居中显示。

GC

在关闭窗口退出程序时,清除内存缓存。

 def __del__(self):variable = Nonedel variable

模块

os

tkinter

PIL

自定义

imageutil.py

"""
@author: MR.N
@created: 2021-08-22 Sun. 21:54"""from PIL import Image, ImageTkS_WIDTH = 560
S_HEIGHT = 640
M_PAD_X = 1
M_PAD_Y = 20
SUB_HEIGHT = (S_HEIGHT - 2 * M_PAD_Y) // 5
SUB_WIDTH = SUB_HEIGHT
M_WIDTH = S_WIDTH + SUB_WIDTH + M_PAD_X
M_HEIGHT = S_HEIGHT
MIN_SUB_WIDTH = 16
MIN_SUB_HEIGHT = 16
I_WIDTH = S_WIDTH
I_HEIGHT = S_HEIGHTdef resize_img(path, scale=-1, screen_width=0, screen_height=0):image = Image.open(path)if scale == -1:if screen_width <= 0:screen_width = I_WIDTHif screen_height <= 0:screen_height = I_HEIGHTraw_width, raw_height = image.size[0], image.size[1]# max_width, max_height = I_WIDTH, I_HEIGHTmax_width, max_height = raw_width, screen_height# '''min_width = max(raw_width, max_width)min_height = int(raw_height * min_width / raw_width)while min_height > screen_height:min_height = int(min_height * .9533)while min_height < screen_height:min_height += 1min_width = int(raw_width * min_height / raw_height)'''min_height = max(raw_width, max_width)min_width = int(raw_width * min_height / raw_height)'''while min_width > screen_width:min_width -= 1min_height = int(raw_height * min_width / raw_width)elif scale == 1:raw_width, raw_height = image.size[0], image.size[1]max_width, max_height = raw_width, SUB_HEIGHT# '''min_width = max(raw_width, max_width)min_height = int(raw_height * min_width / raw_width)while min_height > SUB_HEIGHT:min_height = int(min_height * .9533)while min_height < SUB_HEIGHT:min_height += 1min_width = int(raw_width * min_height / raw_height)while min_width > SUB_WIDTH:min_width -= 1min_height = int(raw_height * min_width / raw_width)else:raw_width, raw_height = image.size[0], image.size[1]min_height = 18min_width = int(raw_width * min_height / raw_height)return image.resize((min_width, min_height))

imageviewer.py

# -*- coding: utf-8 -*-
"""
@file: imageviewer
@author: MR.N
@created: 2022/4/6 4月
@updated: 2022/4/22 4月
@version: 1.0
@blog: https://blog.csdn.net/qq_21264377
"""import tkinter as tk
from tkinter.filedialog import askdirectory
from imageutil import *
from PIL import Image, ImageTk
import osDEFAULT_IMAGE_DIR = 'img'def init_dir():img_dir = os.getcwd() + '/' + DEFAULT_IMAGE_DIRif not os.path.exists(img_dir):os.makedirs(img_dir)def load_cache(media_dir=None):if media_dir is None:current_dir = os.path.abspath('.') + '/' + DEFAULT_IMAGE_DIRelse:current_dir = media_dirif not current_dir.startswith('/'):current_dir = os.path.abspath('.') + '/' + current_dirfiles = os.listdir(current_dir)temp = []for file in files:if os.path.isfile(current_dir + '/' + file):temp.append(current_dir + '/' + file)if len(temp) < 1:return []files.clear()for file in temp:# common picture mediasif '.' in file and file.split('.')[-1].lower() in ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']:files.append(file)files.sort()return filesclass MyWindow:BUTTON_SIZE_NORMAL = 32def __init__(self):self.x = 0self.y = 0self.cursor = 0self.start_pos = 0self.first_load = Trueself.buttons = Noneself.photo = Noneself.sub_image = Noneself.sub_image2 = Noneself.sub_image3 = Noneself.sub_image4 = Noneself.sub_image5 = Noneself.sub_queue = []init_dir()self.window = tk.Tk()self.window.title('Gallery')self.window.config(bg='#111')self.window.overrideredirect(False)self.frame_left_bar = tk.Frame(self.window)self.frame_photo = tk.Frame(self.window)self.window_width = 0self.window_height = 0self.window.geometry(f'{M_WIDTH}x{M_HEIGHT}+{(self.window.winfo_screenwidth() - S_WIDTH) // 2}+{(self.window.winfo_screenheight() - S_HEIGHT) // 2 - 18}')self.window.bind('<Configure>', self.window_resize)# self.window.bind('<B1-Motion>', self.win_motion)self.window.protocol('WM_DELETE_WINDOW', self.win_quit)# self.window.bind('<Button-1>', self.left_down)self.frame_left_bar.config(bg='#111')self.frame_left_bar.config(width=SUB_WIDTH)self.photo_can = tk.Canvas(self.frame_photo)self.selected_dir = DEFAULT_IMAGE_DIRself.caches = load_cache(self.selected_dir)'''if len(self.caches) > 0:self.photo = ImageTk.PhotoImage(image=resize_img(self.caches[self.cursor], screen_width=S_WIDTH, screen_height=S_HEIGHT))self.photo_can.create_image(((S_WIDTH - self.photo.width()) // 2 - 36, (S_HEIGHT - self.photo.height()) // 2),anchor=tk.NW, image=self.photo)else:self.photo = None'''self.photo_can.update()self.photo_can.config(bg='#111')self.photo_can.config(highlightthickness=0)self.photo_can.config(width=M_WIDTH - M_PAD_X - SUB_WIDTH)self.photo_can.config(height=M_HEIGHT)# previous photoself.prev_button = tk.Button(self.window, text='‹')self.prev_button.config(command=self.prev_photo)self.prev_button.config(font=('', 16))self.prev_button.place(x=0, y=(S_HEIGHT - self.BUTTON_SIZE_NORMAL) // 2, width=self.BUTTON_SIZE_NORMAL,height=self.BUTTON_SIZE_NORMAL)self.photo_can.pack(side='left', expand='yes', fill='both', anchor='center')# next photoself.next_button = tk.Button(self.window, text='›')self.next_button.config(command=self.next_photo)self.next_button.config(font=('', 16))self.next_button.place(x=S_WIDTH - self.BUTTON_SIZE_NORMAL, y=(S_HEIGHT - self.BUTTON_SIZE_NORMAL) // 2,width=self.BUTTON_SIZE_NORMAL, height=self.BUTTON_SIZE_NORMAL)self.prev_page_button = tk.Button(self.window, text='⌃')self.prev_page_button.config(command=self.prev_page)self.prev_page_button.config(font=('', 10))self.next_page_button = tk.Button(self.window, text='⌄')self.next_page_button.config(command=self.next_page)self.next_page_button.config(font=('', 10))# open gallery of photo(s) in another folderself.open_button = tk.Button(self.window, text='…')self.open_button.config(command=self.select_dir)self.open_button.config(font=('', 10))self.reload_button = tk.Button(self.window, text='↺')self.reload_button.config(command=self.reload_cache)self.reload_button.config(font=('', 10))self.delete_button = tk.Button(self.window, text='⤫')self.delete_button.config(command=self.delete_cache)self.delete_button.config(font=('', 10))self.config_button()self.sub_image_can = tk.Button(self.window)self.sub_image_can.config(command=self.load_sub_1)self.sub_image_can2 = tk.Button(self.window)self.sub_image_can2.config(command=self.load_sub_2)self.sub_image_can3 = tk.Button(self.window)self.sub_image_can3.config(command=self.load_sub_3)self.sub_image_can4 = tk.Button(self.window)self.sub_image_can4.config(command=self.load_sub_4)self.sub_image_can5 = tk.Button(self.window)self.sub_image_can5.config(command=self.load_sub_5)# load sub-imagesself.load_sub()self.sub_image_cans = [self.sub_image_can, self.sub_image_can2, self.sub_image_can3, self.sub_image_can4,self.sub_image_can5]self.config_can()self.pointer = tk.Label(self.window, text='›')self.pointer.config(height=1)self.pointer.config(bg='#333')self.pointer.config(fg='#eee')self.frame_left_bar.pack(side='right', expand='yes', fill='y')self.frame_photo.pack(side='right', expand='yes', fill='both')self.window.mainloop()def win_quit(self, event=None):self.window.quit()self.__del__()def win_mini(self, event=None):self.window.state('icon')self.window.iconify()def left_down(self, event=None):self.x = event.xself.y = event.ydef win_motion(self, event):new_x = int(event.x - self.x) + self.window.winfo_x()new_y = int(event.y - self.y) + self.window.winfo_y()self.window.geometry(f'{self.window.winfo_width()}x{self.window.winfo_height()}+{new_x}+{new_y}')def config_can(self):for image_can in self.sub_image_cans:image_can.config(relief='ridge')image_can.config(fg='#fff')image_can.config(activeforeground='#f5f5f5')image_can.config(activebackground='#444')image_can.config(bg='#222')image_can.config(bd=0)image_can.config(highlightthickness=0)image_can.config(highlightcolor='#111')image_can.config(highlightbackground='#111')def config_button(self):self.buttons = [self.prev_button, self.next_button, self.prev_page_button, self.next_page_button,self.delete_button, self.open_button, self.reload_button]for button in self.buttons:button.config(relief='ridge')button.config(fg='#fff')button.config(activeforeground='#f5f5f5')button.config(activebackground='#444')button.config(bg='#222')button.config(bd=0)button.config(highlightthickness=0)button.config(highlightcolor='#111')button.config(highlightbackground='#111')def get_sub_image(self, file=None):return ImageTk.PhotoImage(image=resize_img(file, 1, screen_width=M_WIDTH, screen_height=M_HEIGHT))def load_photo(self):if len(self.caches) > 0:if 0 <= self.cursor <= len(self.caches) - 1:width = self.photo_can.winfo_width() if self.photo_can.winfo_width() > 0 else S_WIDTHheight = self.photo_can.winfo_height() if self.photo_can.winfo_height() > 0 else S_HEIGHTimage = resize_img(self.caches[self.cursor], screen_width=width, screen_height=height)self.photo = ImageTk.PhotoImage(image=image)self.photo_can.create_image(((width - self.photo.width()) // 2,(height - self.photo.height()) // 2),anchor=tk.NW, image=self.photo)self.window.title(f'{self.caches[self.cursor].split("/")[-1]}')else:self.photo = Noneself.photo_can.update()def load_sub(self):if 0 <= self.start_pos < len(self.caches):self.sub_image = self.get_sub_image(file=self.caches[self.start_pos])else:self.sub_image = self.get_sub_image(file=os.getcwd() + '/bg_empty.png')self.sub_image_can.config(image=self.sub_image)self.sub_image_can.update()if 0 <= self.start_pos + 1 < len(self.caches):self.sub_image2 = self.get_sub_image(file=self.caches[self.start_pos + 1])else:self.sub_image2 = self.get_sub_image(file=os.getcwd() + '/bg_empty.png')self.sub_image_can2.config(image=self.sub_image2)self.sub_image_can2.update()if 0 <= self.start_pos + 2 < len(self.caches):self.sub_image3 = self.get_sub_image(file=self.caches[self.start_pos + 2])else:self.sub_image3 = self.get_sub_image(file=os.getcwd() + '/bg_empty.png')self.sub_image_can3.config(image=self.sub_image3)self.sub_image_can3.update()if 0 <= self.start_pos + 3 < len(self.caches):self.sub_image4 = self.get_sub_image(file=self.caches[self.start_pos + 3])else:self.sub_image4 = self.get_sub_image(file=os.getcwd() + '/bg_empty.png')self.sub_image_can4.config(image=self.sub_image4)self.sub_image_can4.update()if 0 <= self.start_pos + 4 < len(self.caches):self.sub_image5 = self.get_sub_image(file=self.caches[self.start_pos + 4])else:self.sub_image5 = self.get_sub_image(file=os.getcwd() + '/bg_empty.png')self.sub_image_can5.config(image=self.sub_image5)self.sub_image_can5.update()def load_sub_1(self):if len(self.caches) > self.start_pos:self.cursor = self.start_posself.load_photo()self.point_to()def load_sub_2(self):if len(self.caches) > self.start_pos + 1:self.cursor = self.start_pos + 1self.load_photo()self.point_to()def load_sub_3(self):if len(self.caches) > self.start_pos + 2:self.cursor = self.start_pos + 2self.load_photo()self.point_to()def load_sub_4(self):if len(self.caches) > self.start_pos + 3:self.cursor = self.start_pos + 3self.load_photo()self.point_to()def load_sub_5(self):if len(self.caches) > self.start_pos + 4:self.cursor = self.start_pos + 4self.load_photo()self.point_to()def point_to(self):delta_cursor = self.cursor - self.start_posif 0 <= delta_cursor < len(self.sub_image_cans):width = SUB_WIDTH // 6height = SUB_HEIGHTx = self.window.winfo_width() - SUB_WIDTHy = self.sub_image_can.winfo_y() + delta_cursor * heightif self.sub_image_can.winfo_y() < M_PAD_Y:y += M_PAD_Yself.pointer.place(x=x, y=y, width=width, height=height)def prev_photo(self):if len(self.caches) > 0:updated = Falseself.cursor -= 1if self.cursor < self.start_pos:updated = Trueif self.start_pos - 5 >= 0:self.start_pos -= 5else:self.start_pos = len(self.caches) - 5if self.cursor < 0:updated = Trueself.cursor = len(self.caches) - 1self.load_photo()if updated:self.load_sub()self.point_to()def next_photo(self):if len(self.caches) > 0:updated = Falseself.cursor += 1if self.cursor > self.start_pos + 4:updated = Trueif self.start_pos + 5 < len(self.caches):self.start_pos += 5else:self.start_pos = 0if self.cursor >= len(self.caches):updated = Trueself.cursor = 0self.start_pos = 0self.load_photo()if updated:self.load_sub()self.point_to()def prev_page(self, event=None):if self.start_pos - 5 >= 0:self.start_pos -= 5else:self.start_pos = len(self.caches) - 5self.cursor = self.start_pos + 4self.load_photo()self.load_sub()self.point_to()def next_page(self, event=None):if self.start_pos + 5 < len(self.caches):self.start_pos += 5else:self.start_pos = 0self.cursor = self.start_posself.load_photo()self.load_sub()self.point_to()def window_resize(self, event=None):if event is not None:# listen events of window resizing.if self.window_width != self.photo_can.winfo_width() or self.window_height != self.photo_can.winfo_height():if self.window_width != self.photo_can.winfo_width():self.window_width = self.photo_can.winfo_width()if self.window_height != self.photo_can.winfo_height():self.window_height = self.photo_can.winfo_height()# What happens here?if self.first_load:self.first_load = Falseelse:self.photo_can.config(width=self.window.winfo_width() - M_PAD_X - SUB_WIDTH)self.photo_can.config(height=self.window.winfo_height())self.photo_can.update()self.load_photo()self.place_page_tool()self.place_tool()self.prev_button.place(x=0,y=(self.window_height - self.BUTTON_SIZE_NORMAL) // 2,width=self.BUTTON_SIZE_NORMAL,height=self.BUTTON_SIZE_NORMAL)self.next_button.place(x=self.window_width - self.BUTTON_SIZE_NORMAL,y=(self.window_height - self.BUTTON_SIZE_NORMAL) // 2,width=self.BUTTON_SIZE_NORMAL,height=self.BUTTON_SIZE_NORMAL)self.place_sub()self.point_to()def place_page_tool(self):x = self.window.winfo_width() - SUB_WIDTHy = 0width = SUB_WIDTHheight = M_PAD_Yself.prev_page_button.place(x=x, y=y, width=width, height=height)self.next_page_button.place(x=x, y=self.window_height - M_PAD_Y, width=width, height=height)def place_tool(self):i = 0for button in self.buttons[4:]:button.place(x=self.window.winfo_width() - SUB_WIDTH - 1 - self.BUTTON_SIZE_NORMAL * (len(self.buttons) - 2 - i),y=0,width=self.BUTTON_SIZE_NORMAL,height=self.BUTTON_SIZE_NORMAL)i += 1def place_sub(self):i = 0start_y = (self.window.winfo_height() - SUB_HEIGHT * 5) // 2# start_y += self.BUTTON_SIZE_NORMALfor sub_can in self.sub_image_cans:sub_can.place(x=self.window.winfo_width() - SUB_WIDTH, y=start_y + SUB_HEIGHT * i, width=SUB_WIDTH,height=SUB_HEIGHT)i += 1def reload_cache(self):caches = load_cache(self.selected_dir)if len(caches) > 0:self.caches = cachesself.cursor = 0self.start_pos = 0self.load_sub()self.point_to()self.load_photo()def select_dir(self):# Select the directory of photo(s).selected_dir = askdirectory()# Get () as return if selection is canceled or file dialog close.if selected_dir is not None and selected_dir != ():self.selected_dir = selected_dircaches = load_cache(self.selected_dir)if len(caches) > 0:self.caches = cachesself.cursor = 0self.start_pos = 0self.load_sub()self.point_to()self.load_photo()def delete_cache(self):if 0 <= self.cursor <= len(self.caches) - 1:cache = self.caches[self.cursor]if os.path.exists(cache):os.remove(cache)self.caches.remove(cache)if 0 <= self.cursor <= len(self.caches) - 1:passelse:if len(self.caches) > 0:self.cursor = len(self.caches) - 1else:self.cursor = 0self.point_to()self.load_photo()def __del__(self):self.photo = Noneself.caches = Noneself.window = Nonedel self.photodel self.cachesdel self.windowdef test():my_window = MyWindow()if __name__ == '__main__':test()

界面

附录

Python + Tkinter:图片浏览器(一)——最小体积
Python + Tkinter:简易图片浏览器

Python + Tkinter:图片浏览器(二)相关推荐

  1. Python Tkinter教程(二)——Label控件、Frame控件、Button控件的完整参数和所有方法及详细用法

    >>>[上节回顾:tkinter编程基本步骤.窗口基本属性及Toplevel控件的使用]<<< Python Tkinter教程(二)         这篇博客将详 ...

  2. python的tkinter插入图片_详解python tkinter 图片插入问题

    通过tkinter.PhotoImage插入GIF, PGM/PPM格式的图片. import tkinter class Gui: def __init__(self): self.gui=tkin ...

  3. 图片坐标提取软件/图片坐标点和像素点颜色提取软件/图片坐标获取工具/Python图片坐标获取源码/图片像素坐标获取软件/python tkinter 图片显示(完全开源)

    该软件使用python写的,可以提取像素点的坐标还有也能获取像素点的16进制数据RGB565和RGB888(RGB888仅最新的源码才支持),可以单点坐标也可以按键坐标,甚至可以使用简单的左右键配合使 ...

  4. python做图片浏览器_保护隐私,用Python打造自己的照片浏览器

    现代操作系统比如Win10, Mac都自带了很好用的照片浏览器,采用人像识别技术自动识别家庭成员,还有简单的画面增强功能.然而这些功能都是需要连接云端服务器来实现的.虽然说很难说这些大公司会对你的那些 ...

  5. python tkinter 图片插入问题

    通过tkinter.PhotoImage插入GIF, PGM/PPM格式的图片. import tkinterclass Gui:def __init__(self): self.gui=tkinte ...

  6. python做图片浏览器_python操作浏览器及截图小结

    近期做网页自动化用到内容小结 1.打开浏览器 1)打开默认配置的浏览器 from selenium import webdriver driver = webdriver.Firefox() &quo ...

  7. python tkinter图片为什么要设置全局变量_为什么这里一定要设置全局变量

    下面是程序其中有个变量y如果不开始设置成全局变量,y=y+k;这里就会提示报错说使用了未赋值的局部变量!前提是我再设置其他(inti,z,k,y;)已经设置后不能使用//题目:输入某年某月某... 下 ...

  8. python用图片浏览器打开图片

    import os os.system('start haha.jpg')

  9. python实现一个简单的图片浏览器

    上一个博客中学习了如何在网页上进行简单的图片爬取,目的是制作一个简单的图片浏览器,然后可以选择喜欢的图片筛选到另一个文件夹中. 爬取图片的博客链接:https://blog.csdn.net/yql_ ...

最新文章

  1. 数的补数 Number Complement
  2. 使用Nginx实现服务器反向代理和负载均衡
  3. C语言 矩阵的几种乘法
  4. MyBatis 缓存详解-缓存体系结构
  5. each 数据获取attr_我背着CSDN偷偷记录了大半年我博客数据
  6. sh执行文件 参数传递_sh 脚本执行sql文件传参数
  7. Asp.Net_文件操作基类
  8. Design Compiler指南——设计综合过程
  9. cp oracle,Oracle ASM使用asmcmd中的cp命令来执行远程复制
  10. 计算机程序班搞笑口号,二班班级霸气押韵口号(精选50句)
  11. 打乱魔方软件_怎样打乱魔方
  12. JAVA入门学习资料
  13. NoSQL数据库笔谈(2)
  14. 选择“正激”还是“反激”?这份宝典请收好~
  15. 微信小程序使用echarts绘画动态图表
  16. [图示]做人36字诀:一)社会交往--教你建功立业
  17. 冯诺依曼体系结构及操作系统(OS)的简单认识
  18. 球半足球比分,韩K联:江原FC - 金泉尚武 07月08日
  19. Ubuntu安装cuda10+cudnn7.5+Tensorflow2.0
  20. linux运维常用服务器软件整理和介绍

热门文章

  1. 王阳明:<二> 立志,勤学,改过,责善
  2. android 9平板电脑截屏,平板电脑怎么截图 平板电脑截图方法【详解】
  3. 王者荣耀官网制作心得
  4. markdown文档管理工具
  5. android 摄像头测血压,自拍测血压的手机软件来了!准确率高达95%!
  6. 记一次小米手机安装Google Play(其他手机类似)
  7. 第一次玩switch,需不需要再买一个任天堂Pro手柄
  8. IDELAY输入延迟分析
  9. 此战成硕,我成功上岸西南交通大学了~~~
  10. 一个08届毕业的学长写给即将毕业的09届的学弟学妹们