『pyqt5 从0基础开始项目实战』09.本地数据配置文件的保存与读取之txt类型(保姆级图文)

news2024/10/5 17:21:44

目录

    • 导包和框架代码
    • 绑定按钮点击事件
    • 在dialog中编写代理配置弹窗UI和功能
    • 实现代理配置弹窗UI方法
    • 完整代码
      • main.py
      • threads.py
      • dialog.py
    • 总结


欢迎关注 『pyqt5 从0基础开始项目实战』 专栏,持续更新中
欢迎关注 『pyqt5 从0基础开始项目实战』 专栏,持续更新中

本文提到的IP代理只是一个UI起名,没有任何实际功能!!!

导包和框架代码

请查阅上文获取源码,此处只列举第三方库

import os
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QLineEdit, QTableWidget, QTableWidgetItem, QLabel
from PyQt5.QtWidgets import QMessageBox, QMenu

绑定按钮点击事件

    def init_footer(self):
        btn_proxy = QPushButton("代理IP")
        btn_proxy.clicked.connect(self.event_proxy_click)
        footer_layout.addWidget(btn_proxy)

在dialog中编写代理配置弹窗UI和功能

class ProxyDialog(QDialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("配置代理IP")
        self.resize(500, 400)
        layout = QVBoxLayout()

        # 输入框
        text_edit = QTextEdit()
        text_edit.setPlaceholderText("可用换行来设置多个代理IP,每个代理IP设置格式为:31.40.225.250:3128")

        # 读取本地文件
        file_path = os.path.join("db", "proxy.txt")
        all_proxy = ""#存放读取得到的配置文件
        if os.path.exists(file_path):#如果本地已经有配置文件
            with open(os.path.join("db", "proxy.txt"), mode='r', encoding='utf-8') as f:
                all_proxy = f.read()
        text_edit.setText(all_proxy)

        self.text_edit = text_edit
        layout.addWidget(text_edit)

        footer_config = QHBoxLayout()

        btn_save = QPushButton("重置")
        btn_save.clicked.connect(self.event_save_click)
        footer_config.addWidget(btn_save, 0, Qt.AlignRight)

        layout.addLayout(footer_config)

        self.setLayout(layout)

    def event_save_click(self):
        # PROXY.write()
        text = self.text_edit.toPlainText()

        # 写入到代理文件中
        with open(os.path.join("db", "proxy.txt"), mode='w', encoding='utf-8') as f:
            f.write(text)
        self.close()


实现代理配置弹窗UI方法

    # 点击代理
    def event_proxy_click(self):
        from utils.dialog import ProxyDialog

        dialog = ProxyDialog()
        dialog.setWindowModality(Qt.ApplicationModal)
        dialog.exec_()

在这里插入图片描述


完整代码

main.py

import os
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QLineEdit, QTableWidget, QTableWidgetItem, QLabel
from PyQt5.QtWidgets import QMessageBox, QMenu
# from utils.dialog import LogDialog

#拿到在另外一台计算机上执行程序代码所存储的文件路径。
BASE_DIR = os.path.dirname(os.path.realpath(sys.argv[0]))

STATUS_MAPPING = {
    0: "初始化中",
    1: "待执行",
    2: "正在执行",
    3: "完成并提醒",
    10: "异常并停止",
    11: "初始化失败",
}



class MainWindow(QWidget):
    def __init__(self):
        # 用super 继承父类的初始化
        super().__init__()

        self.txt_asin=None

        # 设置窗口的窗体标题
        self.setWindowTitle('发现你走远了的xx系统')

        # 设置窗体的尺寸
        self.resize(1228, 450)

        # 设置窗体位置
        # 获取整个窗口部分的宽高和左上角坐标信息,返回值是一个QRect类型,(x,y width,height)
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()  # 得到屏幕中间的位置信息
        qr.moveCenter(cp)  # 让我们的窗体移动到屏幕中间

        # 创建窗口总布局
        layout = QVBoxLayout()

        # 讲调用方法生成的顶部菜单布局添加到总布局
        layout.addLayout(self.init_header())
        layout.addLayout(self.init_form())
        layout.addLayout(self.init_table())
        layout.addLayout(self.init_footer())

        # 给窗体设置元素的排列方式
        self.setLayout(layout)

    def init_header(self):
        # 1.顶部菜单布局
        header_layout = QHBoxLayout()  # 创建顶部菜单布局
        # 1.1 放入按钮
        btn_start = QPushButton("开始")  # 新建一个开始按钮
        header_layout.addWidget(btn_start)  # 将开始按钮添加到顶部菜单布局
        btn_stop = QPushButton("停止")  # 新建一个开始按钮
        header_layout.addWidget(btn_stop)  # 将开始按钮添加到顶部菜单布局
        # 1.2 加入弹簧
        header_layout.addStretch()

        return header_layout

    def init_form(self):
        # 2.添加内容布局
        form_layout = QHBoxLayout()  # 创建添加内容布局

        # 2.1 输入框
        txt_asin = QLineEdit()  # 新建一个输入框对象
        txt_asin.setText("B07YN82X3B=100")  # 设置默认的form数据
        txt_asin.setPlaceholderText("请输入商品ID和价格,例如:B0818JJQQ8=88")  # 设置灰色的提示信息
        self.txt_asin=txt_asin
        form_layout.addWidget(txt_asin)  # 将输入框加入到布局中

        # 2.2 添加按钮
        btn_add = QPushButton("添加")  # 新建一个添加按钮
        btn_add.clicked.connect(self.event_add_click)#新增一个点击事件
        form_layout.addWidget(btn_add)  # 将添加按钮添加到form布局

        return form_layout

    def init_table(self):
        # 3.表格数据展示布局
        table_layout = QHBoxLayout()
        # 3.1 创建表格
        self.table_widget=table_widget = QTableWidget(0, 8)  # 新建一个0行8列的表格
        # # 修改表格索引名
        # item=QTableWidgetItem()
        # item.setText("标题0")
        # table_widget.setHorizontalHeaderItem(0,item)
        # table_widget.setColumnWidth(0,150)#设置水平单元格0号位置的宽度 150
        #
        # item2=QTableWidgetItem()
        # item2.setText("网址1")
        # table_widget.setHorizontalHeaderItem(1,item2)
        # table_widget.setColumnWidth(1,400)#设置水平单元格1号位置的宽度 400
        #
        # item3=QTableWidgetItem()
        # item3.setText("行索引0")
        # table_widget.setVerticalHeaderItem(0,item3)
        table_header = [
            {"field": "asin", "text": "ASIN", 'width': 120},
            {"field": "title", "text": "标题", 'width': 150},
            {"field": "url", "text": "URL", 'width': 400},
            {"field": "price", "text": "底价", 'width': 100},
            {"field": "success", "text": "成功次数", 'width': 100},
            {"field": "error", "text": "503次数", 'width': 100},
            {"field": "status", "text": "状态", 'width': 100},
            {"field": "frequency", "text": "频率(N秒/次)", 'width': 100},
        ]
        for idx, info in enumerate(table_header):
            item = QTableWidgetItem()
            item.setText(info['text'])
            table_widget.setHorizontalHeaderItem(idx, item)
            table_widget.setColumnWidth(idx, info['width'])

        # 3.2 初始化表格数据
        # 读取数据文件
        import json
        file_path = os.path.join(BASE_DIR, "db", "db.json")
        with open(file_path, mode='r', encoding='utf-8') as f:
            data = f.read()
        data_list = json.loads(data)#读取得到了json数据


        current_row_count = table_widget.rowCount()  # 当前表格有多少行
        for row_list in data_list:#每有一行json数据,我们就需要遍历一轮增加一行数据
            table_widget.insertRow(current_row_count)#增加一行
            # print(row_list)  # ['B08166SLDF', 'AMD er', 'https://www.amazon.', 300.0, 0, 166, 1, 5]
            # 把row_list写入这一行
            for i, ele in enumerate(row_list):#enumerate中 i表示索引id,ele表示数据值
                # 一个多目运算 如果i==6(此时是状态的数据) 如果STATUS_MAPPING中能够找到ele的索引,那么我们把ele设置成STATUS_MAPPING中的内容
                ele = STATUS_MAPPING[ele] if i == 6 else ele

                cell = QTableWidgetItem(str(ele))#注意我们的数据格式转为str,比如说状态的数据可能本身是int类型
                if i in [0, 4, 5, 6]:
                    # 不可修改
                    cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)#指定索引单元格不能选中和修改
                table_widget.setItem(current_row_count, i, cell)#写入数据到指定单元格

            current_row_count += 1 #行数+1,这样下次遍历行时会在新的行下面新增一行
            # 如果不新增行其实也可以用  current_row_count=table_widget.rowCount()替代


        table_layout.addWidget(table_widget)  # 把表格添加到表格布局中
        return table_layout

    def init_footer(self):
        # 4.底部菜单
        footer_layout = QHBoxLayout()

        label_status = QLabel("未检测", self)
        footer_layout.addWidget(label_status)

        footer_layout.addStretch()  # 添加弹簧,更加美观

        btn_reset = QPushButton("重新初始化")
        btn_reset.clicked.connect(self.event_reset_click)#新增一个点击事件
        footer_layout.addWidget(btn_reset)

        btn_recheck = QPushButton("重新检测")
        footer_layout.addWidget(btn_recheck)

        btn_reset_count = QPushButton("次数清零")
        btn_reset_count.clicked.connect(self.event_reset_count_click)
        footer_layout.addWidget(btn_reset_count)

        btn_delete = QPushButton("删除检测项")
        btn_delete.clicked.connect(self.event_delete_click)
        footer_layout.addWidget(btn_delete)

        btn_alert = QPushButton("SMTP报警配置")
        btn_alert.clicked.connect(self.event_alert_click)
        footer_layout.addWidget(btn_alert)

        btn_proxy = QPushButton("代理IP")
        btn_proxy.clicked.connect(self.event_proxy_click)
        footer_layout.addWidget(btn_proxy)

        return footer_layout

    # 添加数据按钮事件
    def event_add_click(self):
        # 1.获取输入框中的内容
        text = self.txt_asin.text()
        text = text.strip()#去掉空格
        if not text:#如果输入的是空格空字符
            QMessageBox.warning(self, "错误", "商品的ASIN输入错误")
            return
        # B07YN82X3B=100
        asin, price = text.split("=")#把数据分为id 和 价格
        price = float(price)#价格转为浮点型
        print(asin, price)

        # 2.加入到表格中(型号、底价)
        new_row_list = [asin, "", "", price, 0, 0, 0, 5]

        # 写入表格,具体操作和之前初始化表格一样
        current_row_count = self.table_widget.rowCount() # 当前表格有多少行
        self.table_widget.insertRow(current_row_count)
        for i, ele in enumerate(new_row_list):
            ele = STATUS_MAPPING[ele] if i == 6 else ele
            cell = QTableWidgetItem(str(ele))
            if i in [0, 4, 5, 6]:
                # 不可修改
                cell.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.table_widget.setItem(current_row_count, i, cell)

        # 3.发送请求自动获取标题
        # 注意:不能再主线程中做数据获取的事,创建一个线程去做数据获取,爬取到数据再更新到窗体应用(信号)。
        from utils.threads import NewTaskThread #导入我们写的线程py文件中的类

        thread = NewTaskThread(current_row_count, asin, self)#初始化线程中类的对象,传入了 表格总共的行数{current_row_count} 和 当前在表格的第几行{asin} 以及自身这个对象 {self}
        thread.success.connect(self.init_task_success_callback)#添加数据成功事件成功的回调,马上开始初始化数据
        thread.error.connect(self.init_task_error_callback)#事件失败的回调,弹框提示错误
        thread.start()#线程开始运行

        pass

    # 添加数据成功事件成功的回调,得到数据后马上开始初始化数据
    def init_task_success_callback(self, row_index, asin, title, url):
        print("成功",row_index, asin, title, url)
        # 更新标题
        cell_title = QTableWidgetItem(title)
        self.table_widget.setItem(row_index, 1, cell_title)

        # 更新URL
        cell_url = QTableWidgetItem(url)
        self.table_widget.setItem(row_index, 2, cell_url)

        # 更新状态 成功后未待执行
        cell_status = QTableWidgetItem(STATUS_MAPPING[1])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.table_widget.setItem(row_index, 6, cell_status)

        # 输入框清空
        self.txt_asin.clear()

    # 添加数据失败事件成功的回调,得到数据后马上开始初始化数据
    def init_task_error_callback(self, row_index, asin, title, url):
        print("错误",row_index, asin, title, url)
        # 更新标题
        cell_title = QTableWidgetItem(title)
        self.table_widget.setItem(row_index, 1, cell_title)

        # 更新URL
        cell_url = QTableWidgetItem(url)
        self.table_widget.setItem(row_index, 2, cell_url)

        # 更新状态为 初始化失败
        cell_status = QTableWidgetItem(STATUS_MAPPING[11])
        cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.table_widget.setItem(row_index, 6, cell_status)

    def event_reset_click(self):
        # 1.获取已经选中的行
        row_list = self.table_widget.selectionModel().selectedRows()
        if not row_list:#如果没有选中行,提示错误
            QMessageBox.warning(self, "错误", "请选择要重新初始化的行")
            return
        # 2.获取每一行进行重新初始化
        for row_object in row_list:
            index = row_object.row()
            print("选中的行:", index)
            # 获取信号
            asin = self.table_widget.item(index, 0).text().strip()#得到了商品id

            # 状态重新初始化
            cell_status = QTableWidgetItem(STATUS_MAPPING[0])#把状态设置成初始化中
            print("选中的行:", index,"---",STATUS_MAPPING[0]) # 选中的行: 1 --- 初始化中
            cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.table_widget.setItem(index, 6, cell_status)

            # 创建线程去进行初始化动作
            from utils.threads import NewTaskThread
            thread = NewTaskThread(index, asin, self)#传入 选中的行/商品id/窗体对象
            thread.success.connect(self.init_task_success_callback)
            thread.error.connect(self.init_task_error_callback)
            thread.start()

    # 点击数量清零
    def event_reset_count_click(self):
        # 1.获取已经选中的行
        row_list = self.table_widget.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self, "错误", "请选择要操作的行")
            return
        # 2.获取每一行进行重新初始化
        for row_object in row_list:
            index = row_object.row()
            # print("选中的行:", index)
            # # 获取信号
            # asin = self.table_widget.item(index, 0).text().strip()

            # 状态重新初始化
            cell_status = QTableWidgetItem(str(0))#得到一个字符串格式的 0
            cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.table_widget.setItem(index, 4, cell_status)#设置成功次数清零

            cell_status = QTableWidgetItem(str(0))#得到一个字符串格式的 0
            cell_status.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.table_widget.setItem(index, 5, cell_status)#设置503次数清零

    # 点击删除
    def event_delete_click(self):
        # 1.获取已经选中的行
        row_list = self.table_widget.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self, "错误", "请选择要操作的行")
            return

        # 2.翻转,先删除掉行号大的数据,然后在删除行号小的数据,避免删除行号小的数据变动行号大的数据。
        row_list.reverse()
        print (row_list)

        # 3.删除
        for row_object in row_list:
            index = row_object.row()
            print("当前删除的行是:",index)
            self.table_widget.removeRow(index)

    # 点击邮件配置
    def event_alert_click(self):
        # 创建弹窗并在弹窗中进行设置

        from utils.dialog import AlertDialog

        dialog = AlertDialog()
        dialog.setWindowModality(Qt.ApplicationModal)
        dialog.exec_()

    # 点击代理
    def event_proxy_click(self):
        from utils.dialog import ProxyDialog

        dialog = ProxyDialog()
        dialog.setWindowModality(Qt.ApplicationModal)
        dialog.exec_()



if __name__ == '__main__':
    app = QApplication(sys.argv)  # 实例化一个Application应用,所有的窗口均在其下运行

    window = MainWindow()  # 实例化窗口对象
    window.show()  # 窗口展示

    sys.exit(app.exec_())
    # app.exec_()运行主循环,并在退出时返回状态代码。
    # sys.exit(n)退出您的应用程序并返回n到父进程(通常是您的shell)

threads.py

from PyQt5.QtCore import QThread, pyqtSignal


class NewTaskThread(QThread):
    # 信号,触发信号,更新窗体中的数据
    success = pyqtSignal(int, str, str, str)  # 成功后向ui对象发送数据的声明
    error = pyqtSignal(int, str, str, str)  # 失败后向ui对象发送数据的声明

    def __init__(self, row_index, asin, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.row_index = row_index  # 行数
        self.asin = asin  # 商品id

    def run(self):
        """ 具体线程应该做的事"""
        try:
            title = "good_title_{}".format(self.asin)  # good_title_B07YN82X3B
            url = "https://blog.csdn.net/u011027547/{}".format(self.asin)

            # 获取到title和url,将这个信息填写到 表格上 & 写入文件中。
            self.success.emit(self.row_index, self.asin, title, url)
        except Exception as e:
            print(e)
            title = "监控项 {} 添加失败。".format(self.asin)
            self.error.emit(self.row_index, self.asin, title, str(e))

dialog.py

import os
import json

from PyQt5.QtWidgets import QVBoxLayout, QDialog, QPushButton, QLabel, QLineEdit, QMessageBox, QTextEdit, QHBoxLayout

from PyQt5.QtCore import Qt


class AlertDialog(QDialog):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.field_dict = {}#字段字典
        self.init_ui()#初始化ui

    def init_ui(self):
        self.setWindowTitle("报警邮件配置")
        self.resize(300, 270)

        layout = QVBoxLayout()

        form_data_list = [
            {"title": "SMTP服务器:", "filed": "smtp"},
            {"title": "发件箱:", "filed": "from"},
            {"title": "密码:", "filed": "pwd"},
            {"title": "收件人(多个用逗号分隔):", "filed": "to"},
        ]

        # 读取文件中的配置
        old_alert_dict = {}#存放读取本地配置文件中的老的配置数据
        alert_file_path = os.path.join("db", 'alert.json')#在db文件夹下找配置文件 alert.json
        if os.path.exists(alert_file_path):#判断本地是否有配置文件
            file_object = open(os.path.join("db", 'alert.json'), mode='r', encoding='utf-8')
            old_alert_dict = json.load(file_object)
            file_object.close()

        #将form_data_list中的ui界面信息编写成ui界面
        for item in form_data_list:
            lbl = QLabel()#新建一个标签
            lbl.setText(item['title'])
            layout.addWidget(lbl)

            txt = QLineEdit()#新建一个输入框
            layout.addWidget(txt)

            filed = item['filed']#写入输入框字段值
            if old_alert_dict and filed in old_alert_dict:#如果当前的字段恰好在配置文件中也有保存
                txt.setText(old_alert_dict[filed])#设置配置文件的字段值
            self.field_dict[item['filed']] = txt#设置全局变量的字段值

        btn_save = QPushButton("保存")
        btn_save.clicked.connect(self.event_save_click)#绑定保存设置鼠标事件
        layout.addWidget(btn_save, 0, Qt.AlignRight)#

        layout.addStretch(1)#添加一个弹簧
        self.setLayout(layout)

    def event_save_click(self):
        data_dict = {}
        for key, filed in self.field_dict.items():
            value = filed.text().strip()
            if not value:
                QMessageBox.warning(self, "错误", "邮件报警项不能为空")
                return
            data_dict[key] = value

        # {'smtp': '123123', 'from': '123123', 'pwd': '123123', 'to': '123123123'}
        print(data_dict)#打印保存的配置
        # 写入到本地的配置文件
        file_object = open(os.path.join("db", 'alert.json'), mode='w', encoding='utf-8')
        json.dump(data_dict, file_object)
        file_object.close()

        # 关闭对话框
        self.close()


class ProxyDialog(QDialog):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle("配置代理IP")
        self.resize(500, 400)
        layout = QVBoxLayout()

        # 输入框
        text_edit = QTextEdit()
        text_edit.setPlaceholderText("可用换行来设置多个代理IP,每个代理IP设置格式为:31.40.225.250:3128")

        # 读取本地文件
        file_path = os.path.join("db", "proxy.txt")
        all_proxy = ""#存放读取得到的配置文件
        if os.path.exists(file_path):#如果本地已经有配置文件
            with open(os.path.join("db", "proxy.txt"), mode='r', encoding='utf-8') as f:
                all_proxy = f.read()
        text_edit.setText(all_proxy)

        self.text_edit = text_edit
        layout.addWidget(text_edit)

        footer_config = QHBoxLayout()

        btn_save = QPushButton("重置")
        btn_save.clicked.connect(self.event_save_click)
        footer_config.addWidget(btn_save, 0, Qt.AlignRight)

        layout.addLayout(footer_config)

        self.setLayout(layout)

    def event_save_click(self):
        # PROXY.write()
        text = self.text_edit.toPlainText()

        # 写入到代理文件中
        with open(os.path.join("db", "proxy.txt"), mode='w', encoding='utf-8') as f:
            f.write(text)
        self.close()

总结

大家喜欢的话,给个👍,点个关注!给大家分享更多计算机专业学生的求学之路!

版权声明:

发现你走远了@mzh原创作品,转载必须标注原文链接

Copyright 2023 mzh

Crated:2023-3-1

欢迎关注 『pyqt5 从0基础开始项目实战』 专栏,持续更新中
欢迎关注 『pyqt5 从0基础开始项目实战』 专栏,持续更新中
『未完待续』


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jenkins 实现自动化部署

安装 windows下安装:https://blog.csdn.net/u014641168/article/details/130286547 linux下安装:https://blog.csdn.net/u014641168/article/details/130282439 Jenkins支持JDK1.8对应版本说明:https://blog.csdn.net/u014641168/article/…

STM32 学习笔记_3 程序编写基础;arm 内核架构

程序编写基础 Keil 编辑器设置 抛开 tab 和空格哪个好看不谈,不同编译器设置格式不同,空格比较保险。 用户关键字:打出来的时候会高亮。 代码提示:(symbols after 是几个字符后开始提示关键字的意思) 以上…

6、索引的数据结构

3.3 常见的索引概念 索引按照物理实现方式,索引可以分为 2 种:聚簇和非聚簇索引 1、聚簇索引 5、索引的代价 空间上的代价 每建立一个索引都要为它建立一棵B树,每一棵B树的每一个节点都是一个数据页,一个页默认会占用 16KB 的存…

jsp827+java心理测评管理系统dzkfB4程序j2EE+mysql

现代社会的大学生是21世纪的主人,他们不仅需要具有为社会做贡献的真才实学,更需要健康的心理。大学生的身心健康,人格健全和谐发展是他们学习的需要,是社会对未来参与者素质的要求。 1.系统登录:系统登录是…

thinkphp:插入数据到数组

一、插入数据到数组首位(array_unshift),这里全用空值进行插入 $array [a,b,c]; //在首位加入一个空值 array_unshift($array ,); //将数组输出 print_r($array); 输出结果: 二、插入数据到数组末尾(array_push&…

docker安装kafka,并集成springboot进行测试

大家好,今天我们开始学习kafka中间件,今天我们改变一下策略,不刷视频学习,改为实践学习,在网上找一些案例功能去做,来达到学习实践的目的。 首先,是安装相关组件。 1. docker安装安装 1.1 yu…

深度学习中的各种不变性

不变性 平移不变性(Translation Invariance)旋转不变性(Ratation Invariance)尺度不变性(Size Invariance)光照不变性(Illumination Invariance)仿射不变性(Affine Invar…

PS学习记录-图像【像素】与【分辨率】的说明

我们经常能在图片的属性中看到 1920像素x1080像素 (老司机在视频文件中也经常看到~) 这就是我们常说的图片分辨率,以下是我学习整理的关于像素、分辨率的资料。 注意: 图像分辨率是针对【位图】的,图片分辨率决定了…

记录-JS简单实现购物车图片局部放大预览效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 一、实现效果 二、代码实现 代码不多&#xff0c;先看一下 HTML 里面结构很简单&#xff0c;初始化 MagnifyingGlass 对象来关联一个 IMG 标签来实现放大。 <!DOCTYPE html> <html> <h…

做一个网站需要多少个技术人员?

作为互联网从业者&#xff0c;这么多年来经常会碰到一个灵魂拷问&#xff0c;那就是“为什么一个网站需要那么多技术人员&#xff1f;”&#xff0c;尤其是提问者如果再追问一下“听说几个相关专业的学生一个课程的作业就是开发一个网站或者app&#xff0c;那为什么现在主流的网…

C++ | 认识标准库string和vector

本文概要 本篇文章主要介绍C的标准库类型string和vector&#xff0c;文中描述和代码示例很详细&#xff0c;看完即可掌握&#xff0c;感兴趣的小伙伴快来一起学习吧。 &#x1f31f;&#x1f31f;&#x1f31f;个人简介 &#x1f31f;&#x1f31f;&#x1f31f; ☀️大家好&a…

stable diffusion安装从0到1总结:包括遇到的坑和步骤

注&#xff1a;最低电脑配置&#xff1a;8G Vram16G RAM30G磁盘空间以上&#xff0c;20系列显卡及以上&#xff0c;windows>linux>macos。 文件可以不放在系统盘。举个例子&#xff1a;安装在D盘&#xff0c;在D盘创建一个StableDiffusion文件夹。下载下面文件: 1.下载…

为什么实现 API 最佳实践需要重新考虑安全性

随着应用程序编程接口 (API) 的使用与日俱增&#xff0c;实现和维护有效安全性的挑战从未像现在这样大。 由于缺乏管理 API 的单一标准&#xff0c;这意味着团队不能仅依靠工具来解决安全问题&#xff0c;因此这一挑战变得更加严峻。没有任何一种产品可以解决 API 环境的每种…

pg编码相关问题梳理

Lightdb/PG 编码相关问题梳理 之前在通过SQL文件导入数据时&#xff0c;报&#xff1a;ERROR: invalid byte sequence for encoding "EUC_CN"错误。然后就梳理了一下编码相关问题&#xff0c;这边记录一下。涉及到如下两种类型的报错&#xff1a; ERROR: invalid b…

电脑如何还原系统?这样做可以快速解决!

案例&#xff1a;我的电脑系统出问题了&#xff0c;怎么还原&#xff1f; 【我的电脑用了好几年了&#xff0c;最近它的系统出现了一些问题&#xff0c;我想还原电脑系统。有没有知道电脑系统如何还原&#xff1f;蹲一个简单的解决方法&#xff01;】 随着电脑使用时间的增加…

面试华为,花了2个月才上岸,真的难呀····

花2个月时间面试一家公司&#xff0c;你们觉得值吗&#xff1f; 背景介绍 美本计算机专业&#xff0c;代码能力一般&#xff0c;之前有过两段实习以及一个学校项目经历。第一份实习是大二暑期在深圳的一家互联网公司做前端开发&#xff0c;第二份实习由于大三暑假回国的时间比…

Linux中的git命令行

Linux中的git命令行 目录 Linux中的git命令行引入1、Linux下的git工具起源2、gitee的使用.gitignore.git 3、git三板斧3.1 git add3.2 git commit3.3 git push 4、git操作4.1 查看提交日志4.2 查看状态4.3 远端同步4.4 删除文件4.5 修改文件名 引入 当多个开发者同时参与同一个…

(一)Jhipster的基本介绍及入门安装

目录 1、为什么要使用 Jhipster &#xff1f; 2、安装配置 3、安装Jhipster 4、基本使用 5、介绍一下JDL Studio 6、启动 Jhipster 搭建项目 1、为什么要使用 Jhipster &#xff1f; JHipster是一个开发平台&#xff0c;可以快速生成、开发和部署现代Web应用程序和微服务…

分享几个自动化测试的练手项目

学习自动化测试最难的是没有合适的项目练习。 测试本身既要讲究科学&#xff0c;又有艺术成分&#xff0c;单单学几个 api 的调用很难应付工作中具体的问题。 你得知道什么场景下需要添加显性等待&#xff0c;什么时候元素定位需要写得更加优雅&#xff0c;为什么需要断言这个…

CANoe使用记录(四):CANoe Graphics图形窗口

目录 1、概述 2、Graphics图形窗口 2.1、打开测量窗口&#xff08;回放Log&#xff09; 2.2、输出Log 2.3、添加解析DBC文件 2.4、窗口排列 2.5、添加Graphics窗口 2.6、 信号栏选择 2.7、添加信号 2.8、波形样式 2.9、单Y轴测量尺 2.10、多Y轴测量尺 2.11、数据隐…