web自动化(4)——POM设计重构

1. 什么是POM

Page Object Model 是ui自动化测试中常见的封装方式。

原理:将页面封装为PO对象,然后通过面向对象的方式实现UI自动化

2. 封装原则

  1. PO无需包含全部UI元素
  2. PO应当验证元素
  3. PO不应该包含断言
  4. PO不应该暴露元素

3. 怎么进行POM封装

面向对象:属性和方法

封装步骤:

  1. 创建类,代表页面
  2. 创建类的属性,代表页面中的元素
  3. 创建类的方法,代表页面中的交互动作

在项目新建文件user_po.py

from selenium.webdriver import Chrome

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


# 1.创建类
class IndexPage:
    """首页:登录页面"""

    def __init__(self, driver: Chrome):  # 在进行实例化是被自动调用,可以接收参数
        self.driver = driver

    # 2.类的属性,即页面元素
    btn_login = (By.XPATH, '//*[@id="user_head_tip"]/a[1]')  # 立即登录按钮
    ipt_username = (By.XPATH, '//*[@id="login-email-address"]')  # 账号输入框
    ipt_password = (By.XPATH, '//*[@id="login-password"]')  # 密码输入框
    btn_submit = (By.XPATH, '//*[@id="ajax-login-submit"]')  # 登录按钮
    msg = (By.XPATH, '//*[starts-with(@id,"fanwe_")]/table/tbody/tr/td[2]/div[2]')  # 登录结果

    # 3.类的方法,即交互动作
    def login(self, username, password):
        self.driver.find_element(*self.btn_login).click()  # *表示元组解包
        self.driver.find_element(*self.ipt_username).send_keys(username)
        self.driver.find_element(*self.ipt_password).send_keys(password)
        self.driver.find_element(*self.btn_submit).click()
        # 显示等待:系统提示里不包含忘记密码并且系统系统不为空
        WebDriverWait(self.driver, 10).until(
            lambda x: "忘记密码?" not in self.driver.find_element(*self.msg).text and self.driver.find_element(
                *self.msg).text != ""
        )
        msg = self.driver.find_element(*self.msg).text
        return msg


# 1.一个页面一个类
class DealPage:
    """交易页面:投资"""
    # 2.类的属性,即页面元素
    ipt_money = (By.XPATH, '//*[@id="J_BIDMONEY"]')  # 投资金额
    btn_tz_submit = (By.XPATH, '//*[@id="tz_link"]')  # 立即投资
    ipt_pay_password = (By.XPATH, '//*[@id="J_bid_password"]')  # 支付密码
    btn_pay_submit = (By.XPATH, '//*[@id="J_bindpassword_btn"]')  # 确定
    msg = (By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]')

    def __init__(self, driver: Chrome):  # 在进行实例化是被自动调用,可以接收参数
        self.driver = driver

    # 3.类的方法,即交互动作
    def pay(self, money, pay_password):
        self.driver.find_element(*self.ipt_money).send_keys(money)
        self.driver.find_element(*self.btn_tz_submit).click()
        self.driver.find_element(*self.ipt_pay_password).send_keys(pay_password)
        self.driver.find_element(*self.btn_pay_submit).click()
        msg = WebDriverWait(self.driver, 10).until(
            lambda x: self.driver.find_element(
                By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]'
            ).text
        )
        return msg


if __name__ == '__main__':
    driver = Chrome()
    driver.implicitly_wait(10)  # 隐式等待
    driver.get('http://47.107.116.139/fangwei/index.php')
    page = IndexPage(driver)
    msg = page.login('admin', 'msjy123')
    print(msg)
    driver.get('http://47.107.116.139/fangwei/index.php?ctl=deal&id=25370&preview=1')
    page = DealPage(driver)
    msg = page.pay(100, 'msjy123')
    print(msg)
    driver.quit()

4. 引入pytest

  1. 自动判断用例执行结果
  2. 统计用例成功数量
  3. 统一形成测试报告

4.1 安装

pip install pytest

4.2 编写夹具fixture

测试用例所依赖的一些组件应该在夹具中设置就绪:例如启动浏览器,最大化浏览器等。在根目录创建conftest.py文件,在该文件中写夹具

import pytest
from selenium.webdriver import Chrome


# scope用来指定夹具作用域,function指函数,module指模块,如果设置scope=function表示每执行一个函数都会进行浏览器重启和关闭
@pytest.fixture(scope='module')  # 这里设置scope=module是因为如果为函数级别的话,我们在test_user中第1个用例如果关闭了浏览器第二个用例就需要重新登录
def driver():
    driver = Chrome()
    driver.implicitly_wait(5)
    driver.maximize_window()
    yield driver
    driver.quit()

创建test_user.py,代码如下:

from user_po import IndexPage, DealPage


# 在用例中使用夹具:将夹具名称写在参数当中
def test_login(driver):
    driver.get('http://47.107.116.139/fangwei/index.php')
    page = IndexPage(driver)
    msg = page.login('admin', 'msjy123')
    assert msg == '成功登录'


def test_deal(driver):
    driver.get('http://47.107.116.139/fangwei/index.php?ctl=deal&id=25370&preview=1')
    page = DealPage(driver)
    msg = page.pay(100, 'msjy123')
    assert msg == '投标成功!'

在pycharm终端输入pytest即可自动执行test_user.py中的两个测试用例。

4.3 编写测试用例

以登录功能为例,存在以下几种情况,不同的输入会有不同的输出结果,

  • 用户名密码为空:Email格式错误,请重新输入或者昵称格式错误,请重新输入
  • 用户名正确,密码为空:密码格式错误,请重新输入
  • 错误用户名,密码正确:用户不存在
  • 正确用户名,错误密码:密码错误
  • 正确用户名,正确密码:成功登录
  • 首先新建ddt_login.csv,用于存放我们的用例数据,做参数化使用
用户名,密码,登录结果
,,Email格式错误,请重新输入或者昵称格式错误,请重新输入
admin,,密码格式错误,请重新输入
asasasddd,msjy123,用户不存在
admin,msjy123456,密码错误
admin,msjy123,成功登录

接下来将我们的test_user.py代码修改一下,因为输入不再是固定的,所以使用参数传入:

import csv

import pytest

from user_po import IndexPage, DealPage


# 读取csv文件数据作为参数化数据,使用装饰器进行参数化
@pytest.mark.parametrize("data", csv.DictReader(open("ddt_login.csv", encoding="utf-8-sig")))
# 在用例中使用夹具:将夹具名称写在参数当中
def test_login(driver, data):
    driver.get('http://47.107.116.139/fangwei/index.php')
    page = IndexPage(driver)
    msg = page.login(data["用户名"], data["密码"])
    assert msg == data["登录结果"]


def test_deal(driver):
    driver.get('http://47.107.116.139/fangwei/index.php?ctl=deal&id=25370&preview=1')
    page = DealPage(driver)
    msg = page.pay(100, 'msjy123')
    assert msg == '投标成功!'

5. po封装管理后台

要封装后台管理的po,如果新建po文件,如果每个po都新建一个文件会导致文件过多,因此我们需要将原来的user_po.py文件名修改为pages.py,然后在里面添加类就可以了。然后添加类的时候我们可以发现每个类都有一个init方法,造成了代码重复,因此我们使用BasePage抽象类,它表示所有页面共用的代码,我们将init方法写在BasePage类中,后续使用时只需要继承即可。修改后代码如下:

from selenium.webdriver import Chrome

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


# 抽象类,所有页面的公共代码,需要使用时继承即可
class BasePage:
    def __init__(self, driver: Chrome):  # 在进行实例化是被自动调用,可以接收参数
        self.driver = driver


# 1.创建类
class IndexPage(BasePage):
    """首页:登录页面"""

    # 2.类的属性,即页面元素
    btn_login = (By.XPATH, '//*[@id="user_head_tip"]/a[1]')  # 立即登录按钮
    ipt_username = (By.XPATH, '//*[@id="login-email-address"]')  # 账号输入框
    ipt_password = (By.XPATH, '//*[@id="login-password"]')  # 密码输入框
    btn_submit = (By.XPATH, '//*[@id="ajax-login-submit"]')  # 登录按钮
    msg = (By.XPATH, '//*[starts-with(@id,"fanwe_")]/table/tbody/tr/td[2]/div[2]')  # 登录结果

    # 3.类的方法,即交互动作
    def login(self, username, password):
        self.driver.find_element(*self.btn_login).click()  # *表示元组解包
        self.driver.find_element(*self.ipt_username).send_keys(username)
        self.driver.find_element(*self.ipt_password).send_keys(password)
        self.driver.find_element(*self.btn_submit).click()
        # 显示等待:系统提示里不包含忘记密码并且系统系统不为空
        WebDriverWait(self.driver, 10).until(
            lambda x: "忘记密码?" not in self.driver.find_element(*self.msg).text and self.driver.find_element(
                *self.msg).text != ""
        )
        msg = self.driver.find_element(*self.msg).text
        return msg


# 1.一个页面一个类
class DealPage(BasePage):
    """交易页面:投资"""
    # 2.类的属性,即页面元素
    ipt_money = (By.XPATH, '//*[@id="J_BIDMONEY"]')  # 投资金额
    btn_tz_submit = (By.XPATH, '//*[@id="tz_link"]')  # 立即投资
    ipt_pay_password = (By.XPATH, '//*[@id="J_bid_password"]')  # 支付密码
    btn_pay_submit = (By.XPATH, '//*[@id="J_bindpassword_btn"]')  # 确定
    msg = (By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]')

    # 3.类的方法,即交互动作
    def pay(self, money, pay_password):
        self.driver.find_element(*self.ipt_money).send_keys(money)
        self.driver.find_element(*self.btn_tz_submit).click()
        self.driver.find_element(*self.ipt_pay_password).send_keys(pay_password)
        self.driver.find_element(*self.btn_pay_submit).click()
        msg = WebDriverWait(self.driver, 10).until(
            lambda x: self.driver.find_element(
                By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]'
            ).text
        )
        return msg


然后我们将之前写的admin.py中管理后台的代码进行po封装,同样是创建类,属性和方法,写入pages.py,如下:

import time

from selenium.webdriver import Chrome

from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

from funcs import img1code, is_login


# 抽象类,所有页面的公共代码,需要使用时继承即可
class BasePage:
    def __init__(self, driver: Chrome):  # 在进行实例化是被自动调用,可以接收参数
        self.driver = driver


# 1.创建类
class IndexPage(BasePage):
    """首页:登录页面"""

    # 2.类的属性,即页面元素
    btn_login = (By.XPATH, '//*[@id="user_head_tip"]/a[1]')  # 立即登录按钮
    ipt_username = (By.XPATH, '//*[@id="login-email-address"]')  # 账号输入框
    ipt_password = (By.XPATH, '//*[@id="login-password"]')  # 密码输入框
    btn_submit = (By.XPATH, '//*[@id="ajax-login-submit"]')  # 登录按钮
    msg = (By.XPATH, '//*[starts-with(@id,"fanwe_")]/table/tbody/tr/td[2]/div[2]')  # 登录结果

    # 3.类的方法,即交互动作
    def login(self, username, password):
        self.driver.find_element(*self.btn_login).click()  # *表示元组解包
        self.driver.find_element(*self.ipt_username).send_keys(username)
        self.driver.find_element(*self.ipt_password).send_keys(password)
        self.driver.find_element(*self.btn_submit).click()
        # 显示等待:系统提示里不包含忘记密码并且系统系统不为空
        WebDriverWait(self.driver, 10).until(
            lambda x: "忘记密码?" not in self.driver.find_element(*self.msg).text and self.driver.find_element(
                *self.msg).text != ""
        )
        msg = self.driver.find_element(*self.msg).text
        return msg


# 1.一个页面一个类
class DealPage(BasePage):
    """交易页面:投资"""
    # 2.类的属性,即页面元素
    ipt_money = (By.XPATH, '//*[@id="J_BIDMONEY"]')  # 投资金额
    btn_tz_submit = (By.XPATH, '//*[@id="tz_link"]')  # 立即投资
    ipt_pay_password = (By.XPATH, '//*[@id="J_bid_password"]')  # 支付密码
    btn_pay_submit = (By.XPATH, '//*[@id="J_bindpassword_btn"]')  # 确定
    msg = (By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]')

    # 3.类的方法,即交互动作
    def pay(self, money, pay_password):
        self.driver.find_element(*self.ipt_money).send_keys(money)
        self.driver.find_element(*self.btn_tz_submit).click()
        self.driver.find_element(*self.ipt_pay_password).send_keys(pay_password)
        self.driver.find_element(*self.btn_pay_submit).click()
        msg = WebDriverWait(self.driver, 10).until(
            lambda x: self.driver.find_element(
                By.XPATH, '//*[@id="fanwe_success_box"]/table/tbody/tr/td[2]/div[2]'
            ).text
        )
        return msg


class AdminLoginPage(BasePage):
    """后台管理登录页面"""
    ipt_username = (By.XPATH, '/html/body/form/table/tbody/tr/td[3]/table/tbody/tr[2]/td[2]/input')
    ipt_password = (By.XPATH, '/html/body/form/table/tbody/tr/td[3]/table/tbody/tr[3]/td[2]/input')
    ipt_verify = (By.XPATH, '/html/body/form/table/tbody/tr/td[3]/table/tbody/tr[5]/td[2]/input')
    img_verify = (By.XPATH, '//*[@id="verify"]')
    btn_submit = (By.XPATH, '//*[@id="login_btn"]')

    def login(self, username, password):
        self.driver.find_element(*self.img_verify).screenshot("../temp/code.png")
        code = img1code("../temp/code.png")
        self.driver.find_element(*self.ipt_username).send_keys(username)
        self.driver.find_element(*self.ipt_password).send_keys(password)
        self.driver.find_element(*self.ipt_verify).send_keys(code)
        self.driver.find_element(*self.btn_submit).click()
        time.sleep(1)
        return is_login(self.driver)  # 使用is_login函数返回值作为交互返回值


class AdminIndexPage(BasePage):
    """后台管理首页"""
    ifm_top = (By.XPATH, '/html/frameset/frame[1]')
    ifm_left = (By.XPATH, '//*[@id="menu-frame"]')
    ifm_main = (By.XPATH, '//*[@id="main-frame"]')

    def to_deal(self):
        self.driver.refresh()
        # 进入框架
        iframe = self.driver.find_element(*self.ifm_top)
        self.driver.switch_to.frame(iframe)
        self.driver.find_element(By.LINK_TEXT, '贷款管理').click()  # 点击贷款管理
        self.driver.switch_to.default_content()  # 退出框架

        iframe = self.driver.find_element(*self.ifm_left)
        self.driver.switch_to.frame(iframe)
        self.driver.find_element(By.LINK_TEXT, '全部贷款').click()  # 点击全部贷款
        self.driver.switch_to.default_content()  # 退出框架

        iframe = self.driver.find_element(*self.ifm_main)
        self.driver.switch_to.frame(iframe)

        return AdminDealPage(self.driver)


class AdminDealPage(BasePage):
    """贷款管理页面"""
    btn_new_deal = (By.XPATH, '/html/body/div[2]/div[3]/input[1]')  # 新增贷款按钮
    tr_deal = (By.XPATH, '//tr[contains(@class,"row")]')  # 列表所有

    def new_deal(self):
        self.driver.find_element(*self.btn_new_deal).click()
        return AdminNewDealPage(self.driver)  # 返回po表示已经进入新增贷款页面


class AdminNewDealPage(BasePage):
    """新增贷款页面"""
    ipt_name = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[4]/td[2]/input')  # 贷款名称
    ipt_shor_name = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[5]/td[2]/input')  # 简短名称
    ipt_username = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[6]/td[2]/input[1]')  # 会员名称
    btn_username = (By.XPATH, '//strong[text()="beifan"]')
    btn_city = (By.XPATH, '//*[@id="citys_box"]/div[1]/div[2]/input[1]')  # 所在城市
    sel_cate = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[8]/td[2]/select')  # 分类-房产抵押
    btn_show_upload = (By.XPATH,
                       '/html/body/div[2]/form/table[1]/tbody/tr[14]/td[2]/span/div[1]/div/div/button')  # 图片上传按钮
    btn_show_local_upload = (By.XPATH, '/html/body/div[6]/div[1]/div[2]/div/div[1]/ul/li[2]')  # 本地上传按钮
    ipt_upload = (By.XPATH, '//input[@type="file"]')  # 发送文件
    btn_submit_upload = (By.XPATH, '/html/body/div[6]/div[1]/div[3]/span[1]/input')  # 确定上传按钮
    sel_type = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[15]/td[2]/select')  # 借款用途
    sel_contract = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[17]/td[2]/select')  # 借款合同范本
    sel_tcontract = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[18]/td[2]/select')  # 转让合同范本
    ipt_amount = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[19]/td[2]/input')  # 借款金额
    ipt_rate = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[27]/td[2]/input')  # 年利率
    ipt_enddate = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[28]/td[2]/input')  # 筹标期限
    btn_status = (By.XPATH, '/html/body/div[2]/form/table[1]/tbody/tr[33]/td[2]/label[1]/input')  # 借款状态
    ipt_start_time = (By.XPATH, '//*[@id="start_time"]')  # 开始时间
    btn_submit = (By.XPATH, '/html/body/div[2]/form/table[6]/tbody/tr[2]/td[2]/input[4]')  # 新增提交按钮
    msg=(By.XPATH, '/html/body/div/table/tbody/tr[3]/td')

    def submit(self, data):  # data是包含了多个参数的字典
        """提交新的贷款"""
        # 贷款名称
        self.driver.find_element(*self.ipt_name).send_keys(data['name'])
        # 简短名称
        self.driver.find_element(*self.ipt_shor_name).send_keys(data['shor_name'])
        # 会员名称
        self.driver.find_element(*self.ipt_username).send_keys(data['username'])
        self.driver.find_element(*self.btn_username).click()
        # 城市
        self.driver.find_element(*self.btn_city).click()
        # 分类-房产抵押
        el = self.driver.find_element(*self.sel_cate)
        Select(el).select_by_visible_text(data['cate'])
        # 图片上传
        self.driver.find_element(*self.btn_show_upload).click()
        self.driver.find_element(*self.btn_show_local_upload).click()
        self.driver.find_element(*self.ipt_upload).send_keys(data['upload'])
        self.driver.find_element(*self.btn_submit_upload).click()
        # 借款用途
        el = self.driver.find_element(*self.sel_type)
        Select(el).select_by_visible_text(data['type'])
        # 借款合同范本
        el = self.driver.find_element(*self.sel_contract)
        Select(el).select_by_visible_text(data['contract'])
        # 转让合同
        el = self.driver.find_element(*self.sel_tcontract)
        Select(el).select_by_visible_text(data['tcontract'])
        # 借款金额
        el = self.driver.find_element(*self.ipt_amount)
        el.clear()
        el.send_keys(data['amount'])
        # 年利率
        el = self.driver.find_element(*self.ipt_rate)
        el.clear()
        el.send_keys(data['rate'])
        # 筹标期限
        el = self.driver.find_element(*self.ipt_enddate)
        el.clear()
        el.send_keys(data['enddate'])
        # 借款状态
        self.driver.find_element(*self.btn_status).click()
        # 开始时间
        el = self.driver.find_element(*self.ipt_start_time)
        self.driver.execute_script("arguments[0].scrollIntoView()", el)
        self.driver.execute_script(f"arguments[0].value='{data['start_time']}'", el)
        # 新增提交
        self.driver.find_element(*self.btn_submit).click()
        # 系统提示
        el = self.driver.find_element(*self.msg)
        return el.text

完成后我们创建test_admin.py文件,去编写测试用例,我们在编写新增贷款流程的测试用例的时候,首先需要登录,我们可以在conftest.py中新建一个fixture夹具,如下:

import pytest
from selenium.webdriver import Chrome

from funcs import save_cookies, load_cookies,is_login
from pages import AdminLoginPage


# scope用来指定夹具作用域,function指函数,module指模块,如果设置scope=function表示每执行一个函数都会进行浏览器重启和关闭
@pytest.fixture(scope='module')  # 这里设置scope=module是因为如果为函数级别的话,我们在test_user中第1个用例如果关闭了浏览器第二个用例就需要重新登录
def driver():
    driver = Chrome()
    driver.implicitly_wait(5)
    driver.maximize_window()
    yield driver
    driver.quit()


@pytest.fixture(scope='session')
def admin_driver():
    """已经登陆的浏览器,给test_admin使用"""
    driver = Chrome()
    driver.implicitly_wait(5)
    driver.maximize_window()
    load_cookies(driver)
    # 判断:只有未登录才进行登录流程
    if is_login(driver) is False:
        page = AdminLoginPage(driver)  # 实例化
        assert page.login('admin', 'msjy123') is True

    yield driver

    save_cookies(driver)
    driver.quit()

这样我们在test_admin.py中就可以使用admin_driver

from pages import *


def test_new_deal(admin_driver):
    """已经登录成功状态"""
    page = AdminIndexPage(admin_driver)
    page = page.to_deal()  # 跳转到贷款管理
    page = page.new_deal()  # 跳转到新增贷款
    # data表示输入的数据
    data = {
        'name': '借款1亿买别墅',
        'shor_name': '买别墅',
        'username': 'beifan',
        'cate': '|--房产抵押标',
        'upload': r'D:\pythonProject2\code.png',
        'type': '个人消费',
        'contract': '等额本息合同范本【担保】',
        'tcontract': '付息还本合同范本【普通】',
        'amount': '100000000',
        'rate': '5',
        'enddate': '30',
        'start_time': '2023-12-25 18:02:02'
    }
    msg = page.submit(data)
    assert msg == '添加成功'

也可以在test_admin.py中继续添加测试用例,测试不同的输入数据和结果。这里不再举例。最后在终端运行可以看到测试通过,也可以创建main.py进行运行

import pytest

if __name__ == '__main__':
    pytest.main()

写到这里POM封装就基本完成了,整理下项目目录,如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/275494.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode 763 划分字母区间

题意理解: 要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 输入&#xff…

大数据与人工智能|信息技术产业架构、行业发展与前沿技术(第2节)

内容链接:信息技术产业架构、行业发展与前沿技术(大数据与人工智能系列课程 第2节) 声明:学习使用,侵权必删! 主要内容:1. 从算盘到量子计算机,介绍了半导体行业的发展历程和技术原…

二分查找——OJ题(一)

📘北尘_:个人主页 🌎个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上,不忘来时的初心 文章目录 一、二分查找1、题目讲解2、算法原理3、代码实现 二、在排序数组中查找元素的第一个和最后一个…

【本地缓存篇】如何实现本地缓存?

如何实现本地缓存? ✔️典型解析✔️数据结构✔️线程安全✔️对象上限✔️清除策略✔️过期时间 ✔️扩展知识仓基于Caffeine实现本地缓存 ✔️典型解析 所谓本地缓存,就是和应用服务器在一起的缓存工具,将需要缓存的数据放到本地缓存中,可…

【轻松入门】OpenCV4.8 + QT5.x开发环境搭建

引言 大家好,今天给大家分享一下最新版本OpenCV4.8 QT5 如何一起配置,完成环境搭建的。 下载OpenCV4.8并解压缩 软件版本支持 CMake3.13 或者以上版本 https://cmake.org/ VS2017专业版或者以上版本 QT5.15.2 OpenCV4.8源码包 https://github.com/op…

常用的 linux 命令

常用的 linux 命令 1.从其他机器拷贝文件夹2.查看哪个程序在用特定端口3.实时监控日志文件内容4.查看指定用户拥有的进程5.查看磁盘空间使用情况6.文件搜索which(whereis) 显示系统命令所在目录find 查找任何文件或目录1) 根据文件名称查找2)…

【Linux驱动】Linux中断(一)—— 设备树中断节点

裸机使用中断需要通过寄存器手动配置,但有了 Linux 系统后,Linux内核提供了完善的中断框架,我们只需要申请中断,然后注册中断服务函数即可。 一、设备树中断属性 既然驱动中要注册中断服务函数,我们首先需要知道三个点…

实战 | 使用OpenCV快速去除文档中的表格线条(步骤 + 源码)

导 读 本文主要介绍如何使用OpenCV快速去除文档中的表格线条,并给详细步骤和代码。 背景介绍 测试图如下,目标是去除下面三张图中的表格线条,方便后续图像处理。 实现步骤 下面演示详细步骤,以图1为例: 【1】获取二值图像:加载图像、转为灰度图、OTSU二值化 i…

记录 | ubuntu源码编译python3.7.3(指定版本)

一、安装依赖包 sudo apt-get install -y make build-essential libssl-dev zlib1g-dev sudo apt-get install -y libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm sudo apt-get install -y libncurses5-dev libncursesw5-dev xz-utils tk-dev 二、从Python网…

mapboxgl 中给地图添加遮罩蒙版,并不遮罩其中一块区域

文章目录 概要效果预览技术思路技术细节小结 概要 本篇文章主要是给一整块地图添加遮罩层蒙版,但是不遮罩其中一个区域,以反向高亮地区内容。 效果预览 技术思路 这里要实现某个区域反显高亮,需要这个区域的边界json文件,与ech…

Flink1.17实战教程(第三篇:时间和窗口)

系列文章目录 Flink1.17实战教程(第一篇:概念、部署、架构) Flink1.17实战教程(第二篇:DataStream API) Flink1.17实战教程(第三篇:时间和窗口) Flink1.17实战教程&…

华锐三维云展平台创建VR文化宣传展厅,让文化传承变得更便捷和高效

随着科技的不断发展,虚拟现实(VR)技术已经逐渐走进人们的生活。通过华锐云展平台,可以通过拖、拉、拽,快速自由地创建一个VR文化宣传展厅,VR文化宣传展厅为人们提供了一个全新的、沉浸式的文化体验空间。在…

uniapp的分包使用记录

UniApp的分包是一种将应用代码划分为多个包的技术。分包的核心思想是将不同部分的代码划分为不同的包,按需加载,从而提高应用性能。使用UniApp的条件编译功能,开发人员可以根据需要将代码划分为多个包。每个包都包含一组页面和组件&#xff0…

在国内如何在Tiktok上买东西(在tiktok上付款)??

TikTok是一款由中国公司字节跳动(ByteDance)开发的社交媒体应用,于2016年9月正式上线。它在全球范围内迅速走红,特别受到年轻用户的喜爱。以下是关于TikTok的介绍以及其一些优势 支持的卡头有5561、531993 //点我办卡&#xff0c…

stm32H743编译器关于浮点类型强制转换传参的bug

局部函数,正常传参 当测试函数作为局部函数和main函数写在同一个文件中时,参数可以正常传递。函数参数和形参都为3.14 float value 0.0; void float_test(float _v) {value _v; }int main(void) {float_test(3.14f);while(1); } keil仿真截图&#…

关于MySQL、分布式系统、SpringCloud面试题

前言 之前为了准备面试,收集整理了一些面试题。 本篇文章更新时间2023年12月27日。 最新的内容可以看我的原文:https://www.yuque.com/wfzx/ninzck/cbf0cxkrr6s1kniv MySQL 索引 说一下有哪些锁? 行锁有哪些? 性能优化 分库分表…

Java生态系统的进化:从JDK 1.0到今天

目录 前言 JDK 1.0:开启Java时代 JDK 1.1:Swing和内部类 JDK 1.2:Collections框架和JIT编译器 JDK 1.5:引入泛型和枚举 JDK 1.8:Lambda表达式和流 JDK 11以后:模块化和新特性 未来展望 总结 作者简…

3D换肤在服装行业的应用

在线工具推荐: 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 通过采用高质量的 3D 模型,企业可以提供更加身临其境的体…

cpp_07_类型转换构造_析构函数_深拷贝_静态成员

1 类型转换构造函数 1.1 why? 基本类型之间的转换,编译器内置转换规则:int -> double 类类型之间的转换,编译器不知道转换规则,需要用户提供:Cat -> Dog // consconv_why.cpp 为什么需要自定义转换 #includ…

ARM CCA机密计算软件架构之RMI领域管理接口与RSI领域服务接口

领域管理接口 领域管理接口(RMI)是RMM与正常世界主机之间的接口。 RMI允许正常世界虚拟机监视器向RMM发出指令,以管理领域。 RMI使用来自主机虚拟机监视器的SMC调用,请求RMM的管理控制。 RMI使得对领域管理的控制成为可能&…