PyQt5桌面应用开发(21):界面设计结果自动测试(二)

2024-02-04 11:10

本文主要是介绍PyQt5桌面应用开发(21):界面设计结果自动测试(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文目录

  • PyQt5桌面应用系列
  • TDD+UI
    • 为什么?
  • 开发任务
    • 任务设计
    • 小码的工作
      • unittest函数一览表
    • UI单元测试代码
    • 控件代码
    • 测试报告
  • 总结

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16
  • PyQt5桌面应用开发(12):QFile与线程安全
  • PyQt5桌面应用开发(13):QGraphicsView框架
  • PyQt5桌面应用开发(14):数据库+ModelView+QCharts
  • PyQt5桌面应用开发(15):界面动画
  • PyQt5桌面应用开发(16):定制化控件-QPainter绘图
  • PyQt5桌面应用开发(17):类结构+QWebEngineView
  • PyQt5桌面应用开发(18):自定义控件界面设计与实现
  • PyQt5桌面应用开发(19):事件过滤器
  • PyQt5桌面应用开发(20):界面设计结果自动测试(一)
  • PyQt5桌面应用开发(21):界面设计结果自动测试(二)

TDD+UI

上一篇简单说了下什么是测试驱动开发(TDD)。这在软件开发行业是比较常规的做法,因为只要不是完美主义强迫症的TDD,对于软件开发的质量管理是很有帮助的,因为TDD把软件开发的质量管理(计划-执行-检查-改进)的循环变得更加可用。

但是加上UI后,这个问题就变得微妙起来了。UI是一种与人相关的内容,最好的做法当然是用人作为测试,常规开发过程中,也的确是依靠人力来进行测试。

那么采用TDD来开发UI,是否能行呢?答案是肯定的。软件行业,如果说有什么事情是不可行的,那么就只有说别的事情是不可行这个事情是不可行的。万物皆虚,万事皆允。软件开发的魅力不就在这里吗?

第二个问题就是,值得这么做吗?也有不多的几篇文章探讨了这个问题。也有两篇正经发表的会议文章写了这个事情。但是文章那么少就说明一个问题,可能意义不大,收益不高。

为什么?

有几个因素限制了TDD开发UI。

首先,UI的重要元素,视觉,难以测试,写形式化测试很难,所以这部分意义不大,比如界面布局,pyqt5的设计工具里面可以用C+R来观察布局,这个预览功能中,还能完美支持界面缩放这些操作,比如界面动画,那个测试难度可想而知,现在能考虑的最大可能就是截图比较像素,然后用某种大模型来判定,但是整这个意义不大,来个儿童就能完成;

其次,UI的完整状态空间特别大,如果要写一个全状态转换的状态机,显然是不可能的,因为排列的状态是界面交互元素个数阶乘量级,所以这样做的可行性也值得怀疑。

最后,UI的设计中,如果采用MVVM或者MVC模式,一般会尽量把功能、数据、交互分开,这样各个部分的单元测试才得以进行,界面的综合功能,还是采用综合测试的比较多。

这么分析下来,思路这篇文章就没多少意义了。但是我都已经写到这里,骑虎难下,还得硬撑下去。

那么先确定几个原则:

  1. 有限测试:把TDD作为开发工具,太难的,太麻烦的不搞,不搞完美主义;
  2. 人工视觉:不测试UI的布局、控件的位置、控件的名字、控件文字标签这些一眼就能看到的东西;
  3. 开发规范:应用场景考虑为刚入职的萌新提供较小的控件开发输入。

确定这个几个原则之后,我们来设计一个场景。

开发任务

任务设计

假设公司来一个小码,他各项技能点满,什么都能干,于是安排小码带一个团队编个PyQt5的工控。小码把其中的一个功能抽象为一个控件,这个控件要换算露点和相对湿度。

此处忽略饱和蒸气压、湿空气、理想气体、多组分气体分压、露点、相对湿度的内容15000字。我们最终只需要知道露点是压力、温度和相对湿度的函数,对应的通过压力、温度和露点也能唯一计算相对湿度。

D = f ( p , T , R ) D = f(p, T, R) D=f(p,T,R)

R = g ( p , T , D ) R = g(p, T, D) R=g(p,T,D)

最终有一个调库大牛编写了一个库,这个库需要CoolProp(pip install CoolProp)
来计算湿空气的热力学特性,提供接口全部使用SI单位,这个库提供两个静态方法(类方法)计算上面两个函数。另外这个库还能通过改变 p p p, T T T, D D D中任意一个来自动更新相对湿度,如果改变相对湿度,则自动改变露点。最后,这个库还能输入一个字符串元组表达四个值。这个库设计的不咋地,但是你懂的,热力学那帮人怎么会编程序……

from CoolProp.CoolProp import HAPropsSIclass RelativeHumidityModelSI:"""HumidAir model with SI unitstemperature/dewpoint: Kpressure: Pa"""def __init__(self):self._pressure: float = 1e5self._temperature: float = 300.0self._dewpoint: float = 273.0self._rh: float = self.rh(self._pressure, self._temperature, self._dewpoint)@classmethoddef rh(cls, p, t, d):return round(HAPropsSI("R", "P", p, "T", t, "D", d) * 10000) / 100.0@classmethoddef dp(cls, p, t, r):return HAPropsSI("D", "P", p, "T", t, "R", r)def _update_rh(self):self._rh = self.rh(self._pressure, self._temperature, self._dewpoint)def _update_dp(self):self._dewpoint = round(self.dp(self._pressure, self._temperature, self._rh / 100.0) * 100) / 100.0def str_tuple(self):return ("{:.2f}".format(i) for i in [self._pressure, self._temperature, self._dewpoint, self._rh])@propertydef pressure(self):return self._pressure@pressure.setterdef pressure(self, value):self._pressure = valueself._update_rh()@propertydef temperature(self):return self._temperature@temperature.setterdef temperature(self, value):self._temperature = valueself._update_rh()@propertydef dewpoint(self):return self._dewpoint@dewpoint.setterdef dewpoint(self, value):self._dewpoint = valueself._update_rh()@propertydef relative_humidity(self):return self._rh@relative_humidity.setterdef relative_humidity(self, value):self._rh = valueself._update_dp()

小码的工作

小码把编写这个PyQt5组件的任务交给了,比如说,小机。为了让小机好好干活对他进行合理恰当的压榨,引入了单元测试,让小机写一个能通过测试的组件,至于组件是否好看,那就看小机自己的眼睛了……

小码前面已经学习过QTest类的各种方法,能够点击按钮、键盘输入、移动鼠标,就只需要再学习一下unittest的函数:

unittest函数一览表

NameFunction
assertTrue表达式是否为True。
assertFalse检查表达式是否为False。
assertIs等效于assertTrue(a is b)。
assertIsNot等效于assertTrue(a is not b)。
assertIsInstance等效于assertTrue(isinstance(obj, cls))。
assertNotIsInstance与assertIsInstance对应,并非该类的对象。
assertIsNone等效于assertTrue(obj is None)。
assertIsNotNone与assertIsNone对应。
assertEqual运算符’=='意义上的相等。
assertNotEqual两个对象不相等,含义为’!='。
assertGreater等效于assertTrue(a > b)。
assertGreaterEqual等效于assertTrue(a >= b)。
assertLess等效于assertTrue(a < b)。
assertLessEqual等效于assertTrue(a <= b),提示信息更有效。
assertAlmostEqual在一定的允差下的近似相等。
assertNotAlmostEqual与assertAlmostEqual对应,不在允差范围内。
assertIn等效于assertTrue(a in b)。
assertNotIn等价于assertTrue(a not in b)
assertSequenceEqual序列相等(列表或者元组)
assertCountEqual测试集合元素的个数。
assertMultiLineEqual多行字符串相等。
assertTupleEqual元组相等。
assertListEqual列表相等。
assertSetEqual集合相等。
assertDictContainsSubset测试两个字典的包含性,一个是另外一个的子集。
assertDictEqual比较两个字典,assertEqual对字典同样是调用此方法。
assertRegex匹配到正则表达式。
assertNotRegex不匹配正则表达式。
assertLogs日志信息被处罚,按照日志等级来判定。
assertWarns触发特定的 warnClass
assertWarnsRegex警告消息与regexp吻合。
assertRaises触发特定的Exception类。
assertRaisesRegex触发的Exception对应的消息符合正则表达式。
assertNotEquals废弃,用assertNotEqual替代
assertAlmostEquals废弃,用assertAlmostEqual替代
assertEquals废弃,用assertEqual替代
assertNotAlmostEquals废弃,用assertNotAlmostEqual替代
assertNotRegexpMatches废弃,用assertNotRegex替代
assertRaisesRegexp废弃,用assertRaisesRegex替代
assertRegexpMatches废弃,用assertRegex替代
assert_废弃,用assertTrue替代

了解这些之后,小码很快就写出了下面的测试代码:

UI单元测试代码

import unittest
from unittest import TestCasefrom HtmlTestRunner import HTMLTestRunner
from PyQt5.QtCore import Qt
from PyQt5.QtTest import QTest
from PyQt5.QtWidgets import QLineEdit, QWidget, QApplication, QComboBox, QPushButtonimport relative_humidity_coolprop
from relative_humidity_ui import RelativeHumidityWidgetclass RelativeHumidityWidgetConstruction(TestCase):def setUp(self) -> None:self.app = QApplication([])self.widget = RelativeHumidityWidget()self.widget.show()QTest.qWaitForWindowExposed(self.widget)def tearDown(self) -> None:self.app.exit(0)# four line edit to represent four variablesdef test_interact_components(self):self.assertIsInstance(self.widget, QWidget)self.assertIsInstance(self.widget.pressureLineEdit, QLineEdit)self.assertIsInstance(self.widget.temperatureLineEdit, QLineEdit)self.assertIsInstance(self.widget.dewpointLineEdit, QLineEdit)self.assertIsInstance(self.widget.relativeHumidityLineEdit, QLineEdit)self.assertIsInstance(self.widget.calrh, QPushButton)self.assertIsInstance(self.widget.caldp, QPushButton)self.assertIsInstance(self.widget.pressureUnit, QComboBox)cb: QComboBox = self.widget.pressureUnitpressure_units = ['Pa', 'kPa', 'atm', 'bar', 'hPa']units = [cb.itemText(i) for i in range(cb.count())]self.assertCountEqual(pressure_units, units)self.assertListEqual(pressure_units, units)self.assertIsInstance(self.widget.temperatureUnit, QComboBox)cb: QComboBox = self.widget.temperatureUnittemperature_units = ["K", "℃"]units = [cb.itemText(i) for i in range(cb.count())]self.assertCountEqual(temperature_units, units)self.assertListEqual(temperature_units, units)self.assertIsInstance(self.widget.dewpointUnit, QComboBox)cb: QComboBox = self.widget.dewpointUnittemperature_units = ["K", "℃"]units = [cb.itemText(i) for i in range(cb.count())]self.assertCountEqual(temperature_units, units)self.assertListEqual(temperature_units, units)def test_initial_values(self):self.assertAlmostEqual(1e5, self.widget.pressure, delta=1e-5)self.assertAlmostEqual(300.0, self.widget.temperature, delta=1e-5)self.assertAlmostEqual(273.0, self.widget.dewpoint, delta=1e-5)self.assertAlmostEqual(17.07, self.widget.rh, delta=1e-2)def test_value_accessors(self):for p, value in zip([self.widget.pressureLineEdit,self.widget.temperatureLineEdit,self.widget.dewpointLineEdit,self.widget.relativeHumidityLineEdit],[lambda: self.widget.pressure,lambda: self.widget.temperature,lambda: self.widget.dewpoint,lambda: self.widget.rh]):p: QLineEditp.setText("akjdlajf")self.assertRaises(ValueError, lambda: value())p.setText("1e6")self.assertAlmostEqual(1e6, value(), delta=1e-6)def test_cal_relative_humidity(self):"""Test calrh button:return:"""# setup line edits for pressure, temperature, and dewpointp: QLineEdit = self.widget.pressureLineEditt: QLineEdit = self.widget.temperatureLineEditd: QLineEdit = self.widget.dewpointLineEditp.setText("1e5")t.setText("300")d.setText("250")# click calrh buttonQTest.mouseClick(self.widget.calrh, Qt.LeftButton)rh_expected = relative_humidity_coolprop.RelativeHumidityModelSI.rh(1e5, 300, 250)# check relative humidityself.assertAlmostEqual(rh_expected, self.widget.rh, delta=1e-2)def test_cal_dew_point(self):"""Test caldp button:return:"""# setup line edits for pressure, temperature, and relative humidityp: QLineEdit = self.widget.pressureLineEditt: QLineEdit = self.widget.temperatureLineEditrh: QLineEdit = self.widget.relativeHumidityLineEditp.setText("1e5")t.setText("300")rh.setText("50")# click caldp buttonQTest.mouseClick(self.widget.caldp, Qt.LeftButton)dp_expected = relative_humidity_coolprop.RelativeHumidityModelSI.dp(1e5, 300, 0.5)# check dew pointself.assertAlmostEqual(dp_expected, self.widget.dewpoint, delta=1e-2)# set unit to degree Cself.widget.dewpointUnit.setCurrentText("℃")QTest.mouseClick(self.widget.caldp, Qt.LeftButton)dp_expected = relative_humidity_coolprop.RelativeHumidityModelSI.dp(1e5, 300, 0.5)# check dew point return with SIself.assertAlmostEqual(dp_expected, self.widget.dewpoint, delta=1e-2)self.assertAlmostEqual(dp_expected - 273, float(self.widget.dewpointLineEdit.text()), delta=1e-2)def test_change_pressure_unit(self):self.assertAlmostEqual(1e5, self.widget.pressure, delta=1e-5)self.widget.pressureUnit.setCurrentText("kPa")self.assertAlmostEqual(1e5 * 1000, self.widget.pressure, delta=1e-5)self.widget.pressureUnit.setCurrentText("atm")self.assertAlmostEqual(1e5 * 101325, self.widget.pressure, delta=1e-5)self.widget.pressureUnit.setCurrentText("bar")self.assertAlmostEqual(1e5 * 1e5, self.widget.pressure, delta=1e-5)self.widget.pressureUnit.setCurrentText("hPa")self.assertAlmostEqual(1e5 * 1e2, self.widget.pressure, delta=1e-5)self.widget.pressureUnit.setCurrentText("Pa")self.assertAlmostEqual(1e5, self.widget.pressure, delta=1e-5)def test_change_temperature_unit(self):self.assertAlmostEqual(300, self.widget.temperature, delta=1e-5)self.widget.temperatureUnit.setCurrentText("℃")self.assertAlmostEqual(300 + 273, self.widget.temperature, delta=1e-5)self.widget.temperatureUnit.setCurrentText("K")self.assertAlmostEqual(300, self.widget.temperature, delta=1e-5)def test_change_dewpoint_unit(self):self.assertAlmostEqual(273, self.widget.dewpoint, delta=1e-5)self.widget.dewpointUnit.setCurrentText("℃")self.assertAlmostEqual(273 + 273, self.widget.dewpoint, delta=1e-5)self.widget.dewpointUnit.setCurrentText("K")self.assertAlmostEqual(273, self.widget.dewpoint, delta=1e-5)if __name__ == '__main__':# unittest.main()suite = unittest.TestLoader().loadTestsFromTestCase(RelativeHumidityWidgetConstruction)runner = HTMLTestRunner(verbosity=2, output='report', report_name='report', add_timestamp=True,combine_reports=True)# unittest.TextTestRunner(verbosity=2).run(suite)runner.run(suite)

这里为了方便小机编写的空间在其他成立使用,首先小码需要规定那些这个控件是QWidget的子类,还必须提供一些接口,比如获得压力、温度、露点、相对湿度(全部采用SI单位),另外,为了使用方便,还要规定提供表达单位的空间,必须是QComboBox,提供几种单位(压力和温度单位),最后要求设置的数值不合理的时候,要触发ValueError(这个实在太牵强……因为小码也是个渣渣),最后就是改变一个量,对应的相对湿度(或者露点)的变化。

写完这个,小码算是松了一口气。压力给到了小机这边。

控件代码

小机用尽了洪荒之力,反复徘徊于单元测试错误的重压之下,最终提交了一个代码:

import sysfrom PyQt5 import uic
from PyQt5.QtWidgets import QWidget, QLineEdit, QApplication, QComboBox, QPushButtonfrom relative_humidity_coolprop import RelativeHumidityModelSIdef temperature_SI(val, unit):""":param val::param unit:  K, degC:return: val in K"""if unit == "K":return valif unit == "℃":return val + 273raise ValueError(f"{unit} should be K or ℃")def pressure_SI(val, unit):if unit == "Pa":return valif unit == "kPa":return val * 1e3if unit == "atm":return val * 101325if unit == "bar":return val * 1e5if unit == "hPa":return val * 100raise ValueError(f"{unit} should be Pa, kPa, atm, bar, or hPa")class RelativeHumidityWidget(QWidget):def __init__(self, parent=None):self.model = RelativeHumidityModelSI()super(RelativeHumidityWidget, self).__init__(parent)uic.loadUi("relative_humidity.ui", self)self.pressureLineEdit: QLineEditself.pressureUnit: QComboBoxself.temperatureLineEdit: QLineEditself.temperatureUnit: QComboBoxself.dewpointLineEdit: QLineEditself.dewpointUnit: QComboBoxself.relativeHumidityLineEdit: QLineEditself.calrh: QPushButtonself.caldp: QPushButtonp, t, d, r = self.model.str_tuple()self.pressureLineEdit.setText(p)self.temperatureLineEdit.setText(t)self.dewpointLineEdit.setText(d)self.relativeHumidityLineEdit.setText(r)self.calrh.clicked.connect(self.calculate_rh)self.caldp.clicked.connect(self.calculate_dp)def calculate_dp(self):self.model.pressure = self.pressureself.model.temperature = self.temperatureself.model.relative_humidity = self.rhp, t, d, r = self.model.str_tuple()if self.dewpointUnit.currentText() == "℃":d = "{:.2f}".format(self.model.dewpoint - 273)self.dewpointLineEdit.setText(d)def calculate_rh(self):self.model.pressure = self.pressureself.model.temperature = self.temperatureself.model.dewpoint = self.dewpointp, t, d, r = self.model.str_tuple()self.relativeHumidityLineEdit.setText(r)@propertydef temperature(self):value: str = self.temperatureLineEdit.text()unit: str = self.temperatureUnit.currentText()return temperature_SI(float(value), unit)@propertydef pressure(self):value: str = self.pressureLineEdit.text()return pressure_SI(float(value), self.pressureUnit.currentText())@propertydef dewpoint(self):value: str = self.dewpointLineEdit.text()unit: str = self.dewpointUnit.currentText()return temperature_SI(float(value), unit)@propertydef rh(self):value: str = self.relativeHumidityLineEdit.text()return float(value)if __name__ == '__main__':app = QApplication([])widget = RelativeHumidityWidget()widget.show()sys.exit(app.exec_())

界面设计,当然是拖几个控件,搞搞布局就行。

在这里插入图片描述

对应的ui文件也很简单。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>Form</class><widget class="QWidget" name="Form"><property name="geometry"><rect><x>0</x><y>0</y><width>696</width><height>185</height></rect></property><property name="sizePolicy"><sizepolicy hsizetype="Preferred" vsizetype="Minimum"><horstretch>1</horstretch><verstretch>0</verstretch></sizepolicy></property><property name="windowTitle"><string>Form</string></property><layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0"><item><layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0" rowminimumheight="1,1,1,1,0"><item row="0" column="0"><widget class="QLabel" name="label"><property name="text"><string>Pressure</string></property></widget></item><item row="2" column="1"><widget class="QLineEdit" name="dewpointLineEdit"/></item><item row="0" column="1"><widget class="QLineEdit" name="pressureLineEdit"/></item><item row="1" column="1"><widget class="QLineEdit" name="temperatureLineEdit"/></item><item row="2" column="2"><widget class="QComboBox" name="dewpointUnit"><item><property name="text"><string>K</string></property></item><item><property name="text"><string></string></property></item></widget></item><item row="3" column="1"><widget class="QLineEdit" name="relativeHumidityLineEdit"/></item><item row="0" column="2"><widget class="QComboBox" name="pressureUnit"><item><property name="text"><string>Pa</string></property></item><item><property name="text"><string>kPa</string></property></item><item><property name="text"><string>atm</string></property></item><item><property name="text"><string>bar</string></property></item><item><property name="text"><string>hPa</string></property></item></widget></item><item row="2" column="0"><widget class="QLabel" name="label_5"><property name="text"><string>Dew Point</string></property></widget></item><item row="1" column="0"><widget class="QLabel" name="label_2"><property name="text"><string>Temperature</string></property></widget></item><item row="1" column="2"><widget class="QComboBox" name="temperatureUnit"><item><property name="text"><string>K</string></property></item><item><property name="text"><string></string></property></item></widget></item><item row="3" column="0"><widget class="QLabel" name="label_7"><property name="text"><string>RH</string></property></widget></item><item row="3" column="2"><widget class="QLabel" name="label_3"><property name="text"><string>%</string></property></widget></item></layout></item><item><layout class="QHBoxLayout" name="horizontalLayout"><item><widget class="QPushButton" name="calrh"><property name="text"><string>Relative Humidity</string></property></widget></item><item><widget class="QPushButton" name="caldp"><property name="text"><string>Dew Point</string></property></widget></item></layout></item><item><spacer name="verticalSpacer"><property name="orientation"><enum>Qt::Vertical</enum></property><property name="sizeType"><enum>QSizePolicy::MinimumExpanding</enum></property><property name="sizeHint" stdset="0"><size><width>0</width><height>0</height></size></property></spacer></item></layout></widget><resources/><connections/>
</ui>

测试报告

最终就能够运行python relative_humidity_unittest.py得到测试报告:

在这里插入图片描述

总结

通过完整走一遍TDD驱动的UI开发,小码和小机有几个体会。

  1. TDD对于UI设计有至少5毛钱的作用,通过编写单元测试,对单一控件的设计概念到设计实现由很好的推动作用;
  2. 看起来不是很好处理设计变更的……因为测试算例多了之后,跟踪变化将会消耗大量的注意力;
  3. 有单元测试的UI开发确实挺轻松,这样设计的工作就被很好地分离为功能设计和功能实现,对于某些场景的软件开发组织应该有很高的价值;
  4. TDD驱动UI设计,真的要注意的是,界面的正式设计文档、UI/UX设计的结果与测试用例之间的一致性问题。
  5. 行为测试的算例编写实际上进行的是设计工作,这一点必须要弄很清楚,所以编写UI测试用例不应该有一些一蹴而就、三两下就行的奢望,这是设计工作。

这篇关于PyQt5桌面应用开发(21):界面设计结果自动测试(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677296

相关文章

ONLYOFFICE 8.1 版本桌面编辑器测评

在现代办公环境中,办公软件的重要性不言而喻。从文档处理到电子表格分析,再到演示文稿制作,强大且高效的办公软件工具能够极大提升工作效率。ONLYOFFICE 作为一个功能全面且开源的办公软件套件,一直以来都受到广大用户的关注与喜爱。而其最新发布的 ONLYOFFICE 8.1 版本桌面编辑器,更是带来了诸多改进和新特性。本文将详细评测 ONLYOFFICE 8.1 版本桌面编辑器,探讨其在功能、用户

21.手绘Spring IOC运行时序图

1.再谈IOC与 DI IOC(lnversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建、依赖的代码,反转给容器来帮忙实现。那么必然的我们需要创建一个容器,同时需要一种描述来让 容器知道需要创建的对象与对象的关系。这个描述最具体表现就是我们所看到的配置文件。 DI(Dependency Injection)依赖注入:就是指对象是被动接受依赖类

亮相WOT全球技术创新大会,揭秘火山引擎边缘容器技术在泛CDN场景的应用与实践

2024年6月21日-22日,51CTO“WOT全球技术创新大会2024”在北京举办。火山引擎边缘计算架构师李志明受邀参与,以“边缘容器技术在泛CDN场景的应用和实践”为主题,与多位行业资深专家,共同探讨泛CDN行业技术架构以及云原生与边缘计算的发展和展望。 火山引擎边缘计算架构师李志明表示:为更好地解决传统泛CDN类业务运行中的问题,火山引擎边缘容器团队参考行业做法,结合实践经验,打造火山

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用。如果你看不懂,请留言。 完整代码: <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><ti

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述。以下是从不同角度对气象站的种类和应用范围的介绍: 一、气象站的种类 根据用途和安装环境分类: 农业气象站:专为农业生产服务,监测土壤温度、湿度等参数,为农业生产提供科学依据。交通气象站:用于公路、铁路、机场等交通场所的气象监测,提供实时气象数据以支持交通运营和调度。林业气象站:监测林区风速、湿度、温度等气象要素,为林区保护和

WDF驱动开发-WDF总线枚举(一)

支持在总线驱动程序中进行 PnP 和电源管理 某些设备永久插入系统,而其他设备可以在系统运行时插入和拔出电源。 总线驱动 必须识别并报告连接到其总线的设备,并且他们必须发现并报告系统中设备的到达和离开情况。 总线驱动程序标识和报告的设备称为总线的 子设备。 标识和报告子设备的过程称为 总线枚举。 在总线枚举期间,总线驱动程序会为其子 设备创建设备对象 。  总线驱动程序本质上是同时处理总线枚

PyTorch模型_trace实战:深入理解与应用

pytorch使用trace模型 1、使用trace生成torchscript模型2、使用trace的模型预测 1、使用trace生成torchscript模型 def save_trace(model, input, save_path):traced_script_model = torch.jit.trace(model, input)<

JavaWeb系列六: 动态WEB开发核心(Servlet) 上

韩老师学生 官网文档为什么会出现Servlet什么是ServletServlet在JavaWeb项目位置Servlet基本使用Servlet开发方式说明快速入门- 手动开发 servlet浏览器请求Servlet UML分析Servlet生命周期GET和POST请求分发处理通过继承HttpServlet开发ServletIDEA配置ServletServlet注意事项和细节 Servlet注