acombo3d.exe

  href="../../../../style.css" type="text/css" rel="stylesheet" />  


进程知识库

acombo3d - acombo3d.exe - 进程信息 name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-5572165936844014&dt=1195292630218&lmt=1195292627&format=336x280_as&output=html&correlator=1195292630218&url=file%3A%2F%2F%2FC%3A%2FDocuments%2520and%2520Settings%2Flhh1%2F%E6%A1%8C%E9%9D%A2%2F000stthk.exe.htm&color_bg=FFFFFF&color_text=000000&color_link=000000&color_url=FFFFFF&color_border=FFFFFF&ad_type=text&ga_vid=1397507768.1195292630&ga_sid=1195292630&ga_hid=1390572954&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=32&u_tz=480&u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency="allowtransparency">

进程文件: acombo3d 或者 acombo3d.exe
进程名称: Acombo3dmouse
 
描述:
acombo3d.exe是鼠标驱动程序的一部分。

出品者: Acombo
属于: Mouse driver

系统进程:
后台程序:
使用网络:

硬件相关:
常见错误: 未知N/A

内存使用: 未知N/A
 
安全等级 (0-5): 0

间谍软件:
广告软件:
病毒:
木马:

其它进程列表

 Other Processes  
 00thotkey.exe 12popup.exe 2portalmon.exe
 3capplnk.exe 3cdminic.exe 3cmcnkw.exe
 3cmlink.exe 3cmlinkw.exe 3cmlnkw.exe
 3deepctl.exe 3dfxman.exe 3dldemon.exe
 3dlman.exe 3dxsrv.exe 3qdctl.exe
 a4proxy.exe absr.exe abyssws.exe
 acbtnmgr_xxx.exe accelerate.exe access.exe
 acmonitor_xxx.exe acombo3d.exe aconti.exe
 acoustic.exe acroaum.exe acrobat elements.exe
 acrodist.exe act.exe actionagent.exe
 activation.exe activeeyes.exe activemenu.exe
 activeplus.exe ad-aware.exe ad-watch.exe
 ad2kclient.exe adblck.exe adc.exe
 addestroyer.exe addrbook.exe adg.exe
 AdManCtl.exe admillikeep.exe admilliserv.exe
 admlib32.exe admunch.exe Adobe Gamma Loader.exe
 adobes.exe adp.exe adsgone.exe
 adss.exe adstatserv.exe adsub.exe
 adtray.exe advapi.exe advchk.exe
 aeiwlsta.exe aelaunch.exe aexswdusr.exe
 agfaclnk.exe agsatellite.exe ahfp.exe
 ahnsd.exe ahqinit.exe ahqtb.exe
 aiepk.exe aiepk2.exe aimaol.exe
 aimingclick.exe airplus.exe airsvcu.exe
 akiller.exe alarm.app.exe AlarmApp.exe
 alarmwatcher.exe ALCMTR.EXE ALCWZRD.EXE
 alcxmntr.exe aldaemon.exe alertserver.exe
 AlertSvc.exe almxptray.exe ALUNotify.exe
 am32.exe amoumain.exe anote.exe
 AOLServiceHost.exe  aoltpspd.exe aoltray.exe
 aoltsmon.exe aornum.exe ap9h4qmo.exe
 Apache.exe appservices.exe aptezbp.exe
 apvxdwin.exe AquariumDesktop.exe armon32.exe
 armon32a.exe arupld32.exe ashDisp.exe
 ashmaisv.exe ashserv.exe asmproserver.exe
 aspi_me.exe  aspnet_admin.exe asupport.exe
 aswupdsv.exe athoc.exe ati2mdxx.exe
 ati2plab.exe aticwd32.exe ATIDtct.EXE
 atitask.exe atwtusb.exe ausvc.exe
 autoreg.exe autorun.exe autoupdate.exe
 avgamsvr.exe avgcc.exe avgcc32.exe
 avgemc.exe avgnt.exe avguard.exe
 avpm.exe avsched32.exe avwupsrv.exe
 awhost32.exe awrem32.exe babylon.exe
 BackItUp.exe backupnotify.exe bagent.exe
 bass.exe bboy.exe bcb.exe
 bcmwltry.exe bdmcon.exe bdss.exe
 bearshare.exe benetns.exe bengine.exe
 benser.exe beremote.exe beserver.exe
 bigfix.exe billmind.exe bkupexec.exe
 blocker.exe bmupdate.exe boot.exe
 bp.exe bpftp.exe bpk.exe
 bpsinstall.exe brad32.exe brmecom.exe
 bsoft.exe btstackserver.exe bttray.exe
 bwgo0000 cagent.exe cagent32.exe
 calcheck.exe CAP2LAK.EXE CAP2RSK.EXE
 CAP2SWK.EXE capfax.exe cc.exe
 cclaw.exe ccmagent.exe cconnect.exe
 ccsetmgr.exe cd_load.exe cdf.exe
 cdspeed.exe ceekey.exe cepmtray.exe
 cgtask.exe chkadmin.exe chkras.exe
 choke.exe cij3p2ps.exe client.exe
 clipsrv.exe clonecdtray.exe cmgrdian.exe
 cmluc.exe cnqmax.exe CnxMon.exe
 comctl_32.exe comet.exe command.exe
 compaq-rba.exe conf.exe config.exe
 conmgr.exe ConquerCam.exe consol32.exe
 cool.exe CoverDesigner.exe cpdclnt.exe
 cpqa1000.exe cpqdfwag.exe cpqeadm.exe
 cpqinet.exe Cpqset.exe cpunumber.exe
 createcd.exe createcd50.exe crsss.exe
 csinject.exe csinsm32.exe csinsmnt.exe
 Csrrs.exe csrsc.exe csrss32.exe
 ct_load.exe ctbclick.exe ctdvddet.exe
 cteaxspl.exe ctfmon32.exe ctrlvol.exe
 ctsrreg.exe ctsysvol.exe cusrvc.exe
 cuteftp.exe cutftp.exe cyb2k.exe
 cygrunsrv.exe cz.exe d4.exe
 daconfig.exe daemon.exe datalayer.exe
 ddhelper32.exe de_serv.exe defscangui.exe
 delldmi.exe dellmmkb.exe delmsbb.exe
 desk98.exe DeskAdKeep.exe DeskAdServ.exe
 dexplore.exe diagent.exe dialer.exe
 directx.exe directxset.exe dla.exe
 dlgli.exe dlt.exe dluca.exe
 dmremote.exe dmxlauncher.exe dnar.exe
 dnetc.exe dns.exe download.exe
 downloadplus.exe dragdrop.exe dreamweaver.exe
 drgtodsc.exe drivespeed.exe drvddll.exe
 drvlsnr.exe DRWTSN16.EXE dsagnt.exe
 dseraser.exe dslagent.exe dslmon.exe
 dsnthapp.exe dsnthser.exe dvdlauncher.exe
 DVDRegionFree.exe dvldr32.exe dvremind.exe
 DWHeartbeatMonitor.exe DxDebugService.exe DXEnum.exe
 dxnf.exe e-s0bic1.exe e_s0hic1.exe
 e_srcv03.exe eabservr.exe EasyAV.exe
 ebrr.exe edisk.exe edonkey.exe
 ee.exe ehmsas.exe ehrec.exe
 ehSched.exe ehshell.exe ehtray.exe
 elbycheck.exe elccest.exe emule.exe
 enbiei.exe encmontr.exe engutil.exe
 ensmix32.exe enternet.exe essdc.exe
 eudora.exe eusexe.exe EvtEng.exe
 expl32.exe explorer32.exe explorere.exe
 express.exe exshow95.exe ezejmnap.exe
 ezulumain.exe fameh32.exe fan.exe
 farmmext.exe fastdown.exe faxsvc.exe
 fbdirect.exe fc.exe fch32.exe
 fgadmin.exe fih32.exe finder.exe
 flashfxp.exe flashksk.exe flatbed.exe
 fnrb32.exe FONTVIEW.EXE forte.exe
 fpdisp4.exe fpxpress.exe frameworkservic.exe
 freedom.exe frontpage.exe frsk.exe
 fs20.exe fsaa.exe fsav32.exe
 fsbwlan.exe fsdfwd.exe fsg.exe
 fsg_3202.exe fsgk32.exe fsgk32st.exe
 fsm32.exe fsma32.exe fsmb32.exe
 fsscrctl.exe fssm32.exe fsw.exe
 ftpte.exe fts.exe fwenc.exe
 fxredir.exe gah95on6.exe gain_trickler_3202.exe
 gbtray.exe gcASCleaner.exe gcasDtServ.exe
 gcasInstallHelper.exe gcASNotice.exe gcasServ.exe
 gcasServAlert.exe gcasSWUpdater.exe gdonkey.exe
 gesfm32.exe gfxacc.exe Ghostexp.exe
 GHOSTS~2.EXE ghoststartservice.exe ghoststarttrayapp.exe
 giantantispywaremain.exe GIANTAntiSpywareUpdater.exe gnetmous.exe
 gnotify.exe go.exe GoogleDesktop.exe
 gozilla.exe gra.exe graph.exe
 GrpWise.exe gsicon.exe gstartup.exe
 gtwatch.exe gwmdmpi.exe gwsystemservice.exe
 hcontrol.exe helpexp.exe HelpHost.exe
 helpsvc.exe hhw.exe hidden32.exe
 hjym.exe hkserv.exe hkss.exe
 hkwnd.exe hotkeyapp.exe hotsync.exe
 hottray.exe hpbpro.exe hpdrv.exe
 hphmon03.exe hphmon04.exe hphmon06.exe
 hphupd04.exe hphupd05.exe hphupd06.exe
 hpnra.exe hpobnz08.exe hpoddt01.exe
 hpodev07.exe hpoevm07.exe hpohmr08.exe
 hpoopm07.exe hposol08.exe hpqcmon.exe
 hpqgalry.exe hpsjvxd.exe hpwuschd.exe
 HPWuSchd2.exe hpzstatn.exe hpztsb03.exe
 hpztsb09.exe hpztsb10.exe htmdeng.exe
 hypertrm.exe i8kfangui.exe iaanotif.exe
 iaantmon.exe ibmpmsvc.exe iconfig.exe
 icqlite.exe icsmgr.exe icwconn2.exe
 icwtutor.exe iexpiore.exe iexplore32.exe
 iFrmewrk.exe  igfxsrvc.dll ImageDrive.exe
 IMApp.exe IMEKRMIG.EXE imjpmig.exe
 imonnt.exe imontray.exe imscinst.exe
 incd.exe InCDsrv.exe IncMail.exe
 incredimail.exe inetd32.exe InfoTool.exe
 inicio.exe initsdk.exe inotask.exe
 IntelMEM.exe internet.exe ipclient.exe
 ipssvc.exe ireike.exe isignup.exe
 islp2sta.exe ismserv.exe isstart.exe
 itouch.exe iwctrl.exe ixapplet.exe
 JAMMER2ND.EXE javaws.exe jetcar.exe
 jucheck.exe jushed.exe jushed32.exe
 kavsvc.exe kazaalite.exe KB891711.EXE
 kencapi.exe kencli.exe kencron.exe
 kendns.exe kenftpgw.exe keninet.exe
 kenmail.exe kenmap.exe kenproxy.exe
 kenserv.exe kensocks.exe kentbcli.exe
 kernal32.exe keyhook.exe keylogger.exe
 keyword.exe KHALMNPR.exe khooker.exe
 kmw_run.exe kodakccs.exe kodakimage.exe
 kodakprv.exe kodorjan.exe kpf4gui.exe
 lao.exe launch.exe launchap.exe
 launcher.exe launchpd.exe leerlaufprozess
 lexplore.exe lexstart.exe lights.exe
 lmgrd.exe lmpdpsrv.exe load32.exe
 logitray.exe logon.exe lorena.exe
 LSAS.exe Lsass32.exe lsassa.exe
 lsasss.exe lsserv.exe ltcm000c.exe
 ltdmgr.exe ltmoh.exe ltmsg.exe
 lxdboxcp.exe main.exe mainserv.exe
 manager.exe mapiicon.exe master.exe
 matcli.exe mathchk.exe mbm4.exe
 mbm5.exe mc.exe mcagent.exe
 mcappins.exe mcdlc.exe McEPOC.exe
 McEPOCfg.exe mcinfo.exe mcmnhdlr.exe
 mcpalmcfg.exe mcpserver.exe mcupdate.exe
 mcvsshld.exe McWCE.exe McWCECfg.exe
 mediaaccess.exe MediaAccK.exe mediaman.exe
 mediapass.exe mediapassk.exe members-area.exe
 memorymeter.exe messenger.exe mgactrl.exe
 mgaqdesk.exe mgasc.exe mgavrtcl.exe
 mgui.exe mhotkey.exe microsoft.exe
 mim.exe minibug.exe minilog.exe
 mirc.exe MIRC32.exe mm_server.exe
 mmdiag.exe mmtray.exe mmtray2k.exe
 mmtraylsi.exe mmups.exe mmusbkb2.exe
 mnsvc.exe mnyexpr.exe monitor.exe
 monitr32.exe morpheus.exe moviemk.exe
 movieplace.exe Mozilla.exe mp3serch.exe
 mpbtn.exe mpf.exe mpfagent.exe
 mpfservice.exe mpftray.exe mpservic.exe
 mpsetup.exe mqtgsvc.exe msaccess.exe
 msams.exe msc32.exe mscifapp.exe
 mscnsz.exe mscommand.exe msconfig32.exe
 mscvb32.exe MSD.EXE mse7.exe
 msg32.exe msgloop.exe msgplus.exe
 mskagent.exe msmgs.exe msndc.exe
 MSNIASVC.EXE msoffice.exe mspmspv.exe
 mspub.exe msqry32.exe msscli.exe
 mssearch.exe msstat.exe mssvr.exe
 mstore.exe MSupdate.exe msvcmm32.exe
 mtx.exe muamgr.exe musirc4.71.exe
 mwd.exe mxoaldr.exe mxtask.exe
 myfastupdate.exe mysqld-nt.exe nail.exe
 naimag32.exe navapp.exe nbj.exe
 nbr.exe nclaunch.exe nddeagnt.exe
 NDSTray.exe neo.exe neoCapture.exe
 neoCopy.exe neoDVD.exe neoDVDstd.exe
 neotrace.exe nero.exe nerosmartstart.exe
 nerosvc.exe netmail.exe  netsurf.exe
 newdot.exe newsupd.exe ngctw32.exe
 nilaunch.exe NIP.exe nipsvc.exe
 NJeeves.exe nkvmon.exe noads.exe
 notify.exe npfmntor.exe NPFMSG.exe
 npscheck.exe npssvc.exe NRMENCTB.exe
 nscheck.exe nsl.exe NSMdtr.exe
 NSMdtr.exe nsvr.exe nsvsvc.exe
 nt_usdm.exe ntaskldr.exe ntfrs.exe
 ntmulti.exe ntrtscan.exe NVCOAS.exe
 NVCPL.EXE NVCSched.exe nvmctray
 nvsvc.exe nwtray.exe Nymse.exe
 ocxdll.exe oeloader.exe ois.exe
 Olehelp.exe Omniserv.exe onetouch.exe
 oodag.exe opera.exe opware12.exe
 owmngr.exe P2P Networking2.exe P2P Networking3.exe
 pacis.exe PACKAGER.EXE packethsvc.exe
 pav.exe pavfires.exe pavsrv51.exe
 pcard.exe pccclient.exe pccguide.exe
 pccnt.exe pccntmon.exe pccntupd.exe
 PcCtlCom.exe pcfmgr.exe pchbutton.exe
 pcscan.exe PCTVoice.exe pdsched.exe
 PDVDServ.exe persfw.exe pg_ctl.exe
 pgptray.exe phbase.exe photoshop.exe
 picsvr.exe pinball.exe pkjobs.exe
 plauto.exe player.exe plguni.exe
 pmr.exe pmxinit.exe pop3pack.exe
 popupkiller.exe postgres.exe postmaster.exe
 pow.exe powerdvd.exe powerkey.exe
 powerpnt.exe powers.exe ppmemcheck.exe
 pptd40nt.exe pptview.exe ppwebcap.exe
 pqhelper.exe PQIBrowser.exe PQIMountSvc.exe
 pqinit.exe pqtray.exe PQV2ISECURITY.EXE
 pqv2isvc.exe printnow.exe PRISMSTA.EXE
 PRISMSVR.EXE profiler.exe proflwiz.exe
 pruttct.exe psdrvcheck.exe psimsvc.exe
 pssvc.exe pts.exe ptssvc.exe
 pull.exe PureVoice.exe pvlsvr.exe
 qbdagent2002.exe qbupdate.exe qclean.exe
 qconsvc.exe qctray.exe qcwlicon.exe
 qdcsfs.exe Qoeloader.exe qtaet2s.exe
 qtask.exe qtzgacer.exe QuickBooks
 quickdcf.exe quickres.exe quicktimeplayer.exe
 qvp32.exe Radio.exe RadioSvr.EXE
 randomdigits.exe rapapp.exe rasman.exe
 RAVMOND.exe rcapi.exe rds.exe
 reader_sl.exe realjbox.exe realmon.exe
 realpopup.exe realshed.exe register.exe
 regloadr.exe regsrv.exe RegSrvc.exe
 regsvc32.exe remind.exe Remind_XP.exe
 remind32.exe reminder.exe removed.exe
 remupd.exe retrorun.exe rftray.exe
 rlid.exe RM_SV.exe  rosnmgr.exe
 rsrcmtr.exe rtlrack.exe rtmanager.exe
 rtmc.exe rtmservice.exe rtos.exe
 rtvscn95.exe runservice.exe s24evmon.exe
 s3tray2.exe sage.exe saimon.exe
 saproxy.exe sbdrvdet.exe sbserv.exe
 sbsetup.exe scanexplicit.exe ScanMailOutLook.exe
 scanregistry.exe scanserver.exe scards32.exe
 scardsvr32.exe scbar.exe sccenter.exe
 scchost.exe SCHDPL32.EXE schedhlp.exe
 schedul2.exe scheduler.exe schedulerv2.exe
 schost.exe schupd.exe scm.exe
 scrfs.exe scsiaccess.exe sdii.exe
 sdstat.exe se.exe searchnav.exe
 searchnavversion.exe sentstrt.exe service5.exe
 sessmgr.exe sethook.exe seti@home.exe
 setlang.exe sgbhp.exe sgmain.exe
 shadowbar.exe sharedprem.exe shell32.exe
 shine.exe shpc32.exe shstart.exe
 shwicon.exe silent.exe SIMETER.EXE
 sistray.exe sisusbrg.exe sixtypopsix.exe
 ska.exe skynetave.exe Skype.exe
 slee401.exe sllights.exe slpv24s.exe
 slserv.exe sm56hlpr.exe smagent.exe
 smartagt.exe SmartExplorer.exe SmartFTP.exe
 SMax4.exe SMax4PNP.exe SMceMan.exe
 smlogsvc.exe smOutlookPack.exe sms.exe
 smsmon32.exe smsss.exe smsx.exe
 smtray.exe sndrec32.exe sniffer.exe
 snmptrap.exe soffice.exe sointgr.exe
 SonicStageMonitoring.exe soundtrax.exe spamsub.exe
 SpamSubtract.exe SPBBCSvc.exe speedmgr.exe
 speedupmypc.exe splash.exe spool.exe
 spoolsrv.exe SpoolSvc.exe sptip.dll
 spvic.exe spyagent4.exe spyblast.exe
 spybotsd.exe spybuddy.exe spysweeper.exe
 spyware.exe sqlagent.exe sqlmangr.exe
 sqlservr.exe srv32.exe ss.exe
 ssgrate.exe sshd.exe ssonsvr.exe
 sstray.exe Stacmon.exe StatusClient.exe
 supporter5.exe surveyor.exe suss.exe
 svaplayer.exe svchoost.exe svchos1.exe
 svchosl.exe svcinit.exe svcproc.exe
 svhost.exe SwiftBTN.exe swimsuitnetwork.exe
 sws.exe sxgdsenu.exe symlcsvc.exe
 SymSPort.exe synchost.exe sysagent.exe
 syscfg32.exe syscnfg.exe sysformat.exe
 syshost.exe syslog.exe sysmon.exe
 sysmonnt.exe sysreg.exe SYSsfitb.exe
 systask32l.exe systemdll.exe systime.exe
 systray32.exe Szchost.exe tapicfg.exe
 task32.exe taskbar.exe taskpanl.exe
 taskswitch.exe tbctray.exe TBMon.exe
 tbpanel.exe tc.exe termsrv.exe
 testing.exe tfncky.exe tfnf5.exe
 The Weather Channel.exe thotkey.exe timershot.exe
 timeup.exe tintsetp.exe tmksrvi.exe
 tmksrvu.exe tmpfw.exe toadimon.exe
 topdesk.exe toscdspd.exe TouchED.exe
 tp4ex.exe tp4mon.exe tp4serv.exe
 tphkmgr.exe TPONSCR.exe TpScrex.exe
 tpsmain.exe TPTray.exe tpwrtray.exe
 tray.exe trayclnt.exe traymon.exe
 traymonitor.exe traysaver.exe trayserver.exe
 tskdbg.exe tskmgr32.exe tsl2.exe
 tsp2.exe tstool.exe tv_media.exe
 twunk_64.exe uc.exe udserve.exe
 unins000.exe unsecapp.exe upd.exe
 upgrade.exe ups.exe usb.exe
 usbmmkbd.exe usbmonit.exe usrmlnka.exe
 utilman.exe v2iconsole.exe vaserv.exe
 vettray.exe vi_grm.exe videodrv.exe
 view.exe viewport.exe virtualbouncer.exe
 visio.exe vmnat.exe vmss.exe
 vobregcheck.exe vsmain.exe vsserv.exe
 vssvc.exe VzFw.exe w3dbsmgr.exe
 watch.exe watchdog.exe WaveEdit.exe
 wbload.exe wbsched.exe wbss.exe
 wbutton.exe wcesmgr.exe weather.exe
 webcamrt.exe webcolct.exe webinstall.exe
 webtrapnt.exe welcome.exe wfxctl32.exe
 wfxsnt40.exe wfxswtch.exe whagent.exe
 whSurvey.exe win32api.exe WinAce.exe
 winadm.exe winadserv.exe winadslave.exe
 Winaw32.exe winbackup.exe WinCinemaMgr.exe
 wincomm.exe wincomp.exe winctlad.exe
 WinCtlAdAlt.exe winde.exe windefault.exe
 winex.exe winfs.exe wingate.exe
 winhelp.exe winhlp32.exe winhost.exe
 wininfo.exe winkey.exe winlog.exe
 winmgm32.exe winmysqladmin.exe Winpack.exe
 winproj.exe winproxy.exe winpsd.exe
 winpup32.exe winrecon.exe winroute.exe
 wins.exe winserv.exe winservad.exe
 winservices.exe winservs.exe winservsuit.exe
 winsocks.exe winsrv32.exe winstat.exe
 winsys.exe winsys32.exe wintask.exe
 wintasks.exe winvnc.exe winwan.exe
 wiseupdt.exe wkdetect.exe wkfud.exe
 wkqkpick.exe wkscal.exe wkssb.exe
 wlansta.exe wmburn.exe wmencagt.exe
 wmexe.exe wmiapsrv.exe WMPBurn.exe
 wntsf.exe workflow.exe wp.exe
 wptel.exe wsys.exe wuloader.exe
 wupdated.exe ww.exe x10nets.exe
 x1exec.exe xcommsvr.exe xferwan.exe
 xtcfgloader.exe ymsgr_tray.exe ydownloader.exe
 YServer.exe yupdater.exe Zanda.exe
 zbase32.exe zcast.exe zClientm.exe
 ZLH.exe ZStatus.exe


Other Process Categories:
- 系统进程列表
- 存在安全风险进程列表
- 应用程序进程列表

 

 

 

©Copyright 2000-2005 小土豆。版权所有。
  

我要打包以下代码,给出打包的.spec文件 ```python import sys from PyQt5.QtWidgets import QScrollArea, QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton, QLineEdit, QLabel, QFileDialog, QRadioButton, QComboBox, QCheckBox, QGroupBox, QListWidget, QProgressBar from PyQt5.QtCore import QUrl, QRegExp, QTimer, Qt from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage from PyQt5.QtGui import QIcon, QIntValidator, QRegExpValidator from PyQt5.QtWebChannel import QWebChannel # 导入 QWebChannel import configparser import os import subprocess from PyQt5.QtWidgets import QMessageBox import json import time import docker import ctypes import logging import tkinter as tk from tkinter import messagebox import threading import psutil import shutil import 文件服务2 from 服务校验 import validate_service # 外部函数 sys.path.append(os.path.dirname(__file__)) # 添加当前文件的目录到路径 # import 连接数据库添加历史倾斜摄影 # 直接 if getattr(sys, 'frozen', False): # 如果是打包后的exe,使用sys.executable base_dir = os.path.dirname(sys.executable) else: # 如果是普通脚本,使用__file__ base_dir = os.path.dirname(os.path.abspath(__file__)) # 将'\'替换为'/' base_dir = base_dir.replace('\\', '/') # 将第一个字母盘符写为大写 base_dir = base_dir[0].upper() + base_dir[1:] print('文件路径',base_dir) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.task_started = False # 标志任务是否开始 self.current_block = 1 # 当前处理的块号 self.setWindowTitle("实时三维") self.setGeometry(100, 100, 1366, 768) self.setWindowIcon(QIcon('./icon.ico')) self.process = None # 用于存储子进程的引用 check_docker_running() # 检查docker是否运行 # 进度条状态变量 self.progress_phase = 'idle' # 状态:'idle', 'auto_grow', 'immediate_complete', 'waiting', 'completed' self.phase_start_time = None # 当前阶段开始的时间 self.auto_grow_target = 95 # 自动增长目标值(95%) self.auto_grow_duration = 240 # 自动增长持续时间(240秒=4分钟) self.immediate_complete_duration = 5 # 立即完成持续时间(5秒) self.waiting_duration = 3 # 等待时间(3秒) self.all_images_detected = False # 是否检测到allimages=1 self.start_value = 0 # 用于记录进入immediate_complete状态时的进度值 self.block_start_times = {} # 存储每个块开始的时间 self.block_durations = {} # 存储每个块的实际持续时间 self.imgs_detected = False # 是否检测到imgs.ini中的isimgs=1 self.node_process = None # 用于存储 node 进程的引用 self.start_node_process() # 启动 node 进程 # 初始化变量存储路径和选择值 self.paths = { "project": base_dir, "sensor": "", "1": "", "texture": os.path.join(base_dir, "ModelingScope.kml"), "localtions":'' } self.command_line = "" self.data_source = "影像" self.current_progress_key = None # 主窗口布局 main_layout = QHBoxLayout() # 左侧控件布局 left_layout = QVBoxLayout() # 示例控件 self.start_button = QPushButton("开始任务") # 开始任务按钮点击事件绑定run_command方法 self.start_button.clicked.connect(lambda:self.run_command(self.command_line)) left_layout.addWidget(self.start_button) # 只准点击一次开始任务按钮,点击后状态显示停止任务 # stop_button = QPushButton("停止任务") # stop_button.clicked.connect(lambda:self.stopTask()) # left_layout.addWidget(stop_button) # left_layout.addWidget(QLabel("空闲")) # 项目工程路径 # self.createPathInput(left_layout, "项目工程路径:", "project") stop_button2 = QPushButton("文件传输") stop_button2.clicked.connect(lambda:self.file_transfer()) left_layout.addWidget(stop_button2) # 平台上传 stop_button3 = QPushButton("上传平台") stop_button3.clicked.connect(lambda:self.file_transfer_to_platform()) left_layout.addWidget(stop_button3) # 任务队列路径 self.createPathInput(left_layout, "任务队列路径:", "sensor") # 图片文件路径 self.createPathInput(left_layout, "图片文件路径:", "localtions") # # 相机文件路径 # self.createPathInput_file(left_layout, "相机文件路径(.json):", "1", "json") left_layout.addWidget(QLabel("数据源:")) radiobuttons_layout = QHBoxLayout() radio_image = QRadioButton("影像") # radio_video = QRadioButton("视频") radio_image.setChecked(True) radio_image.toggled.connect(lambda: self.setDataSource("影像", radio_image.isChecked())) # radio_video.toggled.connect(lambda: self.setDataSource("视频", radio_video.isChecked())) radiobuttons_layout.addWidget(radio_image) # radiobuttons_layout.addWidget(radio_video) left_layout.addLayout(radiobuttons_layout) # 建模范围 # self.createPathInput_file(left_layout, "建模范围(.kml):", "text极速版ure", "kml") # 经纬度输入部分 # left_layout.addWidget(QLabel("输入建模范围:")) # self.coordinates_layout = QVBoxLayout() # # 添加一个默认的坐标输入 # for _ in range(4): # self.add_coordinate_input() # add_button = QPushButton("+") # add_button.clicked.connect(self.add_coordinate_input) # self.coordinates_layout.addWidget(add_button) # left_layout.addLayout(self.coordinates_layout) # 添加经纬度输入框 # 初始化经纬度输入框布局 self.coordinates_layout = QVBoxLayout() self.input_widgets = [] # 添加四组默认的输入框 for _ in range(4): self.add_coordinate_input() # 添加和删除按钮 button_layout = QHBoxLayout() add_button = QPushButton("+") remove_button = QPushButton("-") add_button.clicked.connect(self.add_coordinate_input) remove_button.clicked.connect(self.remove_coordinate_input) button_layout.addWidget(add_button) button_layout.addWidget(remove_button) # 滚动区域 scroll_area = QScrollArea() scroll_content = QWidget() scroll_content.setLayout(self.coordinates_layout) scroll_area.setWidget(scroll_content) scroll_area.setWidgetResizable(True) scroll_area.setFixedHeight(180) left_layout.addWidget(QLabel("输入建模范围:")) left_layout.addWidget(scroll_area) left_layout.addLayout(button_layout) # 相对航高(米) # left_layout.addWidget(QLabel("相对航高(米):")) # self.elevation_input = QLineEdit("") # left_layout.addWidget(self.elevation_input) # 地面分辨率 # left_layout.addWidget(QLabel("像元大小(um):")) # self.minphotonSize = QLineEdit("") # left_layout.addWidget(self.minphotonSize) # 建模结果文件名 left_layout.addWidget(QLabel("建模结果文件名:")) self.result_file_name = QLineEdit("") left_layout.addWidget(self.result_file_name) self.result_file_name.textChanged.connect(self.update_json) # 建模精度 left_layout.addWidget(QLabel("建模精度:")) self.precision_combo = QComboBox() self.precision_combo.addItem("快速") self.precision_combo.addItem("普通") self.precision_combo.addItem("精细") left_layout.addWidget(self.precision_combo) # 高级设置 advanced_group = QGroupBox("高级设置") advanced_layout = QVBoxLayout() advanced_layout.addWidget(QLabel("图片集大小(张):")) self.tile_size_input = QLineEdit("20") self.tile_size_input.setValidator(QIntValidator()) # 限制输入为整数 advanced_layout.addWidget(self.tile_size_input) advanced_layout.addWidget(QLabel("照片获取等待时间(秒):")) self.interval_input = QLineEdit("10") self.interval_input.setValidator(QIntValidator()) # 限制输入为整数 advanced_layout.addWidget(self.interval_input) advanced_layout.addWidget(QLabel("边飞边建:")) self.checkbox1 = QRadioButton("正射") self.checkbox2 = QRadioButton("倾斜") self.checkbox3 = QRadioButton("全建") self.checkbox1.setChecked(False) self.checkbox2.setChecked(True) self.checkbox3.setChecked(False) # advanced_layout.addWidget(checkbox1) # advanced_layout.addWidget(checkbox2) advanced_layout.addWidget(self.checkbox1) advanced_layout.addWidget(self.checkbox2) advanced_layout.addWidget(self.checkbox3) # 按钮 # Merge_models = QPushButton("合并模型") # connection = 连接数据库添加历史倾斜摄影.create_connection("localhost", "root", "ysxx_0407H123", "product-backsage2") # Merge_models.clicked.connect(lambda:连接数据库添加历史倾斜摄影.query_users(connection)) # advanced_layout.addWidget(Merge_models) advanced_group.setLayout(advanced_layout) left_layout.addWidget(advanced_group) # 实时状态显示 self.status_group = QGroupBox("实时状态") status_layout = QVBoxLayout() self.file_count_label = QLabel("文件数量: 0") self.file_names_list = QListWidget() self.docker_count_label = QLabel("引擎数量: 0") # 添加进度条和文字标签 self.progress_stage_label = QLabel("进度阶段: 等待开始") self.progress_bar = QProgressBar() self.progress_bar.setAlignment(Qt.AlignCenter) self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) status_layout.addWidget(self.file_count_label) status_layout.addWidget(self.file_names_list) status_layout.addWidget(self.docker_count_label) status_layout.addWidget(self.progress_stage_label) status_layout.addWidget(self.progress_bar) self.status_group.setLayout(status_layout) left_layout.addWidget(self.status_group) # 设置左侧宽度 left_widget = QWidget() left_widget.setLayout(left_layout) left_widget.setFixedWidth(300) # 创建Web引擎视图 self.web_view = QWebEngineView() self.web_view.load(QUrl("http://localhost:3123")) web_channel = QWebChannel(self.web_view.page()) # 创建 WebChannel 实例 self.web_view.page().setWebChannel(web_channel) # 关联 WebChannel self.web_view.page().runJavaScript("setInterval(function(){location.reload()}, 1000);") # 将左侧控件和Web视图添加到主布局 main_layout.addWidget(left_widget) main_layout.addWidget(self.web_view) # 设置主widget central_widget = QWidget() central_widget.setLayout(main_layout) self.setCentralWidget(central_widget) # 定时更新状态 self.timer = QTimer() self.timer.timeout.connect(self.update_status) self.timer.start(1000) # 每秒更新一次状态 # 启动进度条更新循环 self.update_progress_timer = QTimer() self.update_progress_timer.timeout.connect(self.update_progress_bar) self.update_progress_timer.start(1000) # 每秒检查一次进度 def update_status(self): # 更新文件数量和文件名 folder_path = self.paths["sensor"] if os.path.exists(folder_path): files = os.listdir(folder_path) self.file_count_label.setText(f"文件数量: {len(files)}") self.file_names_list.clear() for file_name in files: self.file_names_list.addItem(file_name) else: self.file_count_label.setText("文件数量: 0") self.file_names_list.clear() # 更新引擎数量 try: result = subprocess.run( ["docker", "ps", "--filter", "ancestor=opendronemap/odm:gpu", "--format", "{{.ID}}"], capture_output=True, text=True, shell=False, creationflags=subprocess.CREATE_NO_WINDOW # 确保不弹出窗口 ) container_ids = result.stdout.strip().split('\n') if container_ids and container_ids[0] == '': container_ids = [] # 如果结果为空字符串,将其设为空列表 logging.debug(f"Container IDs: {container_ids}") self.docker_count_label.setText(f"引擎数量: {len(container_ids)}") except subprocess.CalledProcessError as e: logging.error(f"获取Docker容器信息时出错: {e}") self.docker_count_label.setText("引擎数量: 0") except FileNotFoundError as e: logging.error(f"Docker命令未找到: {e}") self.docker_count_label.setText("引擎数量: 0") def add_coordinate_input(self): """添加一个新的经纬度输入行""" layout = QHBoxLayout() lng_input = QLineEdit() lat_input = QLineEdit() lng_input.setPlaceholderText("经度") lat_input.setPlaceholderText("纬度") # 使用正则表达式限制输入为正负加小数 validator = QRegExpValidator(QRegExp(r"^-?\d+(\.\d+)?$")) lng_input.setValidator(validator) lat_input.setValidator(validator) layout.addWidget(lng_input) layout.addWidget(lat_input) self.coordinates_layout.addLayout(layout) self.input_widgets.append((lng_input, lat_input)) # 收集经纬度坐标 coordinates = [] for lng_input, lat_input in self.input_widgets: lng = lng_input.text() lat = lat_input.text() print(lng, lat) if lng and lat: coordinates.append(f"{lng},{lat}") # 将所有经纬度坐标合并为一个字符串,用分号分隔 coordinates_str = ";".join(coordinates) print("经纬度",coordinates_str) # 将经纬度加入kml文件 def generate_kml(self): """生成KML内容""" kml_content = f"""<?xml version="1.0" encoding="utf-8"?> <kml xmlns="http://www.opengis.net/kml/2.2"> <Document> <Schema id="Dataset_203sup" name="Dataset_203sup"> <SimpleField name="SmUserID" type="int"/> </Schema> <Folder> <Placemark> <name/> <ExtendedData> <SchemaData schemaUrl="#Dataset_203sup"> <SimpleData name="SmUserID">0</SimpleData> </SchemaData> </ExtendedData> <Polygon> <outerBoundaryIs> <LinearRing> <coordinates> """ coordinates = [] for lng_input, lat_input in self.input_widgets: lng = lng_input.text() lat = lat_input.text() if lng and lat: coordinates.append(f"{lng},{lat}") # 在最后再添加第一个坐标点 if coordinates: coordinates.append(coordinates[0]) kml_content += "\n".join(coordinates) + "\n </coordinates>\n </LinearRing>\n </outerBoundaryIs>\n </Polygon>\n </Placemark>\n </Folder>\n </Document>\n</kml>" # 写入KML文件 kml_path = self.paths["texture"] with open(kml_path, 'w', encoding='utf-8') as kml_file: kml_file.write(kml_content) print("KML文件已写入:", kml_path) def remove_coordinate_input(self): """删除最下面的一个经纬度输入行""" if len(self.input_widgets) > 4: layout = self.coordinates_layout.takeAt(len(self.input_widgets) - 1) for i in range(layout.count()): widget = layout.itemAt(i).widget() if widget: widget.deleteLater() self.input_widgets.pop() else: QMessageBox.warning(self, "提示", "至少需要保留四组坐标。") def start_node_process(self): """启动 node 进程并保存其引用""" self.node_process = subprocess.Popen('node result_server_logs.js', shell=False, creationflags=subprocess.CREATE_NO_WINDOW) def update_json(self): # 获取建模结果文件名 result_filename = self.result_file_name.text() # 创建要写入的 JSON 数据 data = { "name": result_filename } # 写入 JSON 文件 with open('./result/resultName.json', 'w', encoding='utf-8') as json_file: json.dump(data, json_file, ensure_ascii=False, indent=4) def create_config_file(self,): """生成配置文件 config.ini""" config = configparser.ConfigParser() # 裁剪相机文件目录,若裁剪失败,弹窗提示用户重新选择文件 if self.paths["1"] is not None and self.paths["project"] is not None: project_path = self.paths["project"] + "/" if project_path in self.paths["1"]: self.paths["1"] = self.paths["1"].replace(project_path, "") # else: # QMessageBox.warning(self, "提示", "项目路径未包含在相机文件路径中。") else: # QMessageBox.warning(self, "提示", "路径不能为空。") print("路径不能为空。") self.paths["1"] = self.paths["1"].replace(self.paths["project"]+"/", "") config['settings'] = { 'kmlPath': self.paths["texture"], # 建模范围 # 'elevation': self.elevation_input.text(), # 相对航高 'precision': self.precision_combo.currentText(), # 建模精度 'interval': '2.0', # 照片获取等待时间 'imagesnum': self.tile_size_input.text(), # 每多少张图片生成一个文件夹 'imagesFolderPath':self.paths["project"]+'/'+ self.paths["sensor"], # 图像文件夹路径 # 'minphotonSize':self.minphotonSize.text(), # 地面分辨率 'projectPath':self.paths["project"], # 项目工程路径 'taskQueuePath':self.paths["sensor"], # 任务队列路径 'cameraFile':self.paths["1"], # 相机文件路径 'dataSource':self.data_source, # 数据源 'customTileSize':self.tile_size_input.text(), # 自定义瓦片大小 'imagesWaitTime':self.interval_input.text(), # 照片获取等待时间 'DEM':False, # 是否生成DEM 'DSM':False, # 是否生成DSM 'zhengshe':self.checkbox1.isChecked(), # 是否生成正射图 'qingxie':self.checkbox2.isChecked(), # 是否生成倾斜图 'quanjian':self.checkbox3.isChecked(), # 是否生成全建图 'minImagesPerSquare':4 , #建模一块所需数量 'resultNameDir':'result/MODEL/'+self.result_file_name.text(), # 建模结果文件名 "overlapRatio":0.7, #重叠率 "sideOverlapRatio":0.8, #侧重叠率 "localtions":self.paths["localtions"], "centerpointlongitude":114.25, "centerpointlatitude":30.58, "imagesnum":0, "ziplocaltions":self.paths["project"]+'/'+ "ziplocaltions" } with open('config.ini', 'w', encoding='utf-8') as configfile: config.write(configfile) print("配置文件 config.ini 已创建。") def run_command(self, command): for lng_input, lat_input in self.input_widgets: lng = lng_input.text() lat = lat_input.text() if not lng or not lat: QMessageBox.warning(self, "提示", "请填写完整的建模经纬度范围 。") return # 检查输入框是否为空 if not self.paths["sensor"]: QMessageBox.warning(self, "提示", "任务队列路径不能为空。") return self.start_button.setEnabled(False) # 重置进度条状态 self.progress_phase = 'auto_grow' self.phase_start_time = time.time() self.current_block = 1 self.all_images_detected = False self.imgs_detected = False self.block_start_times = {} self.block_durations = {} self.progress_stage_label.setText(f"进度阶段: 等待开始") self.progress_bar.setValue(0) # 初始化imgs.ini文件 self.init_imgs_ini() # # 任务路径不能和之前的相同 # config = configparser.ConfigParser() # try: # with open('config.ini', 'r', encoding='utf-8') as configfile: # config.read_file(configfile) # except Exception as e: # print(f"读取配置文件时出错: {e}") # # return # folder_path = config.get('settings', 'imagesFolderPath', fallback=None) # print(folder_path) # if folder_path == self.paths["project"]+"/"+self.paths["sensor"]: # QMessageBox.warning(self, "提示", "任务队列路径不能重复。") # return # 清空目标文件夹,如果存在 # destination_folder = self.paths["project"]+"/"+self.paths["sensor"] # if os.path.exists(destination_folder): # shutil.rmtree(destination_folder) # if not self.paths["camera_file"]: # QMessageBox.warning(self, "提示", "相机文件路径不能为空。") # return if not self.paths["texture"]: QMessageBox.warning(self, "提示", "建模范围路径不能为空。") return if not self.paths["localtions"]: QMessageBox.warning(self, "提示", "图片文件路径不能为空。") return if not self.result_file_name.text(): QMessageBox.warning(self, "提示", "建模结果文件名不能为空。") return # if not self.elevation_input.text(): # QMessageBox.warning(self, "提示", "相对航高不能为空。") # return # if not self.minphotonSize.text(): # QMessageBox.warning(self, "提示", "像元大小不能为空。") # return if not self.tile_size_input.text(): QMessageBox.warning(self, "提示", "自定义瓦片大小不能为空。") return if not self.interval_input.text(): QMessageBox.warning(self, "提示", "照片获取等待时间不能为空。") return self.task_started = True # 设置任务开始标志为 True self.create_config_file() # 调用生成配置文件的方法 # 将按钮状态设为不可用 self.start_button.setEnabled(False) # self.start_button.setText("正在处理...") self.start_button.repaint() self.start_button.update() # 调用文件服务每多少张图生成一个文件夹 # 直接调用 main 函数 # 创建一个新的线程来运行文件服务 file_service_thread = threading.Thread(target=文件服务2.main) file_service_thread.start() # 启动线程 self.generate_kml() # 生成KML文件 # 生成配置文件后,调用startBuilding.exe def is_exe_running(exe_name): # 检查是否有指定的exe正在运行 for proc in psutil.process_iter(['pid', 'name']): try: if proc.info['name'] == exe_name: return True except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): pass return False # 先判断是否有startbuilding.exe在运行,如果有则关闭所有docker容器,然后关闭startbuilding.exe,再启动startbuilding.exe if is_exe_running("startBuilding.exe"): # 关闭所有基于"opendronemap/odm:gpu"的容器 self.stop_docker_container("opendronemap/odm:gpu") # 关闭startBuilding.exe self.kill_exe("startBuilding.exe") self.process =subprocess.Popen("startBuilding.exe", shell=False,creationflags=subprocess.CREATE_NO_WINDOW) time.sleep(3) # 等待1秒,等待startBuilding.exe启动完成 # 刷新页面 self.web_view.reload() # 使用 QWebEngineView 的 reload 方法 # print(command) # import subprocess # subprocess.Popen(command, shell=True) def init_imgs_ini(self): """初始化imgs.ini文件""" config = configparser.ConfigParser() config['Progress'] = {'isimgs': '0'} # 初始化为0 with open('imgs.ini', 'w', encoding='utf-8') as configfile: config.write(configfile) def file_transfer(self): # 调用文件传输模块 self.process =subprocess.Popen("地表建模-三维航测数据同步.exe", shell=False,creationflags=subprocess.CREATE_NO_WINDOW) # 平台上传 def file_transfer_to_platform(self): # 调用文件传输模块 self.process =subprocess.Popen("结果保存.exe", shell=False,creationflags=subprocess.CREATE_NO_WINDOW) def kill_exe(self, exe_name): """ 杀死exe进程 :param exe_name:进程名字 :return:无 """ os.system('taskkill /f /t /im '+exe_name)#MESMTPC.exe程序名字 print("杀死进程{}".format(exe_name)) def stopTask(self): """停止任务并关闭占用端口3123的进程,同时关闭Docker容器""" # 创建进度对话框 # 创建一个新的窗口 # 显示关闭提示极速版框 msg_box = QMessageBox() msg_box.setWindowTitle("关闭提示") msg_box.setText("软件正在关闭中,请稍候...") msg_box.setStandardButtons(QMessageBox.NoButton) msg_box.setModal(True) # 模态对话框 msg_box.show() QApplication.processEvents() # 处理事件,确保对话框显示 # 关闭 node 进程 if self.node_process: # 检查 node 进程是否存在 self.node_process.kill() # 终止 node 进程 print("已关闭 node 进程") # 查找使用3123的进程 result = subprocess.run("netstat -ano | findstr :3123", capture_output=True, text=True, shell=False, creationflags=subprocess.CREATE_NO_WINDOW) lines = result.stdout.splitlines() if lines: for line in lines: if line: # 确保不是空行 parts = line.split() pid = parts[-1] # 假设最后一部分是PID if pid.isdigit(): os.system(f"taskkill /PID {pid} /F") # 强制关闭该进程 print(f"已关闭占用端口3123的进程 {pid}") # 停止 startBuilding.exe exe_name = 'startBuilding.exe' self.kill_exe(exe_name) # 循环关闭Docker容器,直到3秒没有关闭为止 stop_attempts = 0 while stop_attempts < 5: self.stop_docker_container("opendronemap/odm:gpu") # 更新进度对话框 # progress_dialog.setLabelText("正在关闭Docker容器...请稍候...") # 等待1秒 time.sleep(1) # 检查容器是否还在运行 result = subprocess.run( ["docker", "ps", "--filter", "ancestor=opendronemap/odm:gpu", "--format", "{{.ID}}"], capture_output=True, text=True ) if not result.stdout.strip(): # 如果没有运行的容器,退出循环 break stop_attempts += 1 print("任务停止完成。") # 任务停止完成后关闭对话框 msg_box.close() # 停止dockers def stop_docker_container(self, image_name): # 获取所有基于image_name的容器ID result = subprocess.run( ["docker", "ps", "-f", f"ancestor={image_name}", "-q"], capture_output=True, text=True ) container_ids = result.stdout.strip().split('\n') # 停止每个容器 for container_id in container_ids: subprocess.run(["docker", "stop", container_id]) # 所有容器都停止后,返回True,否则返回False return not container_ids # def createPathInput(self, layout, label_text, path_key): # """创建一个路径输入组件,包括标签、输入框和选择按钮""" # layout.addWidget(QLabel(label_text)) # path_layout = QHBoxLayout() # path_edit = QLineEdit(self.paths[path_key]) # path_button = QPushButton("...") # path_button.setFixedWidth(30) # path_button.clicked.connect(lambda: self.selectPath(path_edit, path_key)) # path_edit.textChanged.connect(lambda text: self.savePath(text, path_key)) # path_layout.addWidget(path_edit) # path_layout.addWidget(path_button) # layout.addLayout(path_layout) # # 选择文件路径 # def createPathInput_file(self, layout, label_text, path_key,type): # """创建一个路径输入组件,包括标签、输入框和选择按钮""" # layout.addWidget(QLabel(label_text)) # path_layout = QHBoxLayout() # path_edit = QLineEdit(self.paths[path_key]) # path_button = QPushButton("...") # path_button.setFixedWidth(30) # path_button.clicked.connect(lambda: self.setPath(path_edit, path_key,type)) # path_edit.textChanged.connect(lambda text: self.savePath(text, path_key)) # path_layout.addWidget(path_edit) # path_layout.addWidget(path_button) # layout.addLayout(path_layout) # def selectPath(self, path_edit, path_key): # """打开文件对话框,设置路径到输入框""" # folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹") # if folder_path: # path_edit.setText(folder_path) # self.savePath(folder_path, path_key) # # 打开文件对话框,选择文件路径,设置到输入框中,并保存到变量中 # def setPath(self, path_edit, path_key,type): # # file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "All Files (*)") # # 只能选json文件 # if type=="json": # file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "json Files (*.json)") # elif type=="kml": # file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "kml Files (*.kml)") # if file_path: # path_edit.setText(file_path) # self.savePath(file_path, path_key) def createPathInput(self, layout, label_text, path_key): """创建一个路径输入组件,包括标签、输入框和选择按钮""" layout.addWidget(QLabel(label_text)) path_layout = QHBoxLayout() path_edit = QLineEdit(self.paths[path_key]) path_edit.setReadOnly(True) # 设置为只读状态 path_button = QPushButton("...") path_button.setFixedWidth(30) path_button.clicked.connect(lambda: self.selectPath(path_edit, path_key)) path_edit.textChanged.connect(lambda text: self.savePath(text, path_key)) path_layout.addWidget(path_edit) path_layout.addWidget(path_button) layout.addLayout(path_layout) def createPathInput_file(self, layout, label_text, path_key, type): """创建一个路径输入组件,包括标签、输入框和选择按钮""" layout.addWidget(QLabel(label_text)) path_layout = QHBoxLayout() path_edit = QLineEdit(self.paths[path_key]) path_button = QPushButton("...") path_button.setFixedWidth(30) path_button.clicked.connect(lambda: self.setPath(path_edit, path_key, type)) path_edit.textChanged.connect(lambda text: self.savePath(text, path_key)) path_layout.addWidget(path_edit) path_layout.addWidget(path_button) layout.addLayout(path_layout) def selectPath(self, path_edit, path_key): """打开文件夹选择对话框,设置路径到输入框""" folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹", base_dir) base_path = base_dir if folder_path: # 假如path_key是localtions,则不需要判断是否在C:/RealTimeModeling下 if path_key == "localtions": path_edit.setText(folder_path) self.savePath(folder_path, path_key) else: print(folder_path,base_dir,folder_path.startswith(base_dir)) # 检查选择的目录是否在 C:/RealTimeModeling 下 if folder_path.startswith(base_dir): path_edit.setText(folder_path) self.savePath(folder_path, path_key) else: QMessageBox.warning(self, "提示", f"只能选择123 {base_path} 目录下的文件夹。") def setPath(self, path_edit, path_key, type): """打开文件对话框,选择文件路径,设置到输入框中,并保存到变量中""" base_path = base_dir if type == "json": file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", base_dir, "JSON Files (*.json)") elif type == "kml": file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", base_dir, "KML Files (*.kml)") # Check if the selected path starts with the allowed directory if file_path and file_path.startswith(base_dir): path_edit.setText(file_path) self.savePath(file_path, path_key) else: QMessageBox.warning(self, "提示", f"只能选择 {base_path} 目录下的文件夹。") def savePath(self, path, path_key): """保存路径到变量""" self.paths[path_key] = path print(self.paths) self.updateCommand() def updateCommand(self): # 原始命令 docker run -ti --rm -v C:\3d\源文件\皂角林:/datasets --gpus all opendronemap/odm:gpu --project-path /datasets 皂角林 --3d-tiles --pc-rectify --pc-ept --pc-quality low --feature-quality medium --boundary "/datasets/皂角林/boundary.json" --orthophoto-png --orthophoto-cutline line1=":/datasets --gpus all opendronemap/odm:gpu --project-path /datasets " line2= " --3d-tiles --pc-rectify --orthophoto-png --orthophoto-cutline --pc-ept --pc-quality low --feature-quality medium --boundary /datasets" # self.paths["sensor"]去掉包含 self.paths["project"] 的字符串 self.paths["sensor"] = self.paths["sensor"].replace(self.paths["project"]+"/", "") # self.paths["texture"]=self.paths["texture"].replace(self.paths["project"], "") """更新命令行""" # self.command_line = "docker run --rm -ti -v " + self.paths["project"] + ":/data/project -极速版v " + self.paths["sensor"] + ":/data/sensor -v " + self.paths["camera_file"] + ":/data/camera_file -v " + self.paths["texture"] + ":/data/texture -p 3123:3123 zyf/3dmodel:latest " + self.data_source + " " + self.elevation_input.text() + " " + self.precision_combo.currentText() + " " + self.tile_size_input.text() + " " + self.interval_input.text() + " " + " ".join([str(checkbox.isChecked()) for checkbox in [checkbox1, checkbox2, checkbox3]]) print("工程目录", self.paths["project"] , "任务队列", self.paths["sensor"], "相机文件", # self.paths["1"], "建模范围", self.paths["texture"], "数据源", self.data_source , # "相对航高", # self.elevation_input.text(), "建模精度", # self.precision_combo.currentText() , "自定义瓦片大小", self.tile_size_input.text(), "照片获取等待时间", self.interval_input.text() , "是否生成正射", self.checkbox3.isChecked() ) # " ".join([str(checkbox.isChecked()) for checkbox in [self.checkbox1, self.checkbox2, self.checkbox3]])) def setDataSource(self, source, checked): """保存数据源选择""" if checked: self.data_source = source print(self.data_source) def closeEvent(self, event): """重写关闭事件以在退出时停止任务""" self.stopTask() # 调用停止任务的方法 event.accept() # 允许窗口关闭 def update_progress_bar(self): """更新进度条状态""" if not self.task_started or self.all_images_detected: return # 读取imgs.ini文件,检查isimgs=1 config_imgs = configparser.ConfigParser() try: config_imgs.read('imgs.ini') if 'Progress' in config_imgs and 'isimgs' in config_imgs['Progress'] and config_imgs['Progress']['isimgs'] == '1': self.imgs_detected = True except Exception as e: logging.error(f"读取imgs.ini文件失败: {e}") # 如果imgs.ini中isimgs=1未出现,不更新进度条 if not self.imgs_detected: self.progress_stage_label.setText("进度阶段: 等待开始") return # 记录当前块的开始时间(如果尚未记录) if self.current_block not in self.block_start_times: self.block_start_times[self.current_block] = time.time() self.progress_stage_label.setText(f"进度阶段: 第{self.current_block}块空三运算中") # 读取Progress.ini文件 config = configparser.ConfigParser() try: config.read('./Progress.ini') except Exception as e: logging.error(f"读取进度文件失败: {e}") return # 检查allimages=1 if 'Progress' in config and 'allimages' in config['Progress'] and config['Progress']['allimages'] == '1': # 当检测到allimages=1时,进入缓慢增长到100%的状态 if not self.all_images_detected: self.all_images_detected = True self.progress_stage_label.setText("进度阶段: 三维重建结束,合并中...") # 当前进度 print("当前进度",self.progress_bar.value()) # 三秒内均匀涨到100%,每0.2秒涨10%直到100% for i in range(10): time.sleep(0.2) if self.progress_bar.value() < 100: self.progress_bar.setValue(self.progress_bar.value()+10) # 确保最终值为100% if self.progress_bar.value() != 100: self.progress_bar.setValue(100) self.progress_stage_label.setText("进度阶段: 建模完成") return # 检查当前块对应的imgs是否等于1 current_imgs_key = f'imgs{self.current_block}' if 'Progress' in config and current_imgs_key in config['Progress'] and config['Progress'][current_imgs_key] == '1': # 如果当前状态是auto_grow或idle,则切换到immediate_complete状态 if self.progress_phase in ['auto_grow', 'idle']: self.progress_phase = 'immediate_complete' self.phase_start_time = time.time() self.start_value = self.progress_bar.value() # 记录当前进度值 self.progress_stage_label.setText(f"进度阶段: 第{self.current_block}块三维重建中...") # 计算前面所有块的总持续时间 total_previous_duration = sum(self.block_durations.values()) # 状态处理 if self.progress_phase == 'idle': # 开始自动增长 self.progress_phase = 'auto_grow' self.phase_start_time = time.time() self.progress_stage_label.setText(f"进度阶段: 第{self.current_block}块空三运算中") elif self.progress_phase == 'auto_grow': if self.current_block == 1: # 第一块的目标持续时间固定为4分钟 target_duration = 240 else: # 后续块使用前面所有块总持续时间的1.5倍 target_duration = total_previous_duration * 1.5 # 整数化 target_duration = int(target_duration) print(f"当前块{self.current_block}目标持续时间为{target_duration}") elapsed = time.time() - self.phase_start_time if elapsed >= target_duration: self.progress_bar.setValue(self.auto_grow_target) else: progress = int((elapsed / target_duration) * self.auto_grow_target) self.progress_bar.setValue(progress) elif self.progress_phase == 'immediate_complete': elapsed = time.time() - self.phase_start_time if elapsed >= self.immediate_complete_duration: # 5秒到,设置100% self.progress_bar.setValue(100) # 记录当前块的总时间 if self.current_block in self.block_start_times: self.block_durations[self.current_block] = time.time() - self.block_start_times[self.current_block] # 进入下一块 self.current_block += 1 self.progress_phase = 'auto_grow' self.phase_start_time = time.time() self.progress_stage_label.setText(f"进度阶段: 第{self.current_block}块空三运算中") else: # 线性增长:从start_value到100 progress = int(self.start_value + (100 - self.start_value) * (elapsed / self.immediate_complete_duration)) self.progress_bar.setValue(progress) # # 处理all_complete状态(当检测到allimages=1时) # elif self.progress_phase == 'all_complete': # elapsed = time.time() - self.phase_start_time # if elapsed >= 5: # 5秒到 # self.progress_bar.setValue(100) # self.progress_stage_label.setText("进度阶段: 建模完成") # else: # # 线性增长:从当前进度到100 # progress = int(self.start_value + (100 - self.start_value) * (elapsed / 5)) # self.progress_bar.setValue(progress) def run_commandss(command): logging.info(f"运行命令: {command}") result = 0 result = subprocess.run(command, shell=True) return result.returncode def check_docker_running(): """检查 Docker 是否正在运行,如果没有运行则显示警告弹窗""" # try: # client = docker.from_env() # 尝试获取 Docker 客户端 # client.ping() # 发送 ping 请求确认 Docker 是否可用 # except docker.errors.DockerException as e: # # 如果发生异常,说明 Docker 没有运行 # show_docker_warning() # 显示 Docker 未运行的警告 # sys.exit() # 退出程序 # except Exception as e: # # 捕获其他类型的异常 # show_docker_warning() # sys.exit() try: # 运行 docker info 命令 result = subprocess.run(['docker', 'info'], capture_output=True, text=True, check=True) # 打印输出信息 print(result.stdout) print("Docker 正常启动") except subprocess.CalledProcessError as e: # 如果命令执行失败,打印错误信息 print("Docker 未正常启动") show_docker_warning() # 显示 Docker 未运行的警告 print(e.stderr) sys.exit() except FileNotFoundError: # 如果 docker 命令未找到,说明 Docker 未安装 print("Docker 未安装或未正确配置") show_docker_warning() # 显示 Docker 未运行的警告 sys.exit() def show_docker_warning(): """显示 Docker 未运行的警告""" QMessageBox.warning(None, "警告", "请打开 Docker 并确保它正常运行!重新开始建模任务!") def start_service(): # 验证许可证 if not validate_service(): return # 如果验证失败,退出服务 # 验证成功,继续运行服务 run_service() def run_service(): # 清空并初始化 Progress.ini 文件 # 清空 Progress.ini 文件 progress_file = 'Progress.ini' if os.path.exists(progress_file): os.remove(progress_file) config_progress = configparser.ConfigParser() config_progress.add_section('Progress') with open(progress_file, 'w', encoding='utf-8') as progressfile: config_progress.write(progressfile) # logger.info("清空并初始化 Progress.ini 文件") # 您的服务主逻辑 # 打开软件时清空结果文件名 # 创建要写入的 JSON 数据 data = { "name": '' } # 写入 JSON 文件 with open('./result/resultName.json', 'w', encoding='utf-8') as json_file: json.dump(data, json_file, ensure_ascii=False, indent=4) ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0) # 运行一个命令node result_server_logs.js # subprocess.Popen('node result_server_logs.js', shell=False,creationflags=subprocess.CREATE_NO_WINDOW) app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) if __name__ == "__main__": start_service() ```
09-18
麻烦帮我优化这段代码,需要保留完整功能和逻辑from PySide2 import QtGui from PySide2 import QtWidgets, QtCore from shiboken2 import wrapInstance from maya.app.general.mayaMixin import MayaQWidgetDockableMixin from maya import OpenMayaUI as omui import maya.OpenMaya as om import maya.cmds as cmds import maya.mel as mel import os, platform, subprocess, sys import xml.dom.minidom as xml # 用于XML设置 DEBUG = False PRNT_STRING = "<RizomUV桥接工具>" # 日志前缀汉化 try: # Python 3 from . import scriptlist except ImportError: import scriptlist class Settings(): def __init__(self): config_path = os.path.join('%s/RizomBridge' % os.getenv('APPDATA')) config_file = 'uisettings.xml' # 脚本路径设置 rizom_script_path = os.path.join(config_path, "MayaRizomBridgeScript.lua") self.rizom_script_path = rizom_script_path.replace('\\', '/') self.apppath = None self.config_file_path = os.path.join(config_path, config_file) self.check_config_exists(config_path) try: self.read_settings() except: print("<RizomUV桥接工具> 读取设置失败,正在创建新的xml文件。") # 日志汉化 self.set_defaults() self.save_xml() self.read_settings() self.exportFile = os.path.join(config_path, self.objname) self.exportFile = self.exportFile.replace('\\', '/') def read_settings(self): doc = xml.parse(self.config_file_path) ui_app = doc.getElementsByTagName("Application")[0] self.apppath = ui_app.getAttribute("apppath") self.objname = ui_app.getAttribute("objname") self.upaxis = ui_app.getAttribute("upaxis") self.loaduvs = self.str_to_bool(ui_app.getAttribute("loaduvs")) self.useuvlink = self.str_to_bool(ui_app.getAttribute("useuvlink")) self.fixuvnames = self.str_to_bool(ui_app.getAttribute("fixuvnames")) ui_packer = doc.getElementsByTagName("Packer")[0] self.quality = int(ui_packer.getAttribute("quality")) self.mutations = int(ui_packer.getAttribute("mutations")) self.margin = int(ui_packer.getAttribute("margin")) self.spacing = int(ui_packer.getAttribute("spacing")) self.resolution = int(ui_packer.getAttribute("resolution")) self.initscaleavg = self.str_to_bool(ui_packer.getAttribute("initscaleavg")) self.autofit = self.str_to_bool(ui_packer.getAttribute("autofit")) def set_defaults(self): self.objname = "MayaRizomExport.fbx" self.upaxis = "Y" self.loaduvs = True self.useuvlink = True self.fixuvnames = False self.quality = 2 self.mutations = 256 self.margin = 2 self.spacing = 2 self.resolution = 1024 self.initscaleavg = True self.autofit = True self.apppath = self.findall_rizom_installs()[-1] def save_xml(self): print(PRNT_STRING, f"将设置保存到磁盘: {self.config_file_path}") # 日志汉化 doc = xml.Document() root_element = doc.createElement("RizomBridge") element_application = doc.createElement("Application") element_application.setAttribute("apppath", str(self.apppath)) element_application.setAttribute("objname", str(self.objname)) element_application.setAttribute("upaxis", str(self.upaxis)) element_application.setAttribute("loaduvs", str(self.loaduvs)) element_application.setAttribute("useuvlink", str(self.useuvlink)) element_application.setAttribute("fixuvnames", str(self.fixuvnames)) element_packer = doc.createElement("Packer") element_packer.setAttribute("quality", str(self.quality)) element_packer.setAttribute("mutations", str(self.mutations)) element_packer.setAttribute("margin", str(self.margin)) element_packer.setAttribute("spacing", str(self.spacing)) element_packer.setAttribute("resolution", str(self.resolution)) element_packer.setAttribute("initscaleavg", str(self.initscaleavg)) element_packer.setAttribute("autofit", str(self.autofit)) doc.appendChild(root_element) root_element.appendChild(element_application) root_element.appendChild(element_packer) doc.writexml(open(self.config_file_path, 'w'), indent=" ", addindent=" ", newl='\n') def check_config_exists(self, config_path): print(PRNT_STRING, f"检查配置文件: {self.config_file_path}") # 日志汉化 if not os.path.exists(config_path): os.makedirs(config_path) if not os.path.exists(self.config_file_path): print(PRNT_STRING, "未找到配置文件。") # 日志汉化 self.set_defaults() self.save_xml() else: print(PRNT_STRING, "找到配置文件。") # 日志汉化 def findall_rizom_installs(self): """ 返回注册表中所有存在的rizom.exe路径 """ try: import winreg except: self.manual_locate_rizom() return [self.apppath] key_path = "SOFTWARE\\Rizom Lab\\" parent_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) installs = [] i = 0 while i < 1000: try: key = winreg.EnumKey(parent_key, i) try: exe_path = winreg.QueryValue(winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path + key), "rizomuv.exe") + ".exe" except FileNotFoundError: exe_path = "" if os.path.exists(exe_path): installs.append(exe_path.replace("\\", "/")) except WindowsError: break i += 1 print(PRNT_STRING, "在Windows注册表中找到RizomUV安装路径:") # 日志汉化 for inst in sorted(installs): print(" ..", inst) return sorted(installs) def manual_locate_rizom(self): om.MGlobal.displayWarning(f"无法定位rizomuv.exe: {self.apppath}") # 提示汉化 fname = QtWidgets.QFileDialog.getOpenFileName(None, '定位rizomuv.exe', 'C:/Program Files/Rizom Lab/', "可执行文件 (*.exe)") # 对话框标题及过滤器汉化 self.apppath = fname[0] self.appver = self.get_version() self.save_xml() def get_version(self): """ 从文件夹名称读取版本号,返回列表形式,例如 [2022, 2] """ # 注释汉化 return os.path.dirname(self.apppath).split()[-1].split('.') def check_lua_path(self): if not os.path.exists(self.rizom_script_path): with open(self.rizom_script_path, 'w') as f: if DEBUG: print(PRNT_STRING, f"在{self.rizom_script_path}创建空白lua文件") # 日志汉化 f.write('') def str_to_bool(self, s): if s == 'True': return True else: # 其他情况均返回False,否则可能返回None导致崩溃 return False class RizomUVBridgeWindow(MayaQWidgetDockableMixin, QtWidgets.QDialog): def __init__(self, rootWidget=None, *args, **kwargs): super(RizomUVBridgeWindow, self).__init__() self.conf = Settings() self.conf.check_lua_path() print("Rizom路径:", self.conf.apppath) # 日志汉化 self.setWindowTitle("桥接工具") # 窗口标题汉化 self.sj_num_selchange = cmds.scriptJob(parent=self.objectName(), event=['SelectionChanged', self.ui_update_uvchannels]) self.resize(250, 300) self.create_widgets() self.create_layouts() self.create_connections() self.exported_objects = [] self.ui_update_uvchannels() self.cleanse_namespaces() self.config_updated = False if not os.path.exists(self.conf.apppath): self.conf.manual_locate_rizom() v = self.conf.get_version() self.btn_run.setText(f'运行RizomUV {v[0]}.{v[1]}') # 按钮文本汉化 rizom_link_path = os.path.dirname(self.conf.apppath) + '/RizomUVLink' self.port = None if rizom_link_path not in sys.path: sys.path.append(os.path.dirname(self.conf.apppath) + '/RizomUVLink') try: from RizomUVLink import CRizomUVLink self.link = CRizomUVLink() except: self.link = None def create_widgets(self): v = ".".join(self.conf.get_version()) self.btn_run = QtWidgets.QPushButton(f'运行RizomUV {v}', self) # 按钮文本汉化 self.btn_export = QtWidgets.QPushButton('导出', self) # 按钮文本汉化 self.btn_import = QtWidgets.QPushButton('导入', self) # 按钮文本汉化 self.cbx_use_link = QtWidgets.QCheckBox('尝试使用CRizomUVLink连接', self) # 复选框文本汉化 self.cbx_use_link.setChecked(self.conf.useuvlink) self.cbx_fix_set_names = QtWidgets.QCheckBox('导入时修复UV集名称', self) # 复选框文本汉化 self.cbx_fix_set_names.setChecked(self.conf.fixuvnames) if not os.path.exists(self.conf.exportFile): self.btn_import.setEnabled(False) # 导出设置组件 self.radioAxisY = QtWidgets.QRadioButton('Y轴') # 单选按钮文本汉化 self.radioAxisZ = QtWidgets.QRadioButton('Z轴') # 单选按钮文本汉化 if self.conf.upaxis == 'Y': self.radioAxisY.setChecked(True) else: self.radioAxisZ.setChecked(True) self.cbx_keepuv = QtWidgets.QCheckBox('加载现有UV', self) # 复选框文本汉化 self.cbx_keepuv.setChecked(self.conf.loaduvs) self.combo_scripts = QtWidgets.QComboBox(self) self.combo_scripts.addItem("无脚本") # 下拉框文本汉化 for s in scriptlist.scripts: self.combo_scripts.addItem(s[1]) # 工具组件 self.btn_fix_shell_normals = QtWidgets.QPushButton('将UV边界边设为硬边') # 按钮文本汉化 self.btn_edit_settings = QtWidgets.QPushButton('打开设置文件夹') # 按钮文本汉化 self.combo_pack_uvset = QtWidgets.QComboBox(self) self.btn_pack = QtWidgets.QPushButton('导出并打包UV', self) # 按钮文本汉化 self.combo_pack_quality = QtWidgets.QComboBox(self) self.combo_pack_quality.addItems(['低', '普通', '高', '较高', '极高']) # 下拉框文本汉化 self.combo_pack_quality.setCurrentIndex(self.conf.quality) self.slider_pack_uvmap = QtWidgets.QSlider(QtCore.Qt.Orientation(1)) self.slider_pack_uvmap.setMinimum(0) self.slider_pack_uvmap.setMaximum(5) self.slider_pack_uvmap.setValue(1) self.label_uvmap = QtWidgets.QLabel("1") self.dspin_pack_mutations = QtWidgets.QSpinBox() self.dspin_pack_mutations.setSingleStep(1) self.dspin_pack_mutations.setRange(1, 1000) self.dspin_pack_mutations.setWrapping(False) self.dspin_pack_mutations.setValue(self.conf.mutations) self.dspin_pack_resolution = QtWidgets.QSpinBox() self.dspin_pack_resolution.setSingleStep(8) self.dspin_pack_resolution.setRange(8, 8192) self.dspin_pack_resolution.setWrapping(False) self.dspin_pack_resolution.setValue(self.conf.resolution) self.dspin_pack_margin = QtWidgets.QSpinBox() self.dspin_pack_margin.setSingleStep(1) self.dspin_pack_margin.setWrapping(False) self.dspin_pack_margin.setValue(self.conf.margin) self.dspin_pack_spacing = QtWidgets.QSpinBox() self.dspin_pack_spacing.setSingleStep(1) self.dspin_pack_spacing.setWrapping(False) self.dspin_pack_spacing.setValue(self.conf.spacing) self.cbx_initial_scale_avg = QtWidgets.QCheckBox("使用'平均'初始缩放", self) # 复选框文本汉化 self.cbx_initial_scale_avg.setChecked(self.conf.initscaleavg) self.cbx_layout_scaling = QtWidgets.QCheckBox("布局:自动适配", self) # 复选框文本汉化 self.cbx_layout_scaling.setChecked(self.conf.autofit) def create_layouts(self): main_layout = QtWidgets.QVBoxLayout(self) grp_layout = QtWidgets.QVBoxLayout() grp_layout.addWidget(self.btn_run) grp_layout.addWidget(self.btn_export) grp_layout.addWidget(self.btn_import) grp_layout.addWidget(self.cbx_use_link) grp_layout.addWidget(self.cbx_fix_set_names) grp = QtWidgets.QGroupBox("UV操作") # 组框标题汉化 grp.setLayout(grp_layout) main_layout.addWidget(grp) # 导出设置 grp_layout = QtWidgets.QVBoxLayout() grp_layout.addWidget(self.cbx_keepuv) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("脚本")) # 标签文本汉化 hor_layout.addWidget(self.combo_scripts) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("上方向轴")) # 标签文本汉化 hor_layout.addWidget(self.radioAxisY) hor_layout.addWidget(self.radioAxisZ) grp_layout.addLayout(hor_layout) grp = QtWidgets.QGroupBox("导出设置") # 组框标题汉化 grp.setLayout(grp_layout) main_layout.addWidget(grp) # 工具 grp_layout = QtWidgets.QVBoxLayout() grp_layout.addWidget(self.btn_fix_shell_normals) grp_layout.addWidget(self.btn_edit_settings) grp = QtWidgets.QGroupBox("工具") # 组框标题汉化 grp.setLayout(grp_layout) main_layout.addWidget(grp) # UV打包 grp_layout = QtWidgets.QVBoxLayout() grp = QtWidgets.QGroupBox("UV打包") # 组框标题汉化 hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(self.btn_pack) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("要打包的UV集")) # 标签文本汉化 hor_layout.addWidget(self.combo_pack_uvset) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("打包质量")) # 标签文本汉化 hor_layout.addWidget(self.combo_pack_quality) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("迭代次数")) # 标签文本汉化 hor_layout.addWidget(self.dspin_pack_mutations) hor_layout.addWidget(QtWidgets.QLabel("分辨率")) # 标签文本汉化 hor_layout.addWidget(self.dspin_pack_resolution) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(QtWidgets.QLabel("边距")) # 标签文本汉化 hor_layout.addWidget(self.dspin_pack_margin) hor_layout.addWidget(QtWidgets.QLabel("间距")) # 标签文本汉化 hor_layout.addWidget(self.dspin_pack_spacing) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(self.cbx_initial_scale_avg) grp_layout.addLayout(hor_layout) hor_layout = QtWidgets.QHBoxLayout() hor_layout.addWidget(self.cbx_layout_scaling) grp_layout.addLayout(hor_layout) grp_layout.addStretch() grp.setLayout(grp_layout) main_layout.addWidget(grp) def create_connections(self): self.btn_run.clicked.connect(self.riz_run) self.btn_export.clicked.connect(self.riz_export) self.btn_import.clicked.connect(self.riz_import) self.btn_fix_shell_normals.clicked.connect(fix_shell_border_normals) self.btn_edit_settings.clicked.connect(self.browse_settings_location) self.cbx_keepuv.stateChanged.connect(self.set_config) self.slider_pack_uvmap.valueChanged.connect(self.ui_pack_update_labels) self.radioAxisY.clicked.connect(self.set_config) self.radioAxisZ.clicked.connect(self.set_config) self.dspin_pack_mutations.valueChanged.connect(self.set_config) self.dspin_pack_resolution.valueChanged.connect(self.set_config) self.dspin_pack_margin.valueChanged.connect(self.set_config) self.dspin_pack_spacing.valueChanged.connect(self.set_config) self.cbx_layout_scaling.stateChanged.connect(self.set_config) self.cbx_use_link.stateChanged.connect(self.set_config) self.cbx_fix_set_names.stateChanged.connect(self.set_config) self.btn_pack.clicked.connect(self.riz_pack) return def riz_run(self): # 确认应用程序路径 if not os.path.exists(self.conf.apppath): self.manual_locate_rizom() if self.link and self.cbx_use_link.isChecked(): self.port = self.link.RunRizomUV() print(PRNT_STRING, f"RizomUV {self.link.RizomUVVersion()}链接已建立。正在TCP端口{self.port}监听命令") # 日志汉化 else: # 链接不可用时使用原始方法 print(PRNT_STRING, "RizomUV链接不可用。将使用LUA脚本文件通信。") # 日志汉化 cmd = '"' + self.conf.apppath + '" -cf "' + self.conf.rizom_script_path + '"' print(cmd) if platform.system() == "Windows": self.sp = subprocess.Popen(cmd, shell=True) else: self.sp = subprocess.Popen(["open", "-a", self.conf.apppath, "-cf", self.conf.rizom_script_path]) self.btn_export.setEnabled(True) return def riz_pack(self): # 导出模型不加载脚本 current_uv = self.combo_pack_uvset.currentText() exported = self.riz_export(False, False) if not exported: return # 根据GUI选项构建LUA代码 cmd = '' # 重复使用的文本块 cmd_properties_prefix = 'ZomIslandGroups({Mode="SetGroupsProperties", WorkingSet="Visible", MergingPolicyString="A_ADD|AIB_ADD_A_VALUE_B|B_CLONE", GroupPaths={ "RootGroup" }, ' # 加载模型 cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZUVW=true, UVWProps=true}})\n' cmd += 'ZomUvset({Mode="SetCurrent", Name="%s"})\n' % current_uv cmd += cmd_properties_prefix + 'Properties={Pack={Rotate={Step=90}}}})\n' cmd += cmd_properties_prefix + 'Properties={Pack={Rotate={Mode=0}}}})\n' margin = float(self.dspin_pack_margin.value()) / self.dspin_pack_resolution.value() spacing = float(self.dspin_pack_spacing.value()) / self.dspin_pack_resolution.value() cmd += cmd_properties_prefix + 'Properties={Pack={SpacingSize=%f}}})\n' % spacing cmd += cmd_properties_prefix + 'Properties={Pack={MarginSize=%f}}})\n' % margin quality = [128, 256, 512, 1024, 2048] cmd += cmd_properties_prefix + 'Properties={Pack={Resolution=%i}}})\n' % quality[self.combo_pack_quality.currentIndex()] cmd += cmd_properties_prefix + 'Properties={Pack={MaxMutations=%i}}})\n' % self.dspin_pack_mutations.value() init_scale = 0 if self.cbx_initial_scale_avg.isChecked(): init_scale = 2 layout_scale = 0 if self.cbx_layout_scaling.isChecked(): layout_scale = 2 cmd += 'ZomSet({Path="Prefs.PackOptions.__ScalingMode", Value=%i})\n' % init_scale cmd += 'ZomSave({File={Path="c:/users/root/appdata/local/temp/MayaRizomExport.fbx", UVWProps=true}, __UpdateUIObjFileName=true})\n' cmd += 'ZomIslandGroups({Mode="DistributeInTilesEvenly", WorkingSet="Visible&Flat", MergingPolicyString="A_ADD|AIB_ADD_A_VALUE_B|B_CLONE", UseTileLocks=true, UseIslandLocks=true})\n' cmd += 'ZomPack({RootGroup="RootGroup", WorkingSet="Visible&Flat", ProcessTileSelection=false, RecursionDepth=1, Translate=true, LayoutScalingMode=%i, Scaling={Mode=%i}})\n' % (layout_scale, init_scale) if DEBUG: print(cmd) self.write_to_lua_file(cmd) return def fbx_export(self): # FBX导出 if not cmds.pluginInfo("fbxmaya", loaded=True, query=True): cmds.loadPlugin("fbxmaya") cmds.undoInfo(openChunk=True) mel.eval('ConvertInstanceToObject;') # 这是唯一需要撤销的命令 mel.eval('FBXExportSmoothingGroups -v true;') mel.eval('FBXExportTriangulate -v false;') mel.eval('FBXExportSmoothMesh -v false;') mel.eval('FBXExportUpAxis {};'.format(self.conf.upaxis)) print(f'FBXExport -s -f "{self.conf.exportFile}";') mel.eval(f'FBXExport -s -f "{self.conf.exportFile}";') try: cmds.undo() except RuntimeError as RE: # 没有更多可撤销的命令 pass # 结束FBX导出 return def riz_export(self, use_script=True, load_model=True): if self.link and self.cbx_use_link.isChecked(): print("Rizom链接版本", self.link.Version()) self.exported_objects = cmds.ls(selection=True, tr=True) # FBX导出 self.fbx_export() if not self.port: self.riz_run() params = { "File.Path": self.conf.exportFile, "File.XYZUVW": True, # 加载3D+UV数据(使用File.XYZ仅加载3D数据) "File.UVWProps": True, # 加载UV属性(如固定、纹理密度设置等) "File.ImportGroups": True, # 加载岛组层级 "__Focus": True, # 在视口中聚焦加载的网格 } self.link.Load(params) if DEBUG: print("<RIZOM> 导出至:", self.conf.exportFile) # 日志汉化 cmds.select(self.exported_objects) # 启用导入按钮(如果之前禁用) self.btn_import.setEnabled(True) else: # 传统方法 # print("<RIZOM> 正在导出模型") # 日志汉化 self.cleanse_namespaces() # 强制清理 self.exported_objects = cmds.ls(selection=True, tr=True) if not self.exported_objects: return False # 删除历史记录(防止存在空组被此操作移除) # 否则导入时会被清除,导致对象计数不匹配 cmds.bakePartialHistory() cmd = '' # 导出选项 if load_model: if self.cbx_keepuv.isChecked(): cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZUVW=true, UVWProps=true}})\n' else: cmd += 'ZomLoad({File={Path="'+self.conf.exportFile+'", ImportGroups=true, XYZ=true}, NormalizeUVW=true})\n' # 添加脚本 if self.combo_scripts.currentIndex() and use_script is True: script_name = scriptlist.scripts[self.combo_scripts.currentIndex()-1][0] script_path = os.path.join(os.path.dirname(__file__), 'lua_scripts/%s' % script_name) with open(script_path, 'r') as lua_script: cmd += lua_script.read() if DEBUG: print("<RizomUV桥接工具>", cmd) # 日志汉化 # FBX导出 self.fbx_export() if DEBUG: print("<RIZOM> 导出至:", self.conf.exportFile) # 日志汉化 cmds.select(self.exported_objects) self.write_to_lua_file(cmd) self.btn_import.setEnabled(True) # 如果有显著更改,将配置写入磁盘 if self.config_updated == True: self.conf.save_xml() return True def riz_import(self): if not os.path.exists(self.conf.exportFile): om.MGlobal.displayError(f"无法找到导出文件: {self.conf.exportFile}") # 错误提示汉化 self.btn_import.setEnabled(False) if self.cbx_use_link.isChecked(): if self.link: try: self.link.Save({"File.Path": self.conf.exportFile}) except RuntimeError as RE: cmds.error('无法通过Python链接向Rizom发送保存命令。导出时是否启用了链接?') # 错误提示汉化 # FBX导入 namespace = ':RIZOMUV' if not cmds.namespace(ex=namespace): cmds.namespace(add=namespace) cmds.namespace(set=namespace) mel.eval('FBXImportMode -v add;') mel.eval(f'FBXImport -f "{self.conf.exportFile}";') # 结束FBX导入 imported_objects = cmds.ls('RIZOMUV:*', long=True, type="transform") original_matches = [] for riz_obj in imported_objects: print("导入的对象名称", riz_obj) # 日志汉化 original = riz_obj.replace('RIZOMUV:', '') original_matches.append(original) # 列出每个项目的UV集,无UV集则跳过(排除组节点等对象) original_uvsets = cmds.polyUVSet(original, allUVSets=True, q=True) imported_uvsets = cmds.polyUVSet(riz_obj, allUVSets=True, q=True) if original_uvsets: # 检查名称 # if self.cbx_fix_set_names.isChecked(): for i in range(len(original_uvsets)): try: if not original_uvsets[i] == imported_uvsets[i]: cmds.polyUVSet(riz_obj, rename=True, uvSet=imported_uvsets[i], newUVSet=original_uvsets[i]) except IndexError: # 可能因Maya奇怪的bug导致(polyUVSet命令列出的UV集数量与UV集编辑器中不同) print("UV集索引错误。对象的UV集数量似乎不匹配") # 日志汉化 print("原始UV集:", original_uvsets, original) print("导入的UV集:", imported_uvsets, riz_obj) pass try: cmds.polyTransfer(original, ao=riz_obj, ch=False, uv=True) except RuntimeError as rt: print(f"<RizomUV桥接工具> 无法从{riz_obj}向{original}传输UV") # 日志汉化 if DEBUG: print(f"<RizomUV桥接工具> 从{riz_obj}向{original}传输UV") # 日志汉化 cmds.select(original_matches) cmds.bakePartialHistory() cmds.delete(':RIZOMUV:*') cmds.namespace(rm=':RIZOMUV') return def browse_settings_location(self): os.startfile(os.path.dirname(self.conf.config_file_path)) def ui_pack_update_labels(self): print("值已更改") # 日志汉化 self.label_uvmap.setText(str(self.slider_pack_uvmap.value())) def ui_update_uvchannels(self): sel_obj = cmds.ls(sl=True, tr=True) if not sel_obj: self.combo_pack_uvset.clear() return uvsets = cmds.polyUVSet(sel_obj[0], allUVSets=True, q=True) current_set = cmds.polyUVSet(sel_obj[0], cuv=True, q=True) if uvsets: print(uvsets) self.combo_pack_uvset.clear() self.combo_pack_uvset.addItems(uvsets) self.combo_pack_uvset.setCurrentIndex(uvsets.index(current_set[0])) def cleanse_namespaces(self): """ 第一步 -- 尝试删除所有现有RIZOMUV命名空间直到失败,这应该能清除像 RIZOMUV:RIZOMUV:RIZOMUV这样的嵌套实例 """ fail = False while(not fail): try: cmds.namespace(rm=':RIZOMUV', mnr=True) except: fail = True """ 第二步 -- 在某些场景中,命名空间可能被重命名为RIZOMUV1、RIZOMUV2、RIZOMUV3等 因此需要查找并删除这些残留 """ """ 第二步(更新)-- 尝试删除除RIZOM之外的其他命名空间,因为存在其他命名空间可能导致导入失败 """ c = cmds.listRelatives(ad=True) if not c: return for obj in c: ns_split = obj.split(':') if len(ns_split)>1: result = cmds.confirmDialog( m="选中的对象已分配命名空间。\n" # 对话框文本汉化 "使用工具前必须移除这些命名空间\n" "是否为您删除?(包括未选中的对象)\n\n" "对象上的命名空间:\n%s" % ns_split[0], button=['移除', '取消'] # 按钮文本汉化 ) if result == '移除': cmds.namespace(rm=ns_split[0], mnr=True) else: return return def write_to_lua_file(self, command): with open(self.conf.rizom_script_path, 'w') as f: f.write(command) print("<RIZOM> 已将命令写入lua文件:", self.conf.rizom_script_path) # 日志汉化 for line in command.split("\n"): print("\t", line) return def set_config(self): self.conf.loaduvs = self.cbx_keepuv.isChecked() if self.radioAxisY.isChecked(): self.conf.upaxis = "Y" else: self.conf.upaxis = "Z" self.conf.mutations = self.dspin_pack_mutations.value() self.conf.resolution = self.dspin_pack_resolution.value() self.conf.margin = self.dspin_pack_margin.value() self.conf.spacing = self.dspin_pack_spacing.value() self.conf.autofit = self.cbx_layout_scaling.isChecked() self.conf.fixuvnames = self.cbx_fix_set_names.isChecked() self.conf.useuvlink = self.cbx_use_link.isChecked() self.config_updated = True self.conf.save_xml() def closeEvent(self, event): self.conf.save_xml() def GetMayaWidget(): """ 返回Maya主窗口部件的Python对象 """ # 注释汉化 main_window_ptr = omui.MQtUtil.mainWindow() if sys.version_info.major >= 3: return wrapInstance(int(main_window_ptr), QtWidgets.QWidget) else: return wrapInstance(long(main_window_ptr), QtWidgets.QWidget) def fix_shell_border_normals(): obj_list = cmds.ls(sl=True, o=True) all_final_borders = [] for sub_obj in obj_list: cmds.select(sub_obj, r=True) cmds.polyNormalPerVertex(ufn=True) cmds.polySoftEdge(sub_obj, a=180, ch=1) print("全部软化") # 日志汉化 # 选择对象UV cmds.select(sub_obj + '.map[*]') mel.eval('polySelectBorderShell 1;') uv_border = cmds.polyListComponentConversion(te=True, internal=True) uv_border = cmds.ls(uv_border, fl=True) final_border = [] # 特殊过滤 for curEdge in uv_border: edge_uvs = cmds.polyListComponentConversion(curEdge, tuv=True) edge_uvs = cmds.ls(edge_uvs, fl=True) if len(edge_uvs) > 2: final_border.append(curEdge) cmds.polySoftEdge(final_border, a=0, ch=1) all_final_borders.append(final_border) cmds.select(cl=True) for sel_l in all_final_borders: cmds.select(sel_l, add=True) cmds.hilite(obj_list) def run(): scriptJobs = cmds.scriptJob(listJobs=True) for sj in scriptJobs: if "RizomBridge" in sj: print(PRNT_STRING, f"终止已存在的scriptJob: {sj}") # 日志汉化 cmds.scriptJob(kill=int(sj.split(':')[0])) d = RizomUVBridgeWindow() d.show(dockable=True) if __name__ == "__main__": run()
11-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值