python 笔记 PySimpleGUI 图形界面9

您所在的位置:网站首页 python模拟windows启动界面 python 笔记 PySimpleGUI 图形界面9

python 笔记 PySimpleGUI 图形界面9

#python 笔记 PySimpleGUI 图形界面9| 来源: 网络整理| 查看: 265

构建自定义窗口

整理 PySimpleGUI 官方网站 原文google翻译过来的

https://pysimplegui.readthedocs.io/en/latest/

如果使用诸如PyCharm之类的IDE,将会发现使用PySimpleGUI编写代码要容易得多。向您显示有关您正在进行的API调用的文档的功能将帮助您确定要更改的设置(如果有)。在PyCharm中,两个命令特别有用。

Control-Q (when cursor is on function name) brings up a box with the function definition Control-P (when cursor inside function call "()") shows a list of parameters and their default values 同步/异步Windows

PySimpleGUI的最常见用途是显示和收集用户信息。最简单的方法是使用“阻止” GUI调用。在等待用户关闭GUI窗口/对话框时,执行被“阻止”。

上面您已经看到了许多使用阻止窗口的示例。如果Read调用没有超时参数,您将知道它会阻塞。

阻塞读取(等待事件发生的读取)如下所示:

event, values = window.read()

非阻塞/异步读取调用如下所示:

event, values = window.read(timeout=100)

您可以在本文档末尾了解有关这些异步/非阻塞窗口的更多信息。

主题-Windows自动着色

在2019年12月,该功能change_look_and_feel被替换为theme。概念保持不变,但是一组新的函数调用使管理颜色和其他设置变得容易得多。

默认情况下,PySimpleGUI颜色主题为now Dark Blue 3。“系统默认值”灰色消失了。如果您希望窗口没有所有颜色,以便系统为您选择颜色(灰色),则将主题设置为’SystemDefault1’或Default1。

有130个主题。您可以通过调用预览这些主题,theme_previewer()这将创建一个LARGE窗口,显示所有可用的颜色主题。

在撰写本文时,这些是您可以使用的主题。 在这里插入图片描述

默认为 Dark Blue 3 在这里插入图片描述

在2019年12月,所有PySimpleGUI窗口的默认设置都从带有蓝色按钮的系统灰色更改为使用带有白色文本的灰蓝色的更完整的主题。以前,用户被困于选择灰色以外的颜色主题。现在已经为您完成,而不是of您。

如果您正在为此颜色主题苦苦挣扎,请添加一个呼叫以对其theme进行更改。

主题名称公式

您指定的主题名称可以是“模糊的”。文本不必与您看到的内容完全匹配。例如,“深蓝色3”,“深蓝色3”和“深蓝色3”都可以使用。

快速确定窗口最佳设置的一种方法是简单地使用许多不同的主题显示窗口。添加代码行以设置主题- theme(‘Dark Green 1’),运行代码,查看是否喜欢它,如果不喜欢,请将主题字符串更改为,'Dark Green 2’然后重试。重复直到找到喜欢的东西。

字符串的“公式”为:

Dark Color #

要么

Light Color #

颜色可以是蓝色,绿色,黑色,灰色,紫色,棕色,蓝绿色,红色。#是可选的,也可以是1到XX。一些颜色有很多选择。例如,有13种“浅棕色”选择。

“系统”默认设置-无颜色 如果您不愿意在窗口中使用任何颜色,请选择Default 1或System Default 1。

如果您想要蓝色按钮的原始PySimpleGUI配色方案以及其他所有灰色,则可以使用主题Default或System Default。

主题功能

基本主题函数调用为theme(theme_name)。这设定了主题。不带参数的调用theme()将返回当前主题的名称。

如果要获取或修改任何主题设置,则可以使用以下功能进行操作,这些功能将在文档底部的功能定义部分中找到有关的详细信息。如果不使用任何参数,则每个函数都将返回当前值。

theme_background_color theme_border_width theme_button_color theme_element_background_color theme_element_text_color theme_input_background_color theme_input_text_color theme_progress_bar_border_width theme_progress_bar_color theme_slider_border_width theme_slider_color theme_text_color

这些将帮助您获得可用选项的列表。

theme_list theme_previewer 窗口对象-开始一个窗口

第一步是使用所需的窗口自定义来创建窗口对象。

注意-PySimpleGUI中不直接支持“ 模式窗口 ”。除非您手动更改窗口的设置,否则所有窗口都可以随时访问。

重要说明 -许多Window方法要求您在调用方法之前先调用Window.read或Window.Finalize(或finalize=True在Window调用中设置)。这是因为这两个调用实际上是使用基础GUI框架创建窗口的。在进行这些调用之一之前,这些方法很可能会崩溃,因为它们尚未创建其基础小部件。

窗口位置

PySimpleGUI计算窗口的确切中心,并将窗口居中显示在屏幕上。如果您想将窗口放在其他位置,例如系统默认值(0,0),则有两种方法。首先是创建窗口时。使用location参数设置窗口的位置。第二种方法是使用该SetOptions调用,该调用将在将来为所有窗口设置默认窗口位置。

多显示器和Linux

如果您在Linux系统上有多个监视器,则PySimpleGUI窗口的自动居中(默认)位置可能不正确。在Windows上,作为tkinter使用并报告的主要监视器,多个监视器似乎可以正常工作。

具有多个监视器的Linux用户在使用默认位置运行时出现问题,需要通过设置location参数来指定在创建窗口时应放置窗口的位置。

视窗大小

您可以通过访问Size属性来获取窗口的大小。该窗口必须被读取一次或完成才能使该值正确。请注意,这是属性,而不是呼叫。

my_windows_size = window.Size 完成您的窗口: window = Window('My Title', layout, finalize=True) 元素大小

设置元素大小的方法有多种。他们是:

全局默认大小-使用SetOptions功能更改在窗口级别-使用default_element_size调用中的参数进行更改Window在元素级别-每个元素都有一个size参数 元素大小以字符为单位(有例外)。文本元素 size = (20,1)的大小为20个字符,宽1个字符。

PySimpleGUI的默认元素大小为(45,1)。

有几个小部件,其中大小值之一是像素而不是字符。这对于进度表和滑块是正确的。第二个参数是像素的“高度”。

没有标题栏

如果您希望创建没有窗口标题栏的干净美观的窗口,请在创建窗口时使用no_titlebar选项。

确保为您的用户提供“退出”按钮,否则他们将无法关闭窗口!如果没有启用标题栏,则该任务栏上的窗口将没有任何图标。没有退出按钮,您将需要通过taskmanager杀死…这很不好玩。

没有标题栏的Windows依赖于“随处抓取”选项来启用,否则您将无法移动窗口。

不带标题栏的Windows可用于轻松创建浮动启动器。

Linux用户!请注意,此设置对其他一些元素有副作用。多行输入根本不起作用,例如,因此,请谨慎使用。

在这里插入图片描述

随处抓

这是PySimpleGUI独有的功能。

注意-如果用户使用启用了grap_anywhere的按钮关闭非阻塞窗口,则会打印一条警告消息。这些消息没有危害,但可能会使用户感到困扰。如果您希望启用非阻塞窗口,则在创建窗口时只需获取grab_anywhere = True。

总在最前面

要将窗口保持在屏幕上所有其他窗口的顶部,请在创建窗口时将keep_on_top = True设置为True。此功能使浮动工具栏非常有用,并且始终在桌面上可见。

焦点

PySimpleGUI将为您设置默认的焦点位置。这通常表示第一个输入字段。您可以将焦点设置为特定元素。如果要自行设置焦点,则应通过use_default_focus=False在Window调用中进行设置来关闭自动焦点。

TTK按钮

从版本4.7.0开始,PySimpleGUI支持“常规” tk按钮和ttk按钮。需要进行此更改,以便Mac用户可以在按钮上使用颜色。尝试更改按钮颜色时,有一个错误会导致tk按钮不显示文本。请注意,如果从官方Python.org站点而不是使用Homebrew安装Python,则此问题将消失。切换后,即使tk Button在Mac上也可以使用,因此许多用户已经切换并非常满意。

默认情况下,使用按钮元素时,Mac用户将获得ttk按钮。所有其他平台将获得正常的tk Button。有一些方法可以覆盖此行为。一种是use_ttk_buttons在创建窗口时使用参数。如果设置为True,则窗口中的所有按钮均为ttk按钮。如果设置为False,则所有按钮均为普通tk按钮。如果未设置,则平台或按钮元素确定使用哪个。

如果需要系统范围的设置,则可以使用设置默认值set_options。这将影响所有窗口,例如弹出窗口和调试窗口。

TTK主题

tkinter具有许多可与ttk小部件一起使用的“主题”。在PySimpleGUI中,这些小部件包括-表格,树,组合框,按钮,ProgressBar,选项卡和TabGroup。有些元素具有“主题”参数,但不再使用,应将其忽略。PySimpleGUI的初始发行版试图在单个窗口中混合主题,但是由于已经了解到这是不可能的,因此可以在Window或系统级别进行设置。

如果需要系统范围的设置,则可以使用设置默认值set_options。这将影响所有窗口,例如弹出窗口和调试窗口。

ttk主题选择取决于平台。Linux的选择数量少于Windows。这些是Windows选项:“默认”,“ winnative”,“蛤”,“ alt”,“经典”,“ vista”,“ xpnative”

定义了一些常量来帮助您完成代码,以确定您的选择。主题常量以开头THEME_。例如,“蛤”主题是THEME_CLAM

强烈建议您尝试使用此设置来确定最喜欢的设置。他们以微妙但仍然有意义的方式更改了基于ttk的元素。

关闭视窗

完全完成一个窗口后,应将其关闭然后删除,以便正确清理资源,尤其是tkinter资源。

如果您希望在一行代码中执行此操作,请在下面一行:

window.close(); del window

删除有助于解决多线程应用程序遇到的问题,其中tkinter抱怨它是从错误的线程(不是程序的主线程)调用的

完成窗口形成的窗口方法

完成布局制作后,将其存储layout在这些示例中称为的变量中,即可创建窗口。

窗口的创建部分涉及3个步骤。

创建一个Window对象将布局添加到窗口可选-如果要在Read通话前进行更改,请完成

随着时间的流逝,PySimpleGUI代码继续进行压缩,压缩,以便程序员将需要编写的代码尽可能少。

个人电话

这是“长格式”,因为每种方法都单独调用。

window = sg.Window('My Title') window.layout(layout) window.finalize() 链接通话(旧方法)

进行的下一个压缩级别是将调用链接在一起,形成一行代码。

window = sg.Window('My Title').Layout(layout).finalize() 使用参数代替调用(新的首选方法)

这是一个新颖的概念,而不是使用链接,对于初学者来说是陌生的,而是使用参数进行Window调用。这正是从PySimpleGUI端口4.2开始发生的情况。

window = sg.Window('My Title', layout, finalize=True)

让Window初始化代码为您完成这项工作,而不是将工作推给进行布局和完成调用的用户。是的,现在听起来似乎很明显,但是几个月前还没有。

此功能已添加到所有4个PySimpleGUI端口,但PyPI上均未添加此功能,因为首先需要一些运行时以确保不会发生真正的不良情况。

调用以设置窗口布局。必须在之前调用Read。最有可能与Window的创建“链接”。

window = sg.Window('My window title', layout)

finalize()或Window参数finalize=True 调用以强制窗口经历初始化的最后阶段。这将导致分配tkinter资源,以便随后对其进行修改。这也会导致您的窗口出现。如果您不希望在调用Finalize时出现窗口,请在窗口的创建参数中将Alpha设置为0。

如果要调用元素的Update方法或调用Graph元素的图形基元,则必须先调用Read或Finalize在进行这些调用之前。

读取(超时=无,timeout_key = TIMEOUT_KEY) 读取窗口的输入值,并以流行的方式单击按钮

返回事件,值。通过设置timeout = 读取超时之前的毫秒数(此后将返回“超时事件”),可以添加超时。timeout_key的值将作为事件返回。如果您未指定超时键,则将TIMEOUT_KEY返回该值。

如果将超时设置为0,则读取将立即返回,而不是等待输入或超时。这是窗口的真正非阻塞“读取”。

版面

尽管在文档中到目前为止,您对每个可用元素的了解还不够多,但是您应该仔细阅读本节,因为您可以使用在此学习的技术来构建更好,更短,更容易理解的PySimpleGUI代码。

如果感觉这个布局部分太早了,那么在您了解每个元素之后,请回到本部分。 无论您发现什么顺序,最容易混淆的都是最好的。

虽然您还没有学习过Elements,但是本节很重要,因此在学习每个元素的工作原理之前,您已经学习了如何使用元素。至此,在您进行PySimpleGUI教育时,比起每个Element而言,更好地掌握与Element一起工作的省时方法。通过现在学习如何立即组装Elements,您将有一个很好的模型将学习的元素放入其中。

有几个 PySimpleGUI的方面,使更多的“Python化”比其他Python GUI软件开发工具包。PySimpleGUI独有的领域之一是如何定义,指定或构建窗口的“布局”。窗口的“布局”只是元素列表的列表。正如您已经了解的那样,这些列表组合在一起形成一个完整的窗口。这种定义窗口的方法非常强大,因为列表是整个Python语言的核心,因此非常易于创建和操作。

考虑一下,然后与Qt,tkinter等进行比较/对比。使用PySimpleGUI,元素在矩阵中的位置确定该元素在窗口中的显示位置。它是如此简单,并且使其功能异常强大。是否要在您的GUI中切换一行包含文本的行,而其下一行具有输入元素?没问题,交换代码行就可以完成。

布局设计为视觉化的。这个想法是让您能够通过简单地查看代码中的布局来设想窗口的外观。CODE本身与屏幕上显示的内容匹配。PySimpleGUI是纯Python代码和可视化GUI设计器之间的交叉。

在创建窗口的过程中,您可以操作这些元素列表,而不会影响元素或窗口。在执行列表的“布局”之前,它们不过是包含对象的列表(它们恰好是窗口的元素)。

很多时候,您的窗口定义/布局将是静态的,易于创建。

但是,窗口布局不限于是这些静态定义的元素列表之一。

生成的版式(如果您有> 5个重复元素/行,请务必阅读)

本节讨论了5种特定的生成布局的技术。它们可以单独使用或彼此结合使用。

布局+布局串联 [[A]] + [[B]] = [[A], [B]]同一行上的元素加法 [[A] + [B]] = [[A, B]]列表理解以生成行 [A for x in range(10)] = [A,A,A,A,A…]列表推导生成多行 [[A] for x in range(10)] = [[A],[A],…]用户定义元素/复合元素 示例-串联多个行的列表理解-“待办事项”列表示例

让我们创建一个小的布局,该布局将用于使用PySimpleGUI生成待办事项列表。

蛮力 import PySimpleGUI as sg layout = [ [sg.Text('1. '), sg.In(key=1)], [sg.Text('2. '), sg.In(key=2)], [sg.Text('3. '), sg.In(key=3)], [sg.Text('4. '), sg.In(key=4)], [sg.Text('5. '), sg.In(key=5)], [sg.Button('Save'), sg.Button('Exit')] ] window = sg.Window('To Do List Example', layout) event, values = window.read()

该脚本的输出是以下窗口:

在这里插入图片描述 花点时间看一下代码和生成的窗口。您是否可以查看布局并在屏幕上设想Window?

通过连接行进行构建

蛮力方法在长度为5的列表上效果很好,但是如果待办事项列表上有40项,该怎么办。那是什么?好的,那就是当我们转向“生成的”布局时,即由您的代码生成的布局。用此布局定义替换上一示例中的layout =内容。

import PySimpleGUI as sg layout = [] for i in range(1,6): layout += [sg.Text(f'{i}. '), sg.In(key=i)], layout += [[sg.Button('Save'), sg.Button('Exit')]] window = sg.Window('To Do List Example', layout) event, values = window.read()

当然,它会产生完全相同的窗口。进步了……从写出GUI的每一行到生成每一行。如果我们建议使用48个项目,请将range(1,6)更改为range(1,48)。每次通过列表时,都会在布局中添加另一行。

使用列表推导创建几行

但是,我们还没有完成!

这是Python * ,我们正在使用列表来构建内容,因此我们应该查看列表推导 ***。让我们将for循环更改为列表理解。回想一下,我们的for循环用于将6行连接到一个布局中。

layout = [[sg.Text(f'{i}. '), sg.In(key=i)] for i in range(1,6)]

在这里,我们将for循环移至列表定义的内部(列表理解)

串联多行

我们使用列表推导来构建行,现在只需要按钮即可。通过简单添加,可以轻松地将它们“粘到末端”。

layout = [[sg.Text(f'{i}. '), sg.In(key=i)] for i in range(1,6)] layout += [[sg.Button('Save'), sg.Button('Exit')]]

只要您有2种布局,就可以通过简单添加将它们串联起来。确保您的布局是“列表列表”布局。在上面的示例中,我们知道第一行是输入行的生成布局。最后一行在布局上添加了另一种布局…请注意格式为[[]]。

此按钮定义是一个完整的布局,可以添加到列表理解中

[[sg.Button('Save'), sg.Button('Exit')]]

这是很易读的代码。2个布局在视觉上排列得很好。

但是,我们不要只压缩代码而已。如何删除该+ =而不是将布局更改为仅一行,+而在两组行之间只有一个行。

在一行上进行此连接,我们最终得到了以下一条语句,该语句为GUI 创建了整个布局:

layout = [[sg.Text(f'{i}. '), sg.In(key=i)] for i in range(1,6)] + [[sg.Button('Save'), sg.Button('Exit')]] 最终的“待办事项”程序

这是我们的最终程序……所有4行。

import PySimpleGUI as sg layout = [[sg.Text(f'{i}. '), sg.In(key=i)] for i in range(1,6)] + [[sg.Button('Save'), sg.Button('Exit')]] window = sg.Window('To Do List Example', layout) event, values = window.read()

如果您确实想精打细算,可以通过将布局移至的调用中,使其成为2行程序(导入和1行代码)。 Window

import PySimpleGUI as sg event, values = sg.Window('To Do List Example', layout=[[sg.Text(f'{i}. '), sg.In(key=i)] for i in range(1,6)] + [[sg.Button('Save'), sg.Button('Exit')]]).read()

示例-建立行的列表理解-表模拟-输入网格 在此示例中,我们使用Input元素构建一个宽4 x高10的“表格”

我们寻求的最终结果是这样的:

HEADER 1HEADER 2HEADER 3HEADER 4INPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUTINPUT

代码完成后,结果将显示如下: 在这里插入图片描述 我们将使用列表推导来构建每一行,并且将使用另一个列表推导来连接行来构建表。那是一个遍历列表的理解,而另一个遍历布局的列表理解,是一排又一排地添加。

建立标题

首先让我们构建标题。这里有2个概念需要注意:

import PySimpleGUI as sg headings = ['HEADER 1', 'HEADER 2', 'HEADER 3','HEADER 4'] # the text of the headings header = [[sg.Text(' ')] + [sg.Text(h, size=(14,1)) for h in headings]] # build header layout

此代码中有两点需要注意1.构成标题元素的列表理解2.添加到前面的空格

让我们从头文件本身开始。

这是使一行文本元素包含标题文本的代码。结果是一行文本元素列表。

[sg.Text(h, size=(14,1)) for h in headings]

然后,我们添加一些空格以将标题移到上方,使它们位于其列的中心。我们只需Text在该标题列表的前面添加一个Element 即可。

header = [[sg.Text(' ')] + [sg.Text(h, size=(14,1)) for h in headings]]

此header变量是具有1行的布局,其中包含Text带有标题的一堆元素。

建立输入元素

所述Input元件被配置成格子。为此,我们将使用双列表理解。一个将构建行,另一个将行添加在一起以构成网格。这是执行此操作的代码行:

input_rows = [[sg.Input(size=(15,1), pad=(0,0)) for col in range(4)] for row in range(10)]

语句的这一部分仅包含4个Input元素的行

[sg.Input(size=(15,1), pad=(0,0)) for col in range(4)]

接下来,我们获取该Input元素列表,并使其与行数一样多,在这种情况下为10行。我们再次使用Python的出色列表推导将这些行加在一起。

input_rows = [[sg.Input(size=(15,1), pad=(0,0)) for col in range(4)] for row in range(10)]

第一部分应该看起来很熟悉,因为刚刚讨论的是构建单行的内容。要制作矩阵,我们只需简单地获取那一行,并创建10行,每行都是一个列表。

放在一起

这是我们的最终程序,该程序使用简单的加法将标题添加到输入矩阵的顶部。为了使其更具吸引力,颜色主题设置为“深棕色1”。

import PySimpleGUI as sg sg.theme('Dark Brown 1') headings = ['HEADER 1', 'HEADER 2', 'HEADER 3','HEADER 4'] header = [[sg.Text(' ')] + [sg.Text(h, size=(14,1)) for h in headings]] input_rows = [[sg.Input(size=(15,1), pad=(0,0)) for col in range(4)] for row in range(10)] layout = header + input_rows window = sg.Window('Table Simulation', layout, font='Courier 12') event, values = window.read()

图片 在这里插入图片描述

用户定义元素/复合元素

“用户定义的元素”和“复合元素”是包装在函数定义中的一个或多个PySimpleGUI元素。在布局中,它们看起来像是某种类型的自定义元素。

当您在布局中反复使用的元素上设置很多参数时,用户定义的元素特别有用。

示例-计算器应用程序的按钮网格

假设您要使用具有以下设置的按钮来制作计算器应用程序:

字体= Helvetica 20 大小= 5,1 按钮颜色=白色,蓝色 这些按钮之一的代码是:

sg.Button('1', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20))

如果您有6个按钮和5个向下按钮,则布局中将包含30行这些文本。

这些按钮的一行可以写成:

[sg.Button('1', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20)), sg.Button('2', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20)), sg.Button('3', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20)), sg.Button('log', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20)), sg.Button('ln', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20)), sg.Button('-', button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20))],

通过使用用户定义的元素,可以显着缩短布局。让我们称之为element CBtn。它会这样写:

def CBtn(button_text): return sg.Button(button_text, button_color=('white', 'blue'), size=(5, 1), font=("Helvetica", 20))

使用新CBtn元素,您可以将上面的按钮行重写为:

[CBtn('1'), CBtn('2'), CBtn('3'), CBtn('log'), CBtn('ln'), CBtn('-')],

看到大量不必编写的代码!每当您发现自己多次复制元素时,请使用此构造。

但是,我们不要就此止步。

由于我们一直在讨论列表推导,因此让我们使用它们来创建此行。做到这一点的方法是制作遍历该行的符号的列表,从而形成遍历该列表的循环。结果是一个看起来像这样的列表:

[CBtn(t) for t in ('1','2','3', 'log', 'ln', '-')],

该代码产生与我们手动创建的列表相同的列表:

[CBtn('1'), CBtn('2'), CBtn('3'), CBtn('log'), CBtn('ln'), CBtn('-')], 复合元素

就像Button可以从用户定义元素中返回一样,多个元素也可以返回。

回到我们之前做的“待办事项列表”示例,我们可以定义一个代表“待办事项”的用户定义元素,这一次我们添加了一个复选框。此列表中的一行将是:

项目#(Text元素)Checkbox表示完成的元素Input键入要做什么的元素 我们的用户元素的定义就是这个ToDoItem功能。它是单个用户元素,是3个PySimpleGUI元素的组合。 def ToDoItem(num): return [sg.Text(f'{num}. '), sg.CBox(''), sg.In()]

与我们之前学习的列表理解技术结合使用时,这使得创建5个待办事项列表变得非常简单。这是在我们的待办事项列表中创建5个条目所需的代码。

layout = [ToDoItem(x) for x in range(1,6)]

然后我们可以从字面上添加按钮

layout = [ToDoItem(x) for x in range(1,6)] + [[sg.Button('Save'), sg.Button('Exit')]]

这是我们的最终程序

import PySimpleGUI as sg def ToDoItem(num): return [sg.Text(f'{num}. '), sg.CBox(''), sg.In()] layout = [ToDoItem(x) for x in range(1,6)] + [[sg.Button('Save'), sg.Button('Exit')]] window = sg.Window('To Do List Example', layout) event, values = window.read()

它创建的窗口如下所示: 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3