PyQt 프로젝트 기본 구조
139341 단어 pyqt
tags:[pyqt, 파일 관리자, 파일 드래그, 창 스트레칭] 클래스 파일 관리자
프로그램 디렉토리 구조
--
|--- activity #
|--- frame #
|--- config #
|--- utils #
|--- deorator #
|--- resource #
-main.py
-requirement.txt
아래의 질서는 가방의 상간 정도에 따라 순서를 정한다.
config 패키지
config 패키지는 배치에서 변경될 수 있는 기본 설정입니다. 예를 들어 데이터베이스, 저장 경로, 상용 문자열 및 기타 서비스의 명령 파라미터 등입니다.
setting.py
다음은 샘플 파일일 뿐 실제 프로덕션 파일은 아닙니다.
import os
dir_path = os.path.abspath(os.path.dirname(__file__))
ROOT_PATH = os.path.abspath(os.path.join(dir_path, os.path.pardir))
DOC_PATH = os.path.abspath(os.path.join(ROOT_PATH, "doc"))
# chm
temp_path = "temp"
# chm
chm_path = "document"
#
html_height_line = "html"
# crate dir
def check(path):
if not os.path.exists(path):
os.makedirs(path)
return path
# document path
def get_chm_path():
return check(os.path.join(ROOT_PATH, "chm"))
이 파일은 항목에 사용되는 기본 경로를 저장합니다.코드에서 여러 번 사용할 수 있습니다. 예를 들어 후기에 수정할 때 다른 코드가 완전하고 변경할 필요가 없는 기초 위에서 수요를 신속하게 완성할 수 있습니다.
기타 구성 파일
프로젝트에 대량의 프로필이 존재하면 여러 종류의 프로필을 새로 만들 수 있습니다.
deorator 패키지
이 패키지는 레이지 같은 일반적인 장식기를 저장하고 데이터베이스에 log 봉인할 수 있습니다
lazy_porperty.py
lazy 불러오기, lazy에 대한 다른 블로그 설명을 참고할 수 있습니다. 다음 예는 Django2를 사용합니다.3의 lazy 소스
from functools import total_ordering, wraps
# author by Django
class Promise:
"""
。
。
"""
pass
def __cast(self):
pass
def _lazy_proxy_unpickle(func, args, kwargs, *result_classes):
return lazy(func, *result_classes)(*args, **kwargs)
def lazy(func, *result_classes):
"""
。
—— ,
。
"""
@total_ordering
class Proxy(Promise):
"""
。
。
"""
__prepared = False
def __init__(self, args, kw):
self.__args = args
self.__kw = kw
if not self.__prepared:
self.__prepare_class__()
self.__prepared = True
def __reduce__(self):
return (
_lazy_proxy_unpickle,
(func, self.__args, self.__kw) + result_classes
)
def __repr__(self):
return repr(self.__cast())
@classmethod
def __prepare_class__(cls):
for resultclass in result_classes:
for type_ in resultclass.mro():
for method_name in type_.__dict__:
# __promise__
if hasattr(cls, method_name):
continue
math = cls.__promise__(method_name)
setattr(cls, method_name, math)
cls._delegate_bytes = bytes in result_classes
cls._delegate_text = str in result_classes
assert not (cls._delegate_bytes and cls._delegate_text), (
" lazy()")
if cls._delegate_text:
cls.__str__ = cls.__text_cast
elif cls._delegate_bytes:
cls.__bytes__ = cls.__bytes_cast
@classmethod
def __promise__(cls, method_name):
def __wrapper(self, *args, **kw):
res = func(*self.__args, **self.__kw)
return getattr(res, method_name)(*args, **kw)
return __wrapper
def __text_cast(self):
return func(*self.__args, **self.__kw)
def __bytes_cast(self):
return bytes(func(*self.__args, **self.__kw))
def __bytes_cast_encoded(self):
return func(*self.__args, **self.__kw).encode()
def __cast(self):
if self._delegate_bytes:
return self.__bytes_cast()
elif self._delegate_text:
return self.__text_cast()
else:
return func(*self.__args, **self.__kw)
def __str__(self):
# object defines __str__(), so __prepare_class__() won't overload
# a __str__() method from the proxied class.
return str(self.__cast())
def __eq__(self, other):
if isinstance(other, Promise):
other = other.__cast()
return self.__cast() == other
def __lt__(self, other):
if isinstance(other, Promise):
other = other.__cast()
return self.__cast() < other
def __hash__(self):
return hash(self.__cast())
def __mod__(self, rhs):
if self._delegate_text:
return str(self) % rhs
return self.__cast() % rhs
def __deepcopy__(self, memo):
# 。 。
memo[id(self)] = self
return self
@wraps(func)
def __wrapper__(*args, **kw):
#
return Proxy(args, kw)
return __wrapper__
class LazyProperty(object):
"""
lazy
:
@LazyProperty
def test_function():
do something
"""
def __init__(self, fun):
self.fun = fun
def __get__(self, instance, owner):
if instance is None:
return self
value = self.fun(instance)
setattr(instance, self.fun.__name__, value)
return value
utils 패키지
도구 패키지에는 문자열 처리, 파일 처리, 루틴 조작, 로그 등 자주 사용하는 도구 종류가 저장되어 있습니다.
logger.py
python logging 로그 패키지는 실제 요구를 직접적으로 충족시키지 못하기 때문에 logger를 재구성해야 합니다 (예시 파일에서 setting의 로그를 포함하는 저장 경로 setting.get log path () 를 제외하고는 독립적으로 실행할 수 있습니다)
# coding=utf-8
"""
create by pymu on 2020/4/29
package: .logger.py
project: status_document_0.1
"""
import datetime
import logging
import os
import re
from config import setting
description = " "
LOG_PATH = setting.get_log_path()
class Logger(logging.Logger):
"""
"""
def __init__(self, name='system', level=logging.INFO):
super().__init__(name, level)
self.level = level
self.name = name
self.__set_log_handler()
def __set_log_handler(self):
"""
, INFO
:return:
"""
main_handler = MyLoggerHandler(filename=self.name, when='D', backup_count=5,
encoding="utf-8")
warn_handler = MyLoggerHandler(filename=' ', when='D',
backup_count=5, encoding="utf-8")
error_handler = MyLoggerHandler(filename=' ', when='D',
backup_count=35, encoding="utf-8")
#
formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
_formatter = logging.Formatter("
%(asctime)s - %(levelname)s: %(message)s")
bug_filter = logging.Filter()
bug_filter.filter = lambda record: record.levelno == logging.ERROR #
error_handler.addFilter(bug_filter)
error_handler.setFormatter(_formatter)
self.addHandler(error_handler)
bug_filter = logging.Filter()
bug_filter.filter = lambda record: record.levelno == logging.WARNING #
warn_handler.addFilter(bug_filter)
warn_handler.setFormatter(_formatter)
self.addHandler(warn_handler)
bug_filter = logging.Filter()
bug_filter.filter = lambda record: record.levelno < logging.WARNING #
main_handler.addFilter(bug_filter)
main_handler.addFilter(bug_filter)
main_handler.setFormatter(formatter)
self.main_handler = main_handler
self.addHandler(main_handler)
def reset_name(self, name):
"""
:param name:
:return:
"""
self.name = name
self.removeHandler(self.main_handler)
self.__set_log_handler()
try:
import codecs
except ImportError:
codecs = None
class MyLoggerHandler(logging.FileHandler):
def __init__(self, filename, when='M', backup_count=15, encoding=None, delay=False):
self.prefix = os.path.join(LOG_PATH, '{name}'.format(name=filename))
self.filename = filename
self.when = when.upper()
# S - Every second a new file
# M - Every minute a new file
# H - Every hour a new file
# D - Every day a new file
if self.when == 'S':
self.suffix = "%Y-%m-%d_%H-%M-%S"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\.log$"
elif self.when == 'M':
self.suffix = "%Y-%m-%d_%H-%M"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.log$"
elif self.when == 'H':
self.suffix = "%Y-%m-%d_%H"
self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}\.log$"
elif self.when == 'D':
self.suffix = "%Y-%m-%d"
self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
else:
raise ValueError("Invalid rollover interval specified: %s" % self.when)
self.filePath = "%s%s.log" % (self.prefix, datetime.datetime.now().strftime(self.suffix))
try:
if os.path.exists(LOG_PATH) is False:
os.makedirs(LOG_PATH)
except Exception as e:
print("can not make dirs")
print("filepath is " + self.filePath)
print(e)
self.backupCount = backup_count
if codecs is None:
encoding = None
logging.FileHandler.__init__(self, self.filePath, 'a', encoding, delay)
def write_log(self):
_filePath = "%s%s.log" % (self.prefix, datetime.datetime.now().strftime(self.suffix))
if _filePath != self.filePath:
self.filePath = _filePath
return 1
return 0
def change_file(self):
self.baseFilename = os.path.abspath(self.filePath)
if self.stream is not None:
self.stream.flush()
self.stream.close()
if not self.delay:
self.stream = self._open()
if self.backupCount > 0:
for s in self.delete_old_log():
os.remove(s)
def delete_old_log(self):
dir_name, base_name = os.path.split(self.baseFilename)
file_names = os.listdir(dir_name)
result = []
p_len = len(self.filename)
for fileName in file_names:
if fileName[:p_len] == self.filename:
suffix = fileName[p_len:]
if re.compile(self.extMatch).match(suffix):
result.append(os.path.join(dir_name, fileName))
result.sort()
if len(result) < self.backupCount:
result = []
else:
result = result[:len(result) - self.backupCount]
return result
def emit(self, record):
"""
Emit a record.
"""
# noinspection PyBroadException
try:
if self.write_log():
self.change_file()
logging.FileHandler.emit(self, record)
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
if __name__ == "__main__":
# to do something
log = Logger('info')
import time
for i in range(12):
time.sleep(1)
log.info(" " + str(i))
특히 로그는 창의 기본 클래스에서 설명할 수 있습니다. 그러면 하위 클래스에서 호출할 수 있습니다. 구체적인 로그 성명은 제출 로그 방법으로 표시하거나 로그 방법에서 추출하는 방법 이름으로 표시됩니다.
thread.py
qt에서는 데이터를 다른 단계로 업데이트하거나 실행하기 위해 라인을 사용합니다.UI 스레드가 혼잡하지 않도록 하기 위해서는 Qthread가 시간 소모 방법을 실행해야 하며,thread 방법으로 인터페이스를 직접 업데이트하면 신호가 없는 스레드가 UI를 업데이트할 것을 경고합니다.
#!/usr/bin/python3
# coding=utf-8
"""---------------------------------------
project :
/ :.thread.py
:v1.0
:pymu on 2020/4/22
:
---------------------------------------"""
from PyQt5.QtCore import QThread, pyqtSignal
from utils.logger import Logger
class FuncThread(QThread):
"""
"""
#
thread_signal = pyqtSignal(dict)
#
thread_error_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
self.func = None
self.func_args = []
self.func_kwargs = {}
self.logger = Logger()
def set_func(self, func, *args, **kwargs):
"""
, ,
:param func:
:param args:
:param kwargs: ( )
:return: None
"""
self.func = func
self.func_args = args
self.func_kwargs = kwargs
self.logger.info(" func={} arg={} kwargs={}".format(func.__name__, args, kwargs))
def run(self):
"""
:
start()
,
"""
if not self.func:
return
self.logger.info(" func={} arg={} kwargs={}".format(self.func.__name__, self.func_args, self.func_kwargs))
try:
result = self.func(*self.func_args, **self.func_kwargs)
if not isinstance(result, dict):
result = {}
self.thread_signal.emit(result)
except Exception as e:
self.thread_error_signal.emit(str(e))
self.logger.error(" func={} arg={} kwargs={}, error={}".format(self.func.__name__,
self.func_args,
self.func_kwargs,
e))
참고: 메소드에 추가될 수 있습니다. 실행 성공 신호, 예외 신호를 제외하고 여러 개의 신호를 추가할 수 있지만 실행 방법에 제출 신호(관찰자 모드)를 명시해야 합니다. 구체적인 사용 방법은 다음에 실례적으로 설명합니다. #스레드self를 업데이트합니다.update_thread = FuncThread() self.update_thread.thread_signal.connect(self.set_tree_data) self.update_thread.set_func(self.get_dir_dict, self.activity_path, _) self.update_thread.start()
resourceLoad.py
정적 자원 마운트, qt에서 대량의 아이콘 파일, 글꼴 설명을 사용합니다.(qt가 메모리에 저장될지 모르기 때문에 스스로 추가했다) 레이지 불러오기와 결합하면 빠르게 접근할 수 있다(복잡한 레이아웃에 대한).
#!/usr/bin/python3
# coding=utf-8
"""---------------------------------------
project :
/ :.resourceLoad.py
:v1.0
:pymu on 2020/4/21
:
---------------------------------------"""
import os
import threading
import qtawesome
from PyQt5 import QtGui
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QBrush, QColor
from config import setting
from utils.lazy_property import LazyProperty
class Resource:
"""
"""
_instance_lock = threading.Lock()
def __init__(self):
self.img_path = setting.get_img_path()
@classmethod
def instance(cls):
with Resource._instance_lock:
if not hasattr(Resource, "_instance"):
Resource._instance = Resource()
return Resource._instance
def render_icon(self, name):
"""
:return:
"""
path = os.path.join(self.img_path, name)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(path), QtGui.QIcon.Normal, QtGui.QIcon.On)
return icon
@LazyProperty
def QT_img_document(self):
"""
:return:
"""
return self.render_icon("icon_tree_document.png")
@LazyProperty
def QT_img_dir(self):
"""
:return:
"""
return self.render_icon("icon_tree_file.png")
@LazyProperty
def QT_img_doc(self):
"""
doc
:return:
"""
return self.render_icon("icon-word.png")
@LazyProperty
def QT_img_excel(self):
"""
Excel
:return:
"""
return self.render_icon("icon-excel.png")
@LazyProperty
def QT_img_pdf(self):
"""
PDF
:return:
"""
return self.render_icon("icon-pdf.png")
@LazyProperty
def QT_img_template_logo(self):
"""
logo
:return:
"""
return self.render_icon("logo-document.png")
@LazyProperty
def QT_img_new_folder(self):
"""
:return:
"""
return self.render_icon("icon-add.png")
@LazyProperty
def QT_img_delete(self):
"""
:return:
"""
return self.render_icon("icon-delete.png")
@LazyProperty
def QT_img_upload(self):
"""
:return:
"""
return self.render_icon("icon-up.png")
@LazyProperty
def QT_img_download(self):
"""
:return:
"""
return self.render_icon("icon-down.png")
@LazyProperty
def QT_img_search(self):
"""
:return:
"""
return self.render_icon("icon_search.png")
@LazyProperty
def QT_img_home_logo(self):
"""
:return:
"""
return self.render_icon("icon-catalogue.png")
@LazyProperty
def QT_img_clear(self):
"""
:return:
"""
return self.render_icon("icon_input_clear.png")
@LazyProperty
def QT_img_unknown(self):
"""
:return:
"""
return self.render_icon("unknown.png")
@LazyProperty
def QT_img_none(self):
"""
:return:
"""
return self.render_icon("??")
@LazyProperty
def QT_img_input_user(self):
"""
:return:
"""
# return self.render_icon("icon_login_user.png")
return qtawesome.icon('fa.user', color="#666")
@LazyProperty
def QT_img_input_pass(self):
"""
:return:
"""
# return self.render_icon("icon_login_key.png")
return qtawesome.icon('fa.unlock-alt', color="#666")
@LazyProperty
def QT_img_user_more(self):
"""
:return:
"""
return qtawesome.icon('fa.angle-down', color="white")
@LazyProperty
def QT_font_16px_song(self):
"""
14 px
:return:
"""
font = QFont()
font.setPixelSize(16)
font.setFamily(" ")
return font
@LazyProperty
def QT_brush_font_color_333(self):
"""
1
:return:
"""
brush = QBrush(QColor(51, 51, 51, 255))
brush.setStyle(Qt.NoBrush)
return brush
fileutils.py(프로젝트 예)
이것은 내 프로젝트에서 가장 짜증나는 부분, 불필요하고 질질 끄는 부분을 저장하여 사용할 수 있다.
#!/usr/bin/python3
# coding=utf-8
"""---------------------------------------
project :
/ :.fileutils.py
:v1.0
:pymu on 2020/4/13
:
---------------------------------------"""
import base64
import json
import os
import shutil
import time
from lxml import etree
from config import setting
def get_file_name(path):
file_name = os.path.basename(path)
if not file_name:
return
else:
return file_name.split(".")[0]
def unchm(path):
"""
chm
:param path:
:return:
"""
file_name = get_file_name(path)
if not file_name:
return
temp_path = os.path.join(setting.get_temp_path(), file_name)
if not os.path.exists(temp_path):
os.makedirs(temp_path)
command = "HH.EXE -decompile {export_path} {source_path}".format(export_path=temp_path, source_path=path)
os.system(command)
def formatTime(temp_time):
"""
''' '''
:param temp_time:
:return:
"""
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(temp_time))
def formatByte(number):
"""
:param number:
:return:
"""
if number < 1024:
return " 1 "
elif number <= 1024 * 1024:
return "%.2f KB" % (number / 1024.0)
elif number <= (1024 * 1024 * 1024):
return "%.2f MB" % (number / 1024.0 / 1024.0)
elif number <= (1024 * 1024 * 1024 * 1024):
return "%.2f GB" % (number / 1024.0 / 1024.0 / 1024.0)
else:
return " "
def get_doc_dict(path, match_html=True):
"""
,
:return:
"""
result = dict()
if not os.path.exists(path):
return result
for i in os.listdir(path):
file = os.path.join(path, i)
if os.path.isdir(file):
s = get_doc_dict(file)
if s:
result.update({i: s})
else:
f_type = i.split(".")
if match_html:
if len(f_type) > 0 and "html" in f_type[-1] or "htm" in f_type[-1]:
result.update({i: file})
else:
result.update({i: file})
return None if len(result.keys()) == 0 else result
def get_doc_dict_new(path):
"""
:param path:
:return:
"""
result = dict()
if not os.path.exists(path):
return result
for i in os.listdir(path):
file = os.path.join(path, i)
if os.path.isdir(file):
s = get_doc_dict_new(file)
result.update({i: s})
else:
result.update({i: file})
return result
def get_dir_dict(path):
"""
,
:param path:
:return:
"""
result = dict()
if not os.path.exists(path):
return result
for i in os.listdir(path):
file = os.path.join(path, i)
if os.path.isdir(file):
s = get_dir_dict(file)
result.update({i: s})
return result
def get_path_list(path, root_path):
"""
:param root_path: ,
:param path: path
:return:
"""
result_dict = dict()
result_dict.update({"root": path})
result = list()
for root, dirs, files in os.walk(path):
for dir_t in dirs:
item = dict()
path = os.path.join(root, dir_t)
item.update({
"last_change_time": formatTime(os.path.getctime(path)),
"path": path,
"size": "",
"name": dir_t,
"type": "dir"
})
result.append(item)
for file in files:
item = dict()
path = os.path.join(root, file)
file_info = os.stat(path)
item.update({"path": os.path.abspath(path),
"size": formatByte(os.path.getsize(path)),
"last_change_time": formatTime(file_info.st_mtime),
"name": file,
"type": os.path.splitext(path)[1]})
result.append(item)
if not result:
result.insert(0, {"path": os.path.abspath(os.path.join(root, os.path.pardir)),
"size": "",
"last_change_time": "",
"name": " ",
"type": "none"})
if not root == root_path:
result.insert(0, {"path": os.path.abspath(os.path.join(root, os.path.pardir)),
"size": "",
"last_change_time": "",
"name": "<,
"type": "back"})
break
result_dict.update({"data": result})
return result_dict
def clean_temp_file(paths: list):
"""
:param paths:
:return:
"""
for file in os.listdir(setting.get_temp_path()):
file_path = os.path.join(setting.get_temp_path(), file)
if file_path not in paths:
print(file_path)
# noinspection PyBroadException
try:
shutil.rmtree(file_path)
except Exception as e:
print(e)
def check_chm():
"""
chm
:return:
"""
file_dir = []
for file in os.listdir(setting.get_chm_path()):
#
file_path = os.path.join(setting.get_chm_path(), file)
if os.path.isfile(file_path):
s = file.split(".")
if len(s) > 1 and str(s[-1]).lower() == "chm":
file_name = get_file_name(file_path)
if not file_name:
return
temp_path = os.path.join(setting.get_temp_path(), file_name)
file_dir.append(temp_path)
if not os.path.exists(temp_path):
unchm(file_path)
clean_temp_file(file_dir)
def match_key(path, key) -> bool:
"""
:param path:
:param key:
:return:
"""
# noinspection PyBroadException
try:
with open(path, "rb") as f:
response = etree.HTML(text=f.read())
if key in response.xpath('string(.)'):
return True
return False
except:
return False
def test():
from lxml import etree
with open("temp/css/index.htm", "rb") as f:
response = etree.HTML(text=f.read())
if " 1" in response.xpath('string(.)'):
return True
return False
def make_dir(path, name):
"""
:param name:
:param path:
:return:
"""
path = os.path.join(path, name)
os.makedirs(path)
def filter_search(result: dict, data, m):
"""
key list
:param result:
:param data:
:param m:
:return:
"""
if isinstance(data, dict):
# key , value
for key in data:
value = data.get(key)
#
if isinstance(value, dict):
filter_search(result, value, m)
else:
if m.lower() in key.lower():
item = {"path": value,
"size": "",
"last_change_time": "",
"name": key,
"type": os.path.splitext(str(value))[1]}
result.get("data").append(item)
def delete_any(path):
"""
"""
if os.path.isdir(path):
shutil.rmtree(path)
else:
os.remove(path)
def delete_path(path):
"""
, ,
:param path:
:return:
"""
if isinstance(path, list):
for i in path:
delete_any(i)
return
if isinstance(path, str):
delete_any(path)
return
raise Exception(" {}".format(path))
def check_exists(source, target):
"""
:param source:
:param target:
:return:
"""
result = []
if source:
for i in source:
file_name = os.path.basename(i)
if os.path.exists(os.path.join(target, file_name)):
result.append(file_name)
return result
def copy_file(source, target):
"""
:param target:
:param source:
:return:
"""
if source:
target = os.path.abspath(target)
for i in source:
target_ = os.path.join(target, os.path.basename(i))
if os.path.isfile(i):
shutil.copyfile(i, target_)
else:
if os.path.exists(target_):
shutil.rmtree(target_)
shutil.copytree(i, target_)
def get_load_user_info():
"""
:return:
"""
# noinspection PyBroadException
try:
with open(setting.get_user_ini_path(), "rb") as f:
content = f.read()
return json.loads(base64.b64decode(content).decode())
except Exception as e:
print(e)
return dict()
# noinspection PyBroadException
def replace_html(path, key):
"""
:param key:
:param path:
:return:
"""
if not path:
return
path = os.path.join(setting.get_activity_path(), path)
if key:
resource_path = setting.get_resource_path()
html_path = os.path.join(resource_path, "html")
html_path = os.path.join(html_path, "temp.html")
js_path = os.path.join(resource_path, "js")
# jq
jq_path = os.path.join(js_path, "jquery-1.12.4.min.js")
# js
js_path = os.path.join(js_path, "height_line._js")
# ,
with open(path, "r", errors='ignore') as file:
html_content = file.read()
# js
with open(js_path, "r", errors='ignore') as file:
js_content = file.read()
#
if html_content and js_content:
js_content = js_content.replace("{{key}}", key)
js_content = js_content.replace("{{jq_path}}", "file:///" + jq_path.replace("\\", "/"))
new_content = html_content.replace("
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
MacBook Pro(Apple Silicon, M1 PRO 2021)를 이용하여 Python 개발 환경 조정MacBook Pro (Apple Silicon, M1 PRO, 2021) macOS Monterey version 12.0.1 파이썬 3.9.7을 활용한 개발. 버전은 Qt5 대응 관계의 버전입니다. 주로 딥러닝 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.