pyjallib 0.1.12__py3-none-any.whl → 0.1.13__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.
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ ToolManager 모듈 - 3DS Max에서 실행되는 도구들을 관리
6
+ 도구 인스턴스 생성, 닫기 등을 담당
7
+ """
8
+
9
+ from PySide2 import QtWidgets, QtCore
10
+ import gc
11
+
12
+ class ToolManager:
13
+ def __init__(self):
14
+ self.tools = {} # {tool_class_name: [instances]} 형태로 관리
15
+
16
+ def register_tool(self, tool_instance):
17
+ """도구 인스턴스를 등록합니다"""
18
+ class_name = tool_instance.__class__.__name__
19
+
20
+ if class_name not in self.tools:
21
+ self.tools[class_name] = []
22
+
23
+ self.tools[class_name].append(tool_instance)
24
+
25
+ def close_tool_by_type(self, tool_class):
26
+ """특정 유형의 도구를 모두 닫습니다"""
27
+ class_name = tool_class.__name__
28
+
29
+ if class_name not in self.tools:
30
+ return
31
+
32
+ # 해당 클래스의 모든 인스턴스 정리
33
+ for tool in self.tools[class_name]:
34
+ try:
35
+ if hasattr(tool, 'close'):
36
+ tool.close()
37
+ if hasattr(tool, 'deleteLater'):
38
+ tool.deleteLater()
39
+ except (RuntimeError, AttributeError) as e:
40
+ print(f"도구 닫기 오류: {e}")
41
+
42
+ # 목록 비우기
43
+ self.tools[class_name] = []
44
+
45
+ # 추가적으로 QApplication.allWidgets()를 통한 검사
46
+ try:
47
+ window_title = None
48
+ if hasattr(tool_class, 'windowTitle'):
49
+ window_title = tool_class.windowTitle
50
+
51
+ for widget in QtWidgets.QApplication.allWidgets():
52
+ if (isinstance(widget, QtWidgets.QDialog) and
53
+ ((window_title and hasattr(widget, 'windowTitle') and widget.windowTitle() == window_title) or
54
+ widget.__class__.__name__ == class_name)):
55
+ try:
56
+ widget.close()
57
+ widget.deleteLater()
58
+ except:
59
+ pass
60
+ except Exception as e:
61
+ print(f"위젯 검색 오류: {e}")
62
+
63
+ # 가비지 컬렉션 수행
64
+ gc.collect()
65
+
66
+ def show_tool(self, tool_class, **kwargs):
67
+ """
68
+ 도구를 표시합니다. 중복 실행을 방지하고 항상 새 인스턴스를 생성합니다.
69
+
70
+ Args:
71
+ tool_class: 도구 클래스
72
+ **kwargs: 도구 클래스 생성자에 전달할 인자들
73
+
74
+ Returns:
75
+ 새로 생성된 도구 인스턴스
76
+ """
77
+ # 기존 인스턴스 모두 정리
78
+ self.close_tool_by_type(tool_class)
79
+
80
+ # 약간의 지연을 두어 정리 완료를 기다림
81
+ QtCore.QTimer.singleShot(50, lambda: None)
82
+
83
+ # 새 인스턴스 생성
84
+ tool_instance = tool_class(**kwargs)
85
+
86
+ # 도구 등록
87
+ self.register_tool(tool_instance)
88
+
89
+ # 도구 표시
90
+ tool_instance.show()
91
+
92
+ return tool_instance
pyjallib/max/twistBone.py CHANGED
@@ -258,6 +258,9 @@ class TwistBone:
258
258
  twistBoneRotListController.setActive(twistBoneRotListController.count)
259
259
  twistBoneRotListController.weight[0] = 100.0
260
260
 
261
+ # 첫 번째 트위스트 본을 boneChainArray에 추가
262
+ boneChainArray.append(twistBone)
263
+
261
264
  if twistNum > 1:
262
265
  lastBone = self.bone.create_nub_bone(boneName, 2)
263
266
  lastBone.name = self.name.replace_name_part("Index", boneName, str(twistNum))
@@ -320,7 +323,8 @@ class TwistBone:
320
323
  if not inBoneChain or inBoneChain.is_empty():
321
324
  return None
322
325
 
323
- # 기존 객체 삭제
326
+ # 기존 객체 삭제 (delete_all 대신 delete 사용)
327
+ # delete는 bones와 helpers만 삭제하고 sourceBones와 parameters는 유지함
324
328
  inBoneChain.delete()
325
329
 
326
330
  # BoneChain에서 필요한 정보 추출
@@ -275,7 +275,8 @@ class VolumeBone: # Updated class name to match the new file name
275
275
  if not inBoneChain or inBoneChain.is_empty():
276
276
  return None
277
277
 
278
- # 기존 객체 삭제
278
+ # 기존 객체 삭제 (delete_all 대신 delete 사용)
279
+ # delete는 bones와 helpers만 삭제하고 sourceBones와 parameters는 유지함
279
280
  inBoneChain.delete()
280
281
 
281
282
  # BoneChain에서 필요한 정보 추출
pyjallib/perforce.py CHANGED
@@ -771,6 +771,40 @@ class Perforce:
771
771
  self._handle_p4_exception(e, f"파일/폴더 업데이트 필요 여부 확인 ({processed_paths})")
772
772
  return False
773
773
 
774
+ def is_file_in_perforce(self, file_path: str) -> bool:
775
+ """파일이 Perforce에 속하는지 확인합니다.
776
+
777
+ Args:
778
+ file_path (str): 확인할 파일 경로
779
+
780
+ Returns:
781
+ bool: 파일이 Perforce에 속하면 True, 아니면 False
782
+ """
783
+ if not self._is_connected():
784
+ return False
785
+
786
+ logger.debug(f"파일 '{file_path}'가 Perforce에 속하는지 확인 중...")
787
+ try:
788
+ # p4 files 명령으로 파일 정보 조회
789
+ file_info = self.p4.run_files(file_path)
790
+
791
+ # 파일 정보가 있고, 'no such file(s)' 오류가 없는 경우
792
+ if file_info and not any("no such file(s)" in str(err).lower() for err in self.p4.errors):
793
+ logger.info(f"파일 '{file_path}'가 Perforce에 존재합니다.")
794
+ return True
795
+ else:
796
+ logger.info(f"파일 '{file_path}'가 Perforce에 존재하지 않습니다.")
797
+ return False
798
+
799
+ except P4Exception as e:
800
+ # 파일이 존재하지 않는 경우는 일반적인 상황이므로 경고 레벨로 로깅
801
+ if any("no such file(s)" in err.lower() for err in self.p4.errors):
802
+ logger.info(f"파일 '{file_path}'가 Perforce에 존재하지 않습니다.")
803
+ return False
804
+ else:
805
+ self._handle_p4_exception(e, f"파일 '{file_path}' Perforce 존재 여부 확인")
806
+ return False
807
+
774
808
  def sync_files(self, file_paths: list) -> bool:
775
809
  """파일이나 폴더를 동기화합니다.
776
810
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyjallib
3
- Version: 0.1.12
3
+ Version: 0.1.13
4
4
  Summary: A utility library for 3D game character development pipelines.
5
5
  Author-email: Dongseok Kim <jalnagakds@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -1,34 +1,38 @@
1
- pyjallib/__init__.py,sha256=4EmbUX3I3DLJ3DpGz7kBAADCFskvffdUEPd3HIu7-9k,509
1
+ pyjallib/__init__.py,sha256=ntM7q_hmCFkCShsMra8MKtcPGZf4z-MYuPIc3jyp6aM,509
2
2
  pyjallib/namePart.py,sha256=lKIiOVkWrtAW-D3nuv--vHmdAnlQeVPaXLYUDhcr8QU,24177
3
3
  pyjallib/nameToPath.py,sha256=aBeezepLYdpv3VYxnQ2c4ZWzz2WjticXjkdbAIlVa1k,4676
4
4
  pyjallib/naming.py,sha256=b2C-P9VWV4Q2StqkizEwABblYOC5g6sXHzN0KpOZ_Ys,37419
5
5
  pyjallib/namingConfig.py,sha256=QGpK5mCnRiclKqNKz3GJ2PeJO8fbVitAEdqWwnwo8oA,34127
6
- pyjallib/perforce.py,sha256=XcF-YG250bh3obOkajPzMNwaJcCmXKIHIlJIEEFw6JE,46459
6
+ pyjallib/perforce.py,sha256=MhG4nCJxvrCBlEKmZXVRI93jcfTcJHlM_xn002OxXU8,48032
7
7
  pyjallib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  pyjallib/reloadModules.py,sha256=RAEG3IxzJ0TlsjvnZwJt56JOkc2j8voqAnRbfQuZ44g,1151
9
9
  pyjallib/ConfigFiles/namingConfig.json,sha256=Ov4bbVJb6qodPaooU63e11YUMGXXPWFAA4AQq1sLBYU,1486
10
- pyjallib/max/__init__.py,sha256=sLF07So7OcCccPOWixSjIkAhuye37-buK-1I5Hp8F4U,1537
10
+ pyjallib/max/__init__.py,sha256=UCvMt5FFWYQz2-ifPyI4ExwqxpPHx5WJEUDREW-QLwc,1649
11
11
  pyjallib/max/align.py,sha256=HKjCViQCuicGmtvHB6xxVv4BEGEBGtV2gO3NvR_6R2A,5183
12
- pyjallib/max/anim.py,sha256=QTpR8T047IMpV40wnMMNo080wY9rHMV9k7ISrh4P61I,26083
13
- pyjallib/max/autoClavicle.py,sha256=Iga8bWUhRabfFePObdwGJSshp5Gww1Jv1Hwcul_y0V4,9559
14
- pyjallib/max/bip.py,sha256=kEneUnYph_GQXY1oojmzpJOV9bAYNBKagE0Ht3EGZ7o,30199
15
- pyjallib/max/bone.py,sha256=XVtX6e5UbMcGaOqz5UeoMEpQNMfbyQWDNM-UuS1CCUA,50019
16
- pyjallib/max/boneChain.py,sha256=weuOmGk7Y7i-0QNCr7G2hRPOecb5xmErshqpmXtikI0,5660
12
+ pyjallib/max/anim.py,sha256=scfxLSSXfFAlxgFi_qePrbONYc4mpKB8mXIxtYtFtl0,29032
13
+ pyjallib/max/autoClavicle.py,sha256=m5rs313-qkaHfs9TUr98HYRVFdB25E_fW-YiY4o9zhE,9559
14
+ pyjallib/max/bip.py,sha256=uhnNwt9EOP1ezPEBf7uIAqveBh3afzXbgr2ydJzTo0U,31471
15
+ pyjallib/max/bone.py,sha256=ImFUqdUoKeXD44rYsccJdgLClbqazraGmsv66I-tyGI,52923
16
+ pyjallib/max/boneChain.py,sha256=qTvbnJVuBchOdOBhi8wPxPClQ-5Wbkhc7Y2H47nUjCc,5740
17
17
  pyjallib/max/constraint.py,sha256=93g-X0aZHtZMKXVKr8xMjIhbKou61yc2b3ubQKJquBs,40589
18
- pyjallib/max/groinBone.py,sha256=nLR8rgWl6Vt4qQPZKY_jDgTRNF9RCnst49iR2JdWEMs,9144
19
- pyjallib/max/header.py,sha256=nuNCVfm5bfYMS0KxB8IRR67D30CXXHRUXHfFYkLG0jU,4120
18
+ pyjallib/max/fbxHandler.py,sha256=rVcnxZh5_Cu012wKIFgiGO_XvZF07Oy3jezxUhIpmSo,8703
19
+ pyjallib/max/groinBone.py,sha256=yBexwDTrnXViP8DRACIMnnpJWErbn9RIA61_d3iADCc,9193
20
+ pyjallib/max/header.py,sha256=miL-dCC1z1syugn9_L-DN0HbdPwTdcvJS0e_UlT8H5A,4199
20
21
  pyjallib/max/helper.py,sha256=Na3jFRwLsjHh4rz0Tk_r_CwHQxOA6n8LhDRA9x5xcSk,18018
21
- pyjallib/max/hip.py,sha256=OXMS_bBJUYVKT-aZoJ2YCbbS9eQStwMOkXfA22t-PKw,12696
22
- pyjallib/max/kneeBone.py,sha256=P5vDX1MFMbDy4DEI1LsT05a2Z62rqgr_FqGBxl2yEeY,24397
23
- pyjallib/max/layer.py,sha256=e9Mn8h7xf0oBYST3QIpyBpLMl8qpWTExO9Y6yH6rKc0,8795
22
+ pyjallib/max/hip.py,sha256=RavoZgK7zP2sXDa4A8CXEbHB6g8MQ-604XryZbStx0E,12684
23
+ pyjallib/max/kneeBone.py,sha256=cs5bCZtHxgLf6u80er1rV_PBF_SizqRgcTjYmy1q3IM,25316
24
+ pyjallib/max/layer.py,sha256=luDAzWzXXPiRq7v5lm-xv0Vd8meEEe7HSoX6ZoKpvu0,8825
24
25
  pyjallib/max/link.py,sha256=J3z9nkP8ZxAh9yYhR16tjQFCJTCYZMSB0MGbSHfA7uI,2592
25
26
  pyjallib/max/mirror.py,sha256=TcbfZXSk-VJQstNqAmD6VGCqYBF9bMuJtFTg-6SiGdQ,14505
27
+ pyjallib/max/mocap.py,sha256=tD0yhan8GBYLBZtdpGBlYC-DzdQkT4gsbqeo5lpyQXU,12544
26
28
  pyjallib/max/morph.py,sha256=I8HRYx4NznL6GZL4CbT9iTv05SeaBW_mJJ4PzMxCBkw,12664
27
29
  pyjallib/max/name.py,sha256=DcJt2td-N7vfUGyWazdGTD4-0JW-noa7z5nwc6SHm6I,15337
30
+ pyjallib/max/rootMotion.py,sha256=H9kvh9dx4zLLBYuigGyN0XZsdGAF5d7YRBdaokZmCiw,31399
28
31
  pyjallib/max/select.py,sha256=HMJD2WNX3zVBEeYrj0UX2YXM3fHNItfw6UtQSItNsoU,9487
29
32
  pyjallib/max/skin.py,sha256=5mBzG2wSUxoGlkFeb9Ys8uUxOwuZRGeqUMTI9LiWWZU,41937
30
- pyjallib/max/twistBone.py,sha256=3fs8EzRH-TTt2Oypm4LGoR9QtNcno9Fe1OV5_gA9p4U,17049
31
- pyjallib/max/volumeBone.py,sha256=SfmxxqmozcDinRtfPsjdNOPDMcqDkHJoquaZZOLrn1g,14649
33
+ pyjallib/max/toolManager.py,sha256=ya6beAGzk1_culw4H7lBdr7klnS-Yl8mVGg13A41Q8A,3185
34
+ pyjallib/max/twistBone.py,sha256=VPZLDE5V6KEW-i5gAberggEeR2YRzLwsFfFQBT4AB1U,17295
35
+ pyjallib/max/volumeBone.py,sha256=xt58rhMeA1YQQl6_y3GPikYLJXTfyoASSsnT0Nt285E,14776
32
36
  pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json,sha256=PBUYawCELG0aLNRdTh6j-Yka4eNdmpF4P8iRyp0Ngpg,3717
33
37
  pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json,sha256=PBUYawCELG0aLNRdTh6j-Yka4eNdmpF4P8iRyp0Ngpg,3717
34
38
  pyjallib/max/macro/jal_macro_align.py,sha256=_Iqwskz0i4AVlP_AhDrqHwhLml6zoDWkVAxPF3AKqEQ,4286
@@ -38,6 +42,6 @@ pyjallib/max/macro/jal_macro_helper.py,sha256=hd8e5x56aq7Qt0g-hP5bY0p-njVy8ja77_
38
42
  pyjallib/max/macro/jal_macro_link.py,sha256=E8i3z2xsrQiGDEz4Qoxc75hkpalzS95mOMcIic0J-Fc,1193
39
43
  pyjallib/max/macro/jal_macro_select.py,sha256=jeSFR_mqqudTTrZE1rU6qifJ4g441cYxXWcHPTWh1CU,2289
40
44
  pyjallib/max/ui/Container.py,sha256=QSk3oCqhfiR4aglSSkherRGAyPFqMRUt83L-0ENBz-s,5571
41
- pyjallib-0.1.12.dist-info/METADATA,sha256=0jeCReJS2g2RlmWhefBDeSPwlIoFDturmBEqrw_s3Y8,870
42
- pyjallib-0.1.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- pyjallib-0.1.12.dist-info/RECORD,,
45
+ pyjallib-0.1.13.dist-info/METADATA,sha256=8CqMSZqqGuOjrwZtXYCzOt5f87-YXJMKoUSoAaAGRvs,870
46
+ pyjallib-0.1.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
+ pyjallib-0.1.13.dist-info/RECORD,,