pyjallib 0.1.15__py3-none-any.whl → 0.1.17__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyjallib/__init__.py CHANGED
@@ -6,7 +6,7 @@ pyjallib Package
6
6
  Python library for game character development pipeline.
7
7
  """
8
8
 
9
- __version__ = '0.1.15'
9
+ __version__ = '0.1.17'
10
10
 
11
11
  # reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
12
12
  from pyjallib.namePart import NamePart, NamePartType
@@ -14,4 +14,6 @@ from pyjallib.naming import Naming
14
14
  from pyjallib.namingConfig import NamingConfig
15
15
  from pyjallib.nameToPath import NameToPath
16
16
  from pyjallib.perforce import Perforce
17
- from pyjallib.reloadModules import reload_modules
17
+ from pyjallib.reloadModules import reload_modules
18
+ from pyjallib.logger import Logger
19
+ from pyjallib.progressEvent import ProgressEvent
pyjallib/logger.py ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ PyJalLib 중앙 집중식 로깅 모듈
6
+ 모든 PyJalLib 모듈에서 사용할 수 있는 통합 로깅 시스템을 제공합니다.
7
+ """
8
+
9
+ import logging
10
+ from pathlib import Path
11
+ from typing import Optional
12
+ from datetime import datetime
13
+
14
+
15
+ class Logger:
16
+ """PyJalLib 간단한 로깅 클래스"""
17
+
18
+ def __init__(self, inLogPath: Optional[str] = None, inLogFileName: Optional[str] = None, inEnableConsole: bool = True, inEnableUE5: bool = False):
19
+ """로거 인스턴스 초기화
20
+
21
+ Args:
22
+ inLogPath (str, optional): 로그 파일 저장 경로.
23
+ None인 경우 기본 경로 사용 (Documents/PyJalLib/logs)
24
+ inLogFileName (str, optional): 로그 파일명 (확장자 제외).
25
+ None인 경우 기본값 "pyjallib" 사용
26
+ 실제 파일명은 "YYYYMMDD_파일명.log" 형식으로 생성
27
+ inEnableConsole (bool): 콘솔 출력 활성화 여부 (기본값: True)
28
+ inEnableUE5 (bool): UE5 출력 활성화 여부 (기본값: False)
29
+ """
30
+ # 기본 로그 경로 설정
31
+ if inLogPath is None:
32
+ documents_path = Path.home() / "Documents"
33
+ self._logPath = documents_path / "PyJalLib" / "logs"
34
+ else:
35
+ self._logPath = Path(inLogPath)
36
+
37
+ # 로그 디렉토리 생성
38
+ self._logPath.mkdir(parents=True, exist_ok=True)
39
+
40
+ # 로그 파일명 설정 (확장자 제외)
41
+ self._logFileName = inLogFileName if inLogFileName is not None else "pyjallib"
42
+
43
+ # 출력 옵션 설정
44
+ self._enableConsole = inEnableConsole
45
+ self._enableUE5 = inEnableUE5
46
+ self._sessionName = None # 초기에는 세션 없음
47
+
48
+ # 로거 생성 및 설정
49
+ self._logger = logging.getLogger(f"pyjallib_{id(self)}")
50
+ self._logger.setLevel(logging.DEBUG)
51
+ self._logger.handlers.clear() # 기존 핸들러 제거
52
+ self._setup_handlers()
53
+
54
+ def debug(self, inMessage: str) -> None:
55
+ """디버그 레벨 로그 메시지"""
56
+ self._logger.debug(inMessage)
57
+
58
+ def info(self, inMessage: str) -> None:
59
+ """정보 레벨 로그 메시지"""
60
+ self._logger.info(inMessage)
61
+
62
+ def warning(self, inMessage: str) -> None:
63
+ """경고 레벨 로그 메시지"""
64
+ self._logger.warning(inMessage)
65
+
66
+ def error(self, inMessage: str) -> None:
67
+ """에러 레벨 로그 메시지"""
68
+ self._logger.error(inMessage)
69
+
70
+ def critical(self, inMessage: str) -> None:
71
+ """치명적 에러 레벨 로그 메시지"""
72
+ self._logger.critical(inMessage)
73
+
74
+ def set_session(self, inSessionName: str) -> None:
75
+ """새로운 로깅 세션 설정 및 시작
76
+
77
+ Args:
78
+ inSessionName (str): 세션 구분용 이름
79
+ """
80
+ # 기존 세션이 있다면 종료
81
+ if self._sessionName is not None:
82
+ self.end_session()
83
+
84
+ # 새 세션 시작
85
+ self._sessionName = inSessionName
86
+ separator_msg = f"===== {self._sessionName} 로깅 시작 ====="
87
+ self._log_separator(separator_msg)
88
+
89
+ def end_session(self) -> None:
90
+ """현재 로깅 세션 종료 구분선 출력"""
91
+ if self._sessionName is not None:
92
+ separator_msg = f"===== {self._sessionName} 로깅 끝 ====="
93
+ self._log_separator(separator_msg)
94
+ self._sessionName = None
95
+
96
+ def close(self) -> None:
97
+ """로거 핸들러들을 명시적으로 닫기"""
98
+ for handler in self._logger.handlers[:]:
99
+ try:
100
+ handler.close()
101
+ self._logger.removeHandler(handler)
102
+ except Exception:
103
+ pass
104
+
105
+ def _log_separator(self, inMessage: str) -> None:
106
+ """구분선 메시지를 모든 핸들러에 직접 출력"""
107
+ # 구분선은 INFO 레벨로 출력하되, 특별한 포맷 사용
108
+ separator_record = logging.LogRecord(
109
+ name=self._logger.name,
110
+ level=logging.INFO,
111
+ pathname="",
112
+ lineno=0,
113
+ msg=inMessage,
114
+ args=(),
115
+ exc_info=None
116
+ )
117
+
118
+ # 각 핸들러에 직접 전송 (포맷터 우회)
119
+ for handler in self._logger.handlers:
120
+ try:
121
+ # 핸들러 레벨 확인
122
+ if handler.level <= logging.INFO:
123
+ # 구분선만 특별한 포맷으로 출력
124
+ if hasattr(handler, 'stream'):
125
+ handler.stream.write(inMessage + "\n")
126
+ if hasattr(handler, 'flush'):
127
+ handler.flush()
128
+ elif isinstance(handler, _UE5LogHandler):
129
+ # UE5 핸들러의 경우 직접 emit 호출
130
+ handler.emit(separator_record)
131
+ except Exception:
132
+ # 핸들러 오류 시 무시
133
+ pass
134
+
135
+ def _setup_handlers(self) -> None:
136
+ """로거에 핸들러 설정"""
137
+ # 파일 핸들러 (항상 활성화) - 날짜 기반 파일명
138
+ current_date = datetime.now().strftime("%Y%m%d")
139
+ log_filename = f"{current_date}_{self._logFileName}.log"
140
+ log_file = self._logPath / log_filename
141
+ file_handler = logging.FileHandler(log_file, encoding='utf-8')
142
+ file_handler.setFormatter(self._get_formatter())
143
+ self._logger.addHandler(file_handler)
144
+
145
+ # 콘솔 핸들러 (선택사항)
146
+ if self._enableConsole:
147
+ console_handler = logging.StreamHandler()
148
+ console_handler.setFormatter(self._get_formatter())
149
+ self._logger.addHandler(console_handler)
150
+
151
+ # UE5 핸들러 (선택사항)
152
+ if self._enableUE5:
153
+ ue5_handler = self._create_ue5_handler()
154
+ if ue5_handler:
155
+ self._logger.addHandler(ue5_handler)
156
+
157
+ def _get_formatter(self) -> logging.Formatter:
158
+ """표준 포맷터 반환"""
159
+ return logging.Formatter(
160
+ '%(asctime)s - %(levelname)s - %(message)s',
161
+ datefmt='%Y-%m-%d %H:%M:%S'
162
+ )
163
+
164
+ def _create_ue5_handler(self) -> Optional[logging.Handler]:
165
+ """UE5 핸들러 생성 (기존 UE5LogHandler 코드 활용)"""
166
+ try:
167
+ return _UE5LogHandler()
168
+ except Exception:
169
+ # UE5 핸들러 생성 실패 시 None 반환
170
+ return None
171
+
172
+
173
+ class _UE5LogHandler(logging.Handler):
174
+ """UE5 전용 로그 핸들러 - UE5의 로그 시스템과 호환되도록 설계"""
175
+
176
+ def emit(self, record):
177
+ """로그 레코드를 UE5 로그 시스템으로 전송"""
178
+ try:
179
+ # UE5의 unreal.log 함수 사용
180
+ import unreal
181
+
182
+ # 메시지 포맷팅
183
+ message = self.format(record) if self.formatter else record.getMessage()
184
+
185
+ # 로그 레벨에 따라 적절한 UE5 로그 함수 호출
186
+ if record.levelno >= logging.ERROR:
187
+ unreal.log_error(f"[PyJalLib] {message}")
188
+ elif record.levelno >= logging.WARNING:
189
+ unreal.log_warning(f"[PyJalLib] {message}")
190
+ elif record.levelno >= logging.INFO:
191
+ unreal.log(f"[PyJalLib] {message}")
192
+ else: # DEBUG
193
+ unreal.log(f"[PyJalLib-DEBUG] {message}")
194
+
195
+ except ImportError:
196
+ # unreal 모듈이 없는 경우 표준 출력 사용
197
+ message = self.format(record) if self.formatter else record.getMessage()
198
+ print(f"[PyJalLib] {message}")
199
+ except Exception:
200
+ # 모든 예외를 무시하여 로깅 실패가 애플리케이션을 중단하지 않도록 함
201
+ pass
pyjallib/max/bip.py CHANGED
@@ -479,27 +479,6 @@ class Bip:
479
479
 
480
480
  if inCollapseLayers:
481
481
  self.collapse_layers(inBipRoot)
482
-
483
- if inBakeAllKeys:
484
- allTargetBipedObjs = self.get_nodes(inBipRoot)
485
- startFrame = rt.execute("(animationRange.start as integer) / TicksPerFrame")
486
- endFrame = rt.execute("(animationRange.end as integer) / TicksPerFrame")
487
- totalFrame = endFrame - startFrame + 1
488
-
489
- for frame in range(startFrame, endFrame + 1):
490
- for item in allTargetBipedObjs:
491
- if item == item.controller.rootNode:
492
- horizontalController = rt.getPropertyController(item.controller, "horizontal")
493
- verticalController = rt.getPropertyController(item.controller, "vertical")
494
- turningController = rt.getPropertyController(item.controller, "turning")
495
-
496
- rt.biped.addNewKey(horizontalController, frame)
497
- rt.biped.addNewKey(verticalController, frame)
498
- rt.biped.addNewKey(turningController, frame)
499
- else:
500
- rt.biped.addNewKey(item.controller, frame)
501
- if progress_callback:
502
- progress_callback(frame - startFrame + 1, totalFrame)
503
482
 
504
483
  minFrame = 0.0
505
484
  maxFrame = 0.0
pyjallib/max/skeleton.py CHANGED
@@ -164,7 +164,6 @@ class Skeleton:
164
164
  returnArray = []
165
165
  nodeArray = self.get_dependencies(inObjs)
166
166
 
167
-
168
167
  # 애드온 레이어의 노드들만 필터링
169
168
  addOnArray = []
170
169
  for item in nodeArray:
@@ -172,13 +171,7 @@ class Skeleton:
172
171
  addOnArray.append(item)
173
172
 
174
173
  # 애드온 노드들의 의존성 가져오기
175
- addOnRefArray = []
176
- progress = Progress("Get All Dependencies", inTotalSteps=len(addOnArray))
177
- for i,item in enumerate(addOnArray):
178
- refs = self.get_dependencies(item)
179
- progress.update(i)
180
-
181
- addOnRefArray.extend(refs)
174
+ addOnRefArray = self.get_dependencies(addOnArray)
182
175
 
183
176
  # 모든 노드들을 returnArray에 추가 (중복 없이)
184
177
  for item in nodeArray:
pyjallib/naming.py CHANGED
@@ -1014,7 +1014,10 @@ class Naming:
1014
1014
  nameArray[partIndex] = inNewName
1015
1015
 
1016
1016
  newName = self._combine(nameArray, self._get_filtering_char(inStr))
1017
- newName = self.set_index_padding_num(newName)
1017
+
1018
+ indexIndex = self.get_name_part_index("Index")
1019
+ if indexIndex >= 0:
1020
+ newName = self.set_index_padding_num(newName)
1018
1021
 
1019
1022
  return newName
1020
1023