QT pyside2 线程嵌套子线程 实现开始运行和停止运行

在这里插入图片描述


文章目录

  • 前言
    • 为什么要使用多线程
  • 一、单个线程实现按钮方法的执行
  • 二、线程嵌套多个子线程实现按钮方法的执行
  • 三、QT GUI常用代码
    • 3.1 多线程取出队列任务循环执行,无停止
    • 3.2 将某个方法放在线程中执行
    • 3.3 QT pyside2 tableWidget 清除日志
    • 3.4 退出整个GUI程序(杀死进程)
    • 3.5 子线程执行和子线程停止
    • 3.6 抛出异常来停止子线程的执行
    • 3.7 使用多个子线程往同一个queue队列对象中存储数据
  • 四、资源推荐
  • 总结


前言

为什么要使用多线程

QT是一种流行的跨平台应用程序开发框架,而PySide2是QT的一个Python绑定库。在QT和PySide2中使用多线程的主要原因是为了提高应用程序的性能和响应能力。

使用多线程可以将耗时的任务分配给不同的线程,在后台并行执行,从而避免阻塞主线程,保持应用程序的流畅性和响应性。当应用程序需要进行复杂的计算、网络请求、文件读写或其他需要较长时间的操作时,使用多线程可以防止这些操作对用户界面的阻塞,使用户能够继续与应用程序进行交互。

需要注意的是在使用多线程时要注意线程安全性和数据同步的问题。多个线程同时访问共享的数据可能导致竞态条件和数据不一致的问题,因此需要采取适当的同步机制,如锁、信号量或其他线程间通信机制,来保证数据的一致性和正确性。

总而言之,使用多线程可以提高QT PySide2应用程序的性能、响应能力和并发性,但需要注意线程安全和数据同步的问题。


提示:以下是本篇文章正文内容,下面案例可供参考

一、单个线程实现按钮方法的执行

实现效果:界面不会卡顿、可以继续与应用程序进行交互;
开始执行、停止执行思路

  • 当点击开始按钮时,分配一个线程a去循环执行按钮绑定的方法,如果变量_stop_event的值为True则一直执行方法;
  • 当点击停止按钮时,分配一个线程b去设置变量_stop_event的值为False,当线程a再次判断时则会跳出执行方法的循环;
  • 具体的逻辑得分析一下MyThread类的代码;
import sys
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import time
import threadingclass MyThread(threading.Thread):def __init__(self, qt):super().__init__()self.qtGui = qtself._stop_event = threading.Event()def stop(self):self._stop_event.set()def run(self):message_list = [f"第{k}次打印" for k in range(100)]log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}for message in message_list:if self._stop_event.is_set() is True:breaklog_content['five'] = messageself.qtGui.logger_show(log_content)time.sleep(0.1)log_content['five'] = '答题子线程已停止运行,请进行下一步操作'time.sleep(5)[self.qtGui.logger_show(log_content) for kk in range(1)]return Trueclass Gui(QWidget):def __init__(self):# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象QtFileObj = QFile("yk.ui")QtFileObj.open(QFile.ReadOnly)QtFileObj.close()self.ui = QUiLoader().load(QtFileObj)# 设置界面图标icon = QIcon("yk.ico")self.ui.setWindowIcon(icon)# 变量、对象定义self.index = 0self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True)  # 设置tableWidget组件的标题显示为Trueself.ui.startButton.clicked.connect(self.start_running)  # 绑定按钮的方法self.ui.stopButton.clicked.connect(self.stop_running)self.answer_thread = None  # 答题启动线程对象def start_running(self):# 插入内容logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '程序已经开始运行,请勿多次点击开始运行按钮'}[self.logger_show(logger_item) for kk in range(1)]# 创建线程对象self.answer_thread = MyThread(self)  # 传入qt对象# 启动线程self.answer_thread.start()def stop_running(self):# 停止线程self.answer_thread.stop()logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'}[self.logger_show(logger_item) for kk in range(1)]def logger_show(self, logger_item):self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))self.index += 1new_item_one = QTableWidgetItem(logger_item['one'])new_item_one.setTextAlignment(Qt.AlignCenter)new_item_two = QTableWidgetItem(logger_item['two'])new_item_two.setTextAlignment(Qt.AlignCenter)new_item_three = QTableWidgetItem(logger_item['three'])new_item_three.setTextAlignment(Qt.AlignCenter)new_item_four = QTableWidgetItem(logger_item['four'])new_item_four.setTextAlignment(Qt.AlignCenter)new_item_five = QTableWidgetItem(logger_item['five'])new_item_five.setTextAlignment(Qt.AlignCenter)self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)# 定位至最新行self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)# 刷新QApplication.processEvents()if __name__ == '__main__':app = QApplication(sys.argv)ykGuiObj = Gui()ykGuiObj.ui.show()sys.exit(app.exec_())

二、线程嵌套多个子线程实现按钮方法的执行

实现效果:界面不会卡顿、可以继续与应用程序进行交互,多个子线程同时执行或停止;
开始执行、停止执行思路

  • 当点击开始按钮时,分配一个线程a去开启5个子线程去执行打印日志的方法;
  • 当点击停止按钮时,分配一个线程b去设置变量_stop_event的值为True,当线程a再次判断时则会跳出执行方法的循环;
  • 具体的逻辑得分析一下MyThread类的代码;
  • 可分析运行效果图标红处,同时开了5个子线程去打印,并实现了停止子线程的执行;
import sys
import time
import random
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import threading
import concurrent.futures
from queue import Queueclass MyThread(threading.Thread):def __init__(self, qt):super().__init__()self.qtGui = qtself.executor = None  # 创建线程池self._stop_event = threading.Event()def stop(self):self._stop_event.set()def run(self):log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}message_queue = self.build_log_messages()thread_number = 5  # 线程数while message_queue.qsize():if self.executor is None or self.executor._shutdown:# 创建线程池self.executor = concurrent.futures.ThreadPoolExecutor()for kkk in range(thread_number):  # 开启4子线程执行打印方法self.executor.submit(self.qtGui.logger_show, message_queue.get(), random.randint(1, 15) * 0.1)time.sleep(0.2)self.executor.shutdown(wait=True)  # 等待任务执行完成if self._stop_event.is_set() is True:  # 当停止按钮被点击后则会进入这个跳出循环条件breaklog_content['five'] = '答题子线程已停止运行,请进行下一步操作'time.sleep(5)[self.qtGui.logger_show(log_content) for kk in range(1)]return Truedef build_log_messages(self):message_queue = Queue()grapheme_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']num = 0while num < 100:  # 存储需要打印的日志信息for grapheme in grapheme_list:num += 1message_queue.put({'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': f"线程{grapheme} {grapheme * 10} 打印数字:{num}"})return message_queueclass Gui(QWidget):def __init__(self):# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象QtFileObj = QFile("yk.ui")QtFileObj.open(QFile.ReadOnly)QtFileObj.close()self.ui = QUiLoader().load(QtFileObj)# 设置界面图标icon = QIcon("yk.ico")self.ui.setWindowIcon(icon)# 变量、对象定义self.index = 0self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True)  # 设置tableWidget组件的标题显示为Trueself.ui.startButton.clicked.connect(self.start_running)  # 绑定按钮的方法self.ui.stopButton.clicked.connect(self.stop_running)self.answer_thread = None  # 答题启动线程对象def start_running(self):# 插入内容logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '程序已经开始运行,请勿多次点击开始运行按钮'}[self.logger_show(logger_item) for kk in range(1)]# 创建线程对象self.answer_thread = MyThread(self)  # 传入qt对象# 启动线程self.answer_thread.start()def stop_running(self):# 停止线程self.answer_thread.stop()logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'}[self.logger_show(logger_item) for kk in range(1)]def logger_show(self, logger_item, sleep=0):time.sleep(sleep)self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))self.index += 1new_item_one = QTableWidgetItem(logger_item['one'])new_item_one.setTextAlignment(Qt.AlignCenter)new_item_two = QTableWidgetItem(logger_item['two'])new_item_two.setTextAlignment(Qt.AlignCenter)new_item_three = QTableWidgetItem(logger_item['three'])new_item_three.setTextAlignment(Qt.AlignCenter)new_item_four = QTableWidgetItem(logger_item['four'])new_item_four.setTextAlignment(Qt.AlignCenter)new_item_five = QTableWidgetItem(logger_item['five'])new_item_five.setTextAlignment(Qt.AlignCenter)self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)# 定位至最新行self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)# 刷新QApplication.processEvents()if __name__ == '__main__':app = QApplication(sys.argv)ykGuiObj = Gui()ykGuiObj.ui.show()sys.exit(app.exec_())

在这里插入图片描述

三、QT GUI常用代码

3.1 多线程取出队列任务循环执行,无停止

import time
import threading
from queue import Queuedef task_running(param1, param2):while True:task_info = task_queue.get()  # 取出任务time.sleep(1)print(task_info)task_queue.task_done()  # 全部完成则退出死循环def thread_controller(task_queue):thread_number = 3  # 线程数for j in range(thread_number):t = threading.Thread(target=task_running, args=(1, 2))t.daemon = True  # 设置线程daemon 主线程退出就直接让子线程跟随退出t.start()if __name__ == '__main__':task_queue = Queue()thread_controller(task_queue)

3.2 将某个方法放在线程中执行

# 调用示例
StartButton.clicked.connect(lambda: self.thread_it(self.function, param))def thread_it(self, func, *args):'''将函数打包进线程'''self.myThread = threading.Thread(target=func, args=args)self.myThread.setDaemon(True)self.myThread.start()

3.3 QT pyside2 tableWidget 清除日志

def clear_logger(self, route_name=None):for i in range(self.index):self.tableWidget .removeRow(0)  # 删除tableWidget组件的第0行数据# self.ui.tableWidget.clearContents()  # 清除/删除所有内容time.sleep(1)self.index = 0

3.4 退出整个GUI程序(杀死进程)

def exit_program(self, route_name):tmp = os.popen("tasklist").readlines()for tm in tmp:if 'python' not in tm:continuetime.sleep(0.5)pid = tm.strip().replace(' ', '').split('.exe')[-1].split('Console')[0]subprocess.Popen("taskkill /F /T /PID " + str(pid), shell=True)return True

3.5 子线程执行和子线程停止

import threadingclass MyThread(threading.Thread):def __init__(self, qt):super().__init__()self._stop_event = threading.Event()def stop(self):self._stop_event.set()def run(self):while True:print("开始运行")if self._stop_event.is_set() is True:  # 当停止按钮被点击后则会进入这个跳出循环条件break

3.6 抛出异常来停止子线程的执行

import ctypes
import threadingclass ThreadWithException(threading.Thread):def __init__(self, _tk, route_number=0):threading.Thread.__init__(self)self._tk = _tkself.route_number = route_numberdef run(self):passdef get_id(self):if hasattr(self, '_thread_id'):return self._thread_idfor id, thread in threading._active.items():if thread is self:return iddef raise_exception(self):thread_id = self.get_id()# 精髓就是这句话,给线程发过去一个exceptions,线程就那边响应完就停了response = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit))if response > 1:ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)

3.7 使用多个子线程往同一个queue队列对象中存储数据

逻辑解释

  • queue模块提供了线程安全的队列类Queue,可以安全地在多个线程之间进行数据存储和获取操作;
  • 在主进程中创建一个queue对象,调用方法开启多子线程时将queue对象作为参数传入;
  • 多个子线程可以通过queue对象进行数据存储,并且不会发生数据冲突的情况;
  • 主进程也可开启一个子线程去实时监听queue对象中是否有新的数据存入,如果有则进行处理;
  • 下面我分了两个源码文件进行演示,一个是gui.py主要写的是qt代码,另一个是data_save.py主要是进行数据存储的代码;
gui.py
import sys
import time
import random
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import threading
import concurrent.futures
from queue import Queue
from data_save import DataSaveclass MyThread(threading.Thread):def __init__(self, qt):super().__init__()self.qtGui = qtself.executor = None  # 创建线程池self._stop_event = threading.Event()def stop(self):self._stop_event.set()def run(self):task_queue = Queue()log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}message_queue = self.build_log_messages()thread_number = 5  # 线程数while message_queue.qsize():if self.executor is None or self.executor._shutdown:# 创建线程池self.executor = concurrent.futures.ThreadPoolExecutor()for kkk in range(thread_number):  # 开启5子线程执行打印方法data_sava_pipeline = DataSave()self.executor.submit(data_sava_pipeline.runs, self.qtGui, task_queue, message_queue.get())time.sleep(0.2)self.executor.shutdown(wait=True)  # 等待任务执行完成if self._stop_event.is_set() is True:  # 当停止按钮被点击后则会进入这个跳出循环条件breaklog_content['five'] = '答题子线程已停止运行,请进行下一步操作'print('task_queue', task_queue.qsize(), task_queue.queue)time.sleep(2)[self.qtGui.logger_show(log_content) for kk in range(1)]return Truedef build_log_messages(self):message_queue = Queue()grapheme_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']num = 0while num < 100:  # 存储需要打印的日志信息for grapheme in grapheme_list:num += 1message_queue.put({'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': f"线程{grapheme} {grapheme * 10} 打印数字:{num}"})return message_queueclass Gui(QWidget):def __init__(self):# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象QtFileObj = QFile("D:\MyStudy\MyProject\PythonProject\python_example\项目接单学习/18_PYQT\可视化编程\yk.ui")QtFileObj.open(QFile.ReadOnly)QtFileObj.close()self.ui = QUiLoader().load(QtFileObj)# 设置界面图标icon = QIcon("yk.ico")self.ui.setWindowIcon(icon)# 变量、对象定义self.index = 0self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True)  # 设置tableWidget组件的标题显示为Trueself.ui.startButton.clicked.connect(self.start_running)  # 绑定按钮的方法self.ui.stopButton.clicked.connect(self.stop_running)self.answer_thread = None  # 答题启动线程对象def start_running(self):# 插入内容logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '程序已经开始运行,请勿多次点击开始运行按钮'}[self.logger_show(logger_item) for kk in range(1)]# 创建线程对象self.answer_thread = MyThread(self)  # 传入qt对象# 启动线程self.answer_thread.start()def stop_running(self):# 停止线程self.answer_thread.stop()logger_item = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'}[self.logger_show(logger_item) for kk in range(1)]def logger_show(self, logger_item, sleep=0):time.sleep(sleep)self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))self.index += 1new_item_one = QTableWidgetItem(logger_item['one'])new_item_one.setTextAlignment(Qt.AlignCenter)new_item_two = QTableWidgetItem(logger_item['two'])new_item_two.setTextAlignment(Qt.AlignCenter)new_item_three = QTableWidgetItem(logger_item['three'])new_item_three.setTextAlignment(Qt.AlignCenter)new_item_four = QTableWidgetItem(logger_item['four'])new_item_four.setTextAlignment(Qt.AlignCenter)new_item_five = QTableWidgetItem(logger_item['five'])new_item_five.setTextAlignment(Qt.AlignCenter)self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)# 定位至最新行self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)# 刷新QApplication.processEvents()if __name__ == '__main__':app = QApplication(sys.argv)ykGuiObj = Gui()ykGuiObj.ui.show()sys.exit(app.exec_())
data_save.py
import time
import randomclass DataSave(object):def __init__(self):passdef data_save(self, items):message = items['message']items['queue'].put(message)items['qt'].logger_show(message)def runs(self, qt, queue, message):sleep = random.randint(2, 10) * 0.1time.sleep(sleep)print(f'message:{message} sleep:{sleep}\n', end='')items = {'qt': qt, 'queue': queue, 'message': message}self.data_save(items)

在这里插入图片描述

四、资源推荐

1、QT Pyside2 Designer 的基本使用:https://blog.csdn.net/EXIxiaozhou/article/details/131401574
2、入门文档请看白月黑羽:https://www.byhy.net/tut/py/gui/qt_01
3、入门视频请看白月黑羽:https://www.bilibili.com/video/BV19A411H7dS/
4、深入学习博主推荐-国内站点:https://gitee.com/feiyangqingyun
5、深入学习博主推荐-国际站点:https://github.com/feiyangqingyun
6、CSDN个人主页:https://blog.csdn.net/feiyangqingyun
7、官网:https://www.pythonguis.com/topics/pyside2/

总结

这篇博文的示例主要以实现功能为目的,我自己觉得有地方不太合适,欢迎大佬和小伙伴在评论处积极提出;

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# OpenCvSharp 基于直线检测的文本图像倾斜校正

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenCvSharp_基于直线检测的文…

02 MIT线性代数-矩阵消元 Elimination with matrices

一, 消元法 Method of Elimination 消元法是计算机软件求解线形方程组所用的最常见的方法。任何情况下&#xff0c;只要是矩阵A可逆&#xff0c;均可以通过消元法求得Axb的解 eg: 我们将矩阵左上角的1称之为“主元一”&#xff08;the first pivot&#xff09;&#xff0c;第…

SSRF漏洞

Server-Side Request Forgery:服务器端请求伪造 目标&#xff1a;网站的内部系统 形成的原因 攻击者构造形成由服务器端发起请求的译者安全漏洞。 由于服务端提供了从其他服务器应用获取数据的功能&#xff0c;且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内…

oracle执行计划中,同一条语句块,在不同情况下执行计划不一样问题。子查询,union 导致索引失效。

场景&#xff1a; 需要获取部分数据集(视图)的业务时间最大值&#xff0c;希望只通过一条语句获取多个的最大值。 则使用select (视图1业务时间最大值),(视图2业务时间最大值),(视图3业务时间最大值) from dual 程序执行过程中&#xff0c;发现语句执行较慢&#xff0c;则进行s…

delphi 11 安装失败

delphi 11 安装遇到如下图&#xff1a; 解决方法&#xff1a; 以管理员身份重新安装&#xff01;&#xff01;&#xff01; 以管理员身份重新安装&#xff01;&#xff01;&#xff01; 以管理员身份重新安装&#xff01;&#xff01;&#xff01; 管理员身份&#xff01;&…

【项目】基于C++11实现的数据库连接池

文章目录 前置知识关键技术点项目背景连接池功能点介绍MySQL Server参数介绍功能设计连接池功能点介绍开发平台选型关于MySQL数据库编程MySQL接口介绍测试表设计Connection设计数据库配置文件mysql.conf日志文件log.hppConnectionPool设计压力测试源码链接:前置知识 关键技术…

IOTE 2023盛况回顾,美格智能聚连接之力促数字新生长

9月20~22日&#xff0c;IOTE国际物联网展深圳站在深圳国际会展中心正式召开。本届展会以“IoT构建数字经济底座”为主题&#xff0c;聚焦物联网技术助推数字经济发展的核心动力。美格智能携前沿技术成果亮相展会&#xff0c;与参展观众深入交流。 展会上&#xff0c;美格智能带…

【Kettle】Kettle部署与运行

一、部署 1.安装java 此处安装openjdk1.8&#xff0c;可用yum、apt、源码等方式安装&#xff0c;具体安装方式略。 2.安装kettle 1.下载 https://www.hitachivantara.com/en-us/products/pentaho-platform/data-integration-analytics/pentaho-community-edition.html 此…

【Java 进阶篇】使用 SQL 进行排序查询

在数据库中&#xff0c;我们经常需要对查询的结果进行排序&#xff0c;以便更容易地理解和分析数据。SQL&#xff08;Structured Query Language&#xff09;提供了强大的排序功能&#xff0c;允许我们按照指定的列对数据进行升序或降序排序。本文将详细介绍如何使用 SQL 进行排…

2023软工作业(一)——计算器

班级班级社区作业要求软件工程实践第一次作业-CSDN社区作业目标完成一个具有可视化界面的科学计算器参考文献Fyne 目录 作业要求 项目源码地址 作业目标 0. 界面及功能展示 1. PSP表格 2. 解题思路描述 3. 核心代码 4. 设计与实现过程 5. 程序性能改进 6. 单元测试展…

读取.nrrd和.dcm文件格式医学图片可视化与预处理

nrrd数据格式 MITK默认会将医学图像保存为格式为NRRD的图像&#xff0c;在这个数据格式中包含&#xff1a; 1、一个单个的数据头文件&#xff1a;为科学可视化和医学图像处理准确地表示N维度的栅格信息。 2、既能分开又能合并的图像文件。 nrrd_options输出 {u’dimension’:…

基于Java实现的仓库管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言功能介绍&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导…