def read_build_info(self, folder): path = Path(get_library_folder()) / folder / '.blinfo' if not path.is_file(): if self.write_build_info(folder) == 1: return None with open(path) as file: data = json.load(file) if ('file_version' not in data) or (data['file_version'] != BuildInfo.file_version): if self.write_build_info(folder) == 1: return None else: return self.read_build_info(folder) else: blinfo = data['blinfo'][0] link = Path(get_library_folder()) / folder build_info = BuildInfo('path', link, blinfo['subversion'], blinfo['build_hash'], blinfo['commit_time'], blinfo['branch']) return build_info
def remove_from_drive(self): self.launchButton.setText("Deleting") self.setEnabled(False) path = Path(get_library_folder()) / self.link self.remover = Remover(path) self.remover.finished.connect(self.remover_finished) self.remover.start()
def run(self): self.started.emit() request = self.manager.request('GET', self.link, preload_content=False) temp_folder = Path(get_library_folder()) / ".temp" # Create temp directory if not temp_folder.is_dir(): temp_folder.mkdir() dist = temp_folder / Path(self.link).name # Download with open(dist, 'wb') as file: size = int(request.headers['Content-Length']) for chunk in request.stream(16 * 1024): file.write(chunk) progress = os.stat(dist).st_size / size self.progress_changed.emit(progress, "Downloading: %p%") request.release_conn() request.close() self.finished.emit(dist) return
def show_folder(self): platform = get_platform() library_folder = Path(get_library_folder()) folder = library_folder / self.link if platform == 'Windows': os.startfile(folder.as_posix()) elif platform == 'Linux': subprocess.call(["xdg-open", folder.as_posix()])
def set_library_folder(self): library_folder = str(get_library_folder()) new_library_folder = QFileDialog.getExistingDirectory( self, "Select Library Folder", library_folder, options=QFileDialog.DontUseNativeDialog | QFileDialog.ShowDirsOnly) if new_library_folder and (library_folder != new_library_folder): self.LibraryFolderLineEdit.setText(new_library_folder) set_library_folder(new_library_folder) self.parent.draw_library(clear=True)
def __init__(self, app): super().__init__() self.setupUi(self) self.setAcceptDrops(True) # Server self.server = QLocalServer() self.server.listen("blender-launcher-server") self.server.newConnection.connect(self.new_connection) # Global scope self.app = app self.favorite = None self.status = "None" self.app_state = AppState.IDLE self.cashed_builds = [] self.notification_pool = [] self.windows = [self] self.manager = PoolManager(num_pools=50, maxsize=10) self.timer = None self.started = True self.latest_tag = "" self.new_downloads = False # Setup window self.setWindowTitle("Blender Launcher") self.app.setWindowIcon( QIcon(taskbar_icon_paths[get_taskbar_icon_color()])) # Setup font QFontDatabase.addApplicationFont( ":/resources/fonts/OpenSans-SemiBold.ttf") self.font = QFont("Open Sans SemiBold", 10) self.font.setHintingPreference(QFont.PreferNoHinting) self.app.setFont(self.font) # Setup style file = QFile(":/resources/styles/global.qss") file.open(QFile.ReadOnly | QFile.Text) self.style_sheet = QTextStream(file).readAll() self.app.setStyleSheet(self.style_sheet) # Check library folder if is_library_folder_valid() is False: self.dlg = DialogWindow( self, title="Information", text="First, choose where Blender<br>builds will be stored", accept_text="Continue", cancel_text=None, icon=DialogIcon.INFO) self.dlg.accepted.connect(self.set_library_folder) else: create_library_folders(get_library_folder()) self.draw()
def init_extractor(self, source): library_folder = Path(get_library_folder()) if self.build_info.branch == 'stable': dist = library_folder / 'stable' elif self.build_info.branch == 'daily': dist = library_folder / 'daily' else: dist = library_folder / 'experimental' self.thread = Extractor(self.parent.manager, source, dist) self.thread.progress_changed.connect(self.set_progress_bar) self.thread.finished.connect(self.download_finished) self.thread.start()
def create_shortcut(folder, name): platform = get_platform() library_folder = Path(get_library_folder()) if platform == 'Windows': targetpath = library_folder / folder / "blender.exe" workingdir = library_folder / folder desktop = shell.SHGetFolderPath(0, shellcon.CSIDL_DESKTOP, None, 0) dist = Path(desktop) / (name + ".lnk") if getattr(sys, 'frozen', False): icon = sys._MEIPASS + "/files/winblender.ico" else: icon = Path( "./resources/icons/winblender.ico").resolve().as_posix() icon_location = library_folder / folder / "winblender.ico" copyfile(icon, icon_location.as_posix()) _WSHELL = win32com.client.Dispatch("Wscript.Shell") wscript = _WSHELL.CreateShortCut(dist.as_posix()) wscript.Targetpath = targetpath.as_posix() wscript.WorkingDirectory = workingdir.as_posix() wscript.WindowStyle = 0 wscript.IconLocation = icon_location.as_posix() wscript.save() elif platform == 'Linux': _exec = library_folder / folder / "blender" icon = library_folder / folder / "blender.svg" desktop = Path.home() / "Desktop" filename = name.replace(' ', '-') dist = desktop / (filename + ".desktop") desktop_entry = \ "[Desktop Entry]\n" + \ "Name={0}\n".format(name) + \ "Comment=3D modeling, animation, rendering and post-production\n" + \ "Keywords=3d;cg;modeling;animation;painting;sculpting;texturing;video editing;video tracking;rendering;render engine;cycles;game engine;python;\n" + \ "Icon={0}\n".format(icon.as_posix().replace(' ', r'\ ')) + \ "Terminal=false\n" + \ "Type=Application\n" + \ "Categories=Graphics;3DGraphics;\n" + \ "MimeType=application/x-blender;\n" + \ "Exec={0} %f".format(_exec.as_posix().replace(' ', r'\ ')) with open(dist, 'w') as file: file.write(desktop_entry) os.chmod(dist, 0o744)
def init_extractor(self, source): self.cancelButton.setEnabled(False) library_folder = Path(get_library_folder()) if (self.build_info.branch == 'stable') or (self.build_info.branch == 'lts'): dist = library_folder / 'stable' elif self.build_info.branch == 'daily': dist = library_folder / 'daily' else: dist = library_folder / 'experimental' self.extractor = Extractor(self.parent.manager, source, dist) self.extractor.progress_changed.connect(self.set_progress_bar) self.extractor.finished.connect(self.init_template_installer) self.extractor.start()
def context_menu(self): if len(self.list_widget.selectedItems()) > 1: self.menu_extended.exec_(QCursor.pos()) return link_path = Path(get_library_folder()) / "bl_symlink" link = link_path.as_posix() if os.path.exists(link): if (os.path.isdir(link) or os.path.islink(link)): if link_path.resolve() == self.link: self.createSymlinkAction.setEnabled(False) self.menu.exec_(QCursor.pos()) return self.createSymlinkAction.setEnabled(True) self.menu.exec_(QCursor.pos())
def create_symlink(self): target = self.link.as_posix() link = (Path(get_library_folder()) / "bl_symlink").as_posix() platform = get_platform() if platform == 'Windows': if os.path.exists(link): if os.path.isdir(link): os.rmdir(link) _check_call('mklink /J "{0}" "{1}"'.format(link, target)) elif platform == 'Linux': if os.path.exists(link): if os.path.islink(link): os.unlink(link) os.symlink(target, link)
def run(self): library_folder = Path(get_library_folder()) if get_platform() == 'Windows': blender_exe = "blender.exe" elif get_platform() == 'Linux': blender_exe = "blender" for dir in library_folder.iterdir(): if dir.is_dir(): for build in dir.iterdir(): path = library_folder / dir / build / blender_exe if path.is_file(): self.build_found.emit(dir / build) self.finished.emit() return
def create_symlink(self): target = self.link.as_posix() link = (Path(get_library_folder()) / "bl_symlink").as_posix() platform = get_platform() if platform == 'Windows': if os.path.exists(link): if os.path.isdir(link): os.rmdir(link) check_call('mklink /J "{0}" "{1}"'.format(link, target), creationflags=CREATE_NO_WINDOW, shell=True, stderr=DEVNULL, stdin=DEVNULL) elif platform == 'Linux': if os.path.exists(link): if os.path.islink(link): os.unlink(link) os.symlink(target, link)
def launch(self): self.item.setSelected(True) if hasattr(self, "NewItemLabel"): self.NewItemLabel.hide() platform = get_platform() library_folder = Path(get_library_folder()) blender_args = get_blender_startup_arguments() if platform == 'Windows': b3d_exe = library_folder / self.link / "blender.exe" if blender_args == "": proc = _popen(b3d_exe.as_posix()) else: args = [b3d_exe.as_posix()] args.extend(blender_args.split(' ')) proc = _popen(args) elif platform == 'Linux': bash_args = get_bash_arguments() if bash_args != '': bash_args = bash_args + " nohup" else: bash_args = "nohup" b3d_exe = library_folder / self.link / "blender" proc = _popen('{0} "{1}" {2}'.format( bash_args, b3d_exe.as_posix(), blender_args)) if self.observer is None: self.observer = Observer(self) self.observer.count_changed.connect(self.proc_count_changed) self.observer.started.connect(self.observer_started) self.observer.finished.connect(self.observer_finished) self.observer.start() self.observer.append_proc(proc)
def launch(self): self.item.setSelected(True) if hasattr(self, "NewItemLabel"): self.NewItemLabel.hide() platform = get_platform() library_folder = Path(get_library_folder()) if platform == 'Windows': b3d_exe = library_folder / self.link / "blender.exe" proc = _popen(b3d_exe.as_posix()) elif platform == 'Linux': b3d_exe = library_folder / self.link / "blender" proc = _popen('nohup "' + b3d_exe.as_posix() + '"') if self.observer is None: self.observer = Observer(self) self.observer.count_changed.connect(self.proc_count_changed) self.observer.started.connect(self.observer_started) self.observer.finished.connect(self.observer_finished) self.observer.start() self.observer.append_proc(proc)
def __init__(self, parent): super().__init__() self.setWindowFlag(Qt.SubWindow) self.parent = parent self.setupUi(self) self.setWindowTitle("Settings") self.HeaderLayout = QHBoxLayout() self.HeaderLayout.setContentsMargins(36, 1, 1, 0) self.HeaderLayout.setSpacing(0) self.CentralLayout.addLayout(self.HeaderLayout) self.CloseButton = \ QPushButton(QIcon(":resources/icons/close.svg"), "") self.CloseButton.setIconSize(QSize(20, 20)) self.CloseButton.setFixedSize(36, 32) self.CloseButton.setProperty("HeaderButton", True) self.CloseButton.setProperty("CloseButton", True) self.CloseButton.clicked.connect(self.close) self.HeaderLabel = QLabel("Settings") self.HeaderLabel.setAlignment(Qt.AlignCenter) self.HeaderLayout.addWidget(self.HeaderLabel, 1) self.HeaderLayout.addWidget(self.CloseButton, 0, Qt.AlignRight) # Library Folder self.LibraryFolderLineEdit = QLineEdit() self.LibraryFolderLineEdit.setText(str(get_library_folder())) self.LibraryFolderLineEdit.setContextMenuPolicy(Qt.NoContextMenu) self.LibraryFolderLineEdit.setReadOnly(True) self.LibraryFolderLineEdit.setCursorPosition(0) self.SetLibraryFolderButton = \ QPushButton(QIcon(":resources/icons/folder.svg"), "") self.SetLibraryFolderButton.clicked.connect(self.set_library_folder) self.ReloadLibraryFolderContentButton = QPushButton( "Reload Library Folder Content") self.ReloadLibraryFolderContentButton.clicked.connect( lambda: self.parent.draw_library(True)) self.LibraryFolderLayout = QHBoxLayout() self.LibraryFolderLayout.setContentsMargins(0, 0, 0, 0) self.LibraryFolderLayout.setSpacing(0) self.LibraryFolderLayout.addWidget(self.LibraryFolderLineEdit) self.LibraryFolderLayout.addWidget(self.SetLibraryFolderButton) # Launch When System Starts self.LaunchWhenSystemStartsCheckBox = QCheckBox( "Launch When System Starts") self.LaunchWhenSystemStartsCheckBox.setChecked( get_launch_when_system_starts()) self.LaunchWhenSystemStartsCheckBox.clicked.connect( self.toggle_launch_when_system_starts) # Launch Minimized To Tray self.LaunchMinimizedToTrayCheckBox = QCheckBox( "Launch Minimized To Tray") self.LaunchMinimizedToTrayCheckBox.setChecked( get_launch_minimized_to_tray()) self.LaunchMinimizedToTrayCheckBox.clicked.connect( self.toggle_launch_minimized_to_tray) # High Dpi Scaling self.EnableHighDpiScalingCheckBox = \ QCheckBox("Enable High DPI Scaling") self.EnableHighDpiScalingCheckBox.clicked.connect( self.toggle_enable_high_dpi_scaling) self.EnableHighDpiScalingCheckBox.setChecked( get_enable_high_dpi_scaling()) # Taskbar Icon Color self.TaskbarIconColorComboBox = QComboBox() self.TaskbarIconColorComboBox.addItems(taskbar_icon_colors.keys()) self.TaskbarIconColorComboBox.setCurrentIndex( get_taskbar_icon_color()) self.TaskbarIconColorComboBox.activated[str].connect( self.change_taskbar_icon_color) # Default Library Page self.DefaultLibraryPageComboBox = QComboBox() self.DefaultLibraryPageComboBox.addItems(library_pages.keys()) self.DefaultLibraryPageComboBox.setCurrentIndex( get_default_library_page()) self.DefaultLibraryPageComboBox.activated[str].connect( self.change_default_library_page) # Default Downloads Page self.DefaultDownloadsPageComboBox = QComboBox() self.DefaultDownloadsPageComboBox.addItems(downloads_pages.keys()) self.DefaultDownloadsPageComboBox.setCurrentIndex( get_default_downloads_page()) self.DefaultDownloadsPageComboBox.activated[str].connect( self.change_default_downloads_page) # Notifications self.EnableNewBuildsNotifications = QCheckBox( "When New Builds Are Available") self.EnableNewBuildsNotifications.clicked.connect( self.toggle_enable_new_builds_notifications) self.EnableNewBuildsNotifications.setChecked( get_enable_new_builds_notifications()) self.EnableDownloadNotifications = QCheckBox( "When Downloading Is Finished") self.EnableDownloadNotifications.clicked.connect( self.toggle_enable_download_notifications) self.EnableDownloadNotifications.setChecked( get_enable_download_notifications()) # Mark As Favorite self.MarkAsFavorite = QComboBox() self.MarkAsFavorite.addItems(favorite_pages.keys()) self.MarkAsFavorite.setCurrentIndex( get_mark_as_favorite()) self.MarkAsFavorite.activated[str].connect( self.change_mark_as_favorite) # Layout self.SettingsLayout = QFormLayout() self.SettingsLayout.setContentsMargins(6, 6, 6, 6) self.SettingsLayout.setSpacing(6) self.SettingsLayout.setRowWrapPolicy(QFormLayout.DontWrapRows) self.SettingsLayout.setFieldGrowthPolicy( QFormLayout.AllNonFixedFieldsGrow) self.SettingsLayout.setLabelAlignment(Qt.AlignLeft) self.CentralLayout.addLayout(self.SettingsLayout) self.SettingsLayout.addRow(QLabel("Library Folder:")) self.SettingsLayout.addRow(self.LibraryFolderLayout) self.SettingsLayout.addRow( self.ReloadLibraryFolderContentButton) self.SettingsLayout.addRow(QLabel("System:")) if get_platform() == 'Windows': self.SettingsLayout.addRow(self.LaunchWhenSystemStartsCheckBox) self.SettingsLayout.addRow(self.LaunchMinimizedToTrayCheckBox) self.SettingsLayout.addRow(self.EnableHighDpiScalingCheckBox) self.SettingsLayout.addRow(QLabel("Interface:")) self.SettingsLayout.addRow( "Taskbar Icon Color", self.TaskbarIconColorComboBox) self.SettingsLayout.addRow( "Default Library Page", self.DefaultLibraryPageComboBox) self.SettingsLayout.addRow( "Default Downloads Page", self.DefaultDownloadsPageComboBox) self.SettingsLayout.addRow(QLabel("Notifications:")) self.SettingsLayout.addRow(self.EnableNewBuildsNotifications) self.SettingsLayout.addRow(self.EnableDownloadNotifications) self.SettingsLayout.addRow(QLabel("Service:")) self.SettingsLayout.addRow( "Mark New Build As Favorite", self.MarkAsFavorite) self.resize(self.sizeHint()) self.show()
def clear_temp(self): temp_folder = Path(get_library_folder()) / ".temp" self.remover = Remover(temp_folder) self.remover.start()
def write_build_info(self, folder): # Read Blender Version platform = get_platform() if platform == 'Windows': blender_exe = "blender.exe" elif platform == 'Linux': blender_exe = "blender" exe_path = Path(get_library_folder()) / folder / blender_exe try: if platform == 'Windows': info = subprocess.check_output([exe_path.as_posix(), "-v"], creationflags=CREATE_NO_WINDOW, shell=True, stderr=DEVNULL, stdin=DEVNULL) elif platform == 'Linux': info = subprocess.check_output([exe_path.as_posix(), "-v"], shell=False, stderr=DEVNULL, stdin=DEVNULL) except CalledProcessError: return 1 info = info.decode('UTF-8') set_locale() ctime = re.search("build commit time: " + "(.*)", info)[1].rstrip() cdate = re.search("build commit date: " + "(.*)", info)[1].rstrip() strptime = time.strptime(cdate + ' ' + ctime, "%Y-%m-%d %H:%M") commit_time = time.strftime("%d-%b-%y-%H:%M", strptime) build_hash = re.search("build hash: " + "(.*)", info)[1].rstrip() subversion = re.search("Blender " + "(.*)", info)[1].rstrip() try: if platform == 'Windows': folder_parts = folder.name.replace("blender-", "").replace( "-windows64", "").rsplit('-', 2) elif platform == 'Linux': folder_parts = folder.name.replace("blender-", "").replace( "-linux64", "").rsplit('-', 2) if len(folder_parts) > 2: branch = folder_parts[0] elif len(folder_parts) > 1: branch = "daily" else: branch = "stable" subversion = folder_parts[0] except Exception: branch = None # Write Version Information data = {} data['file_version'] = BuildInfo.file_version data['blinfo'] = [] data['blinfo'].append({ 'branch': branch, 'subversion': subversion, 'build_hash': build_hash, 'commit_time': commit_time, }) path = Path(get_library_folder()) / folder / '.blinfo' with open(path, 'w') as file: json.dump(data, file) return 0
def register_extension(self): path = Path(get_library_folder()) / self.link self.register = Register(path) self.register.start()