I have a large structured model with thousands of parts. During CAD export (to fbx), the file was simplified and many small parts (such as screws and washers) were removed. However, in Blender I now have many 'empties' without any children below it.
I'd like to select all the empties that have no children, so that I can delete them all.
Achim's script to select Empties without children can be optimized and simplified:
import bpy
# select empties
bpy.ops.object.select_by_type(extend=False, type='EMPTY')
empties = bpy.context.selected_objects
# remove elements with children from selection
for obj in empties:
if len(obj.children) > 0:
obj.select_set(False)
print('{} empties were selected'.format(len(bpy.context.selected_objects)))
To delete all Empties without children automatically you can modify this script like so
import bpy
iteration = 1
print('Iteration {}'.format(iteration))
iteration = iteration + 1
# select empties
bpy.ops.object.select_by_type(extend=False, type='EMPTY')
empties = bpy.context.selected_objects
print('Total empties: {}'.format(len(empties)))
while len(empties) > 0:
# deselect everyting
bpy.ops.object.select_all(action='DESELECT')
parents = []
# select childless nodes and collect its parents to check in next iteration
for obj in empties:
if len(obj.children) == 0:
obj.select_set(True)
if obj.parent is not None:
parents.append(obj.parent)
count = len(bpy.context.selected_objects)
if count == 0:
break
print('{} empties were selected'.format(count))
bpy.ops.object.delete(use_global=True, confirm=False)
empties = parents
print('Iteration {}'.format(iteration))
iteration = iteration + 1
print('Done')
The key trick here is that script checks deleted object parent if it became childless and iterates process untill no Empties without children left.
My python is a bit rusty and I'm certain, there are much better ways of doing this. However, here's a bpy script that does the trick.
Select the root empty and run the script. The selected empties can then be deleted.
To get all the empties in the entire tree, the script has to be run several times until no more empties are selected...
import bpy
root_object = bpy.context.object
empties = []
selected = []
def select_children_recursive(obj_parent):
for obj in obj_parent.children:
select_children_recursive(obj)
if obj.type == 'EMPTY':
empties.append(obj)
# call the function
select_children_recursive(root_object)
# deselect everyting
bpy.ops.object.select_all(action='DESELECT')
# select all empties that have NO children
for obj in empties:
if len(obj.children) == 0:
selected.append(obj)
obj.select_set(True)
print('{} empties were selected'.format(len(selected)))
print('Press x to delete them and re-run this script again multiple times in order to delete all empies throughout the entire structure.')
Related
I have a QFileSystemModel and can delete empty directory with QFileSystemModel.rmdir(index).
Is there a way to delete non-empty directory with Pyside2?
I can shutil.rmtree() delete non-empty directory, but since I use Qt, I tend to use the Qt method. if it existed.
my solution is outside of QFileSystemModel. use
QDir.removeRecursively()
def del_folder(self, checked, index):
if not index.isValid():
return
model = index.model()
path = model.fileInfo(index).absoluteFilePath()
dir1 = QtCore.QDir(path)
dir1.removeRecursively() # remove all.
I’m stuck with a problem using Pyroot. I’m not able to read a leaf on a tree which is a two dimensional array of float values. You can see the related Tree in the following:
root [1] TTree tr=(TTree)g->Get(“tevent_2nd_integral”)
root [2] tr.Print()
*Tree :tevent_2nd_integral: Event packet tree 2nd GTUs integral *
*Entries : 57344 : Total = 548967602 bytes File Size = 412690067 *
: : Tree compression factor = 1.33 *
*Br 7 :photon_count_data : photon_count_data[1][1][48][48]/F *
*Entries : 57344 : Total Size= 530758073 bytes File Size = 411860735 *
*Baskets : 19121 : Basket Size= 32000 bytes Compression= 1.29 *
…
The array (the bold one) is photon_count_data[1][1][48][48]. Actually i have several root files and I tried both to make a chain and to use hadd method like hadd file.root 'ls /path/.root’.*
I tried several ways as i will show soon. Anytime i found different problem: once the numpy array which should contain the 48x48 values per each event was not created at all, others just didn’t write anything or strange values (negative also which is not possible).
My code is the following:
# calling the root file after using hadd to merge all files
rootFile = path+"merge.root"
f = XROOT.TFile(rootFile,'read')
tree = f.Get('tevent_2nd_integral')
# making a chain
PDMchain=TChain("tevent_2nd_integral")
for filename in sorted(os.listdir(path)):
if filename.endswith('.root') and("CPU_RUN_MAIN" in filename) :
PDMchain.Add(filename)
pdm_counts = []
#First method using python pyl class
leaves = tree.GetListOfLeaves()
# define dynamically a python class containing root Leaves objects
class PyListOfLeaves(dict) :
pass
# create an istance
pyl = PyListOfLeaves()
for i in range(0,leaves.GetEntries() ) :
leaf = leaves.At(i)
name = leaf.GetName()
# add dynamically attribute to my class
pyl.__setattr__(name,leaf)
for iev in range(0,nEntries_pixel) :
tree.GetEntry(iev)
pdm_counts.append(pyl.photon_count_data.GetValue())
# the Draw method
count = tree.Draw("photon_count_data","","")
pdm_counts.append(np.array(np.frombuffer(tree.GetV1(), dtype=np.float64, count=count)))
#ROOT buffer method
for event in PDMchain:
pdm_data_for_this_event = event.photon_count_data
pdm_data_for_this_event.SetSize(2304) #ROOT buffer
pdm_couts.append(np.array(pdm_data_for_this_event,copy=True))
with the python class method the array pdm_counts is filled with just the first element contained in photon_count_data
with the Draw method I get a segmentation violation or a strange kernel issue
with the root buffer method I get right back a list containing all the 2304 (48x48) values but they are completely different from those in the photon_count_data, id est, negative values or orders of magnitude senseless
Could you tell me where I’m wrong or if there could be a more elegant and quick method to do so.
Thanks in advance
actually I found the solution and I would like to share it if anytime someone will need it!
Actually the third method explained
for event in PDMchain:
pdm_data_for_this_event = event.photon_count_data
pdm_data_for_this_event.SetSize(2304) #ROOT buffer
pdm_couts.append(np.array(pdm_data_for_this_event,copy=True))
works, but unfortunately I was using Spyder to visualize data and for some reason it return strange values which are not right! So...don't use Spyder!!!
Moreover another method works fine:
from root_pandas import read_root
data = read_root('merge.root', 'tevent_2nd_integral', columns=['cpu_packet_time', 'photon_count_data'])
Cheers!
I have the following QlistWidget:
self.myListWidget1 = QListWidget()
os.chdir("./Downloads")
for file in glob.glob("*.pdf"):
QListWidgetItem(file, self.myListWidget1)
self.myListWidget1.sortItems()
How can I add a QLineEdit that is able to filter items in self.myListWidget1 by partial
string?
The currently accepted answer by Papooch is not very good unfortunately. The reason is that it will not keep the current row event if it remains withing filter criteria. You do not want to generate a new list. You actually want to hide items which are not withing filter criteria. Do it like this:
void ItemsListWidget::filterItems(QString filter)
{
for (int row = 0; row < count(); ++row)
{
item(row)->setHidden(!item(row)->text().contains(filter, Qt::CaseInsensitive)); // you can leave out the case insensitivity if you do not want it
}
}
(it is Qt/C++ code but I am pretty sure you can easily adapt it for Python)
You are going to need a separate list on which you will do the filtering and then display the filtered list in the QListWidget.
The most basic example could look like this:
self.myLineEdit = QLineEdit()
self.myListWidget1 = QListWidget()
self.listOfPdfs = [] # this will hold the complete list of files
os.chdir("./Downloads")
for file in glob.glob("*.pdf"):
self.listOfPdfs.append(file) # fill the list
QListWidgetItem(file, self.myListWidget1)
# connect the signal textChanged to the filter function
self.myLineEdit.textChanged.connect(self.filterList)
def filterList(self, text):
self.myListWidget1.clear() # clear the list widget
for file on self.listOfPdfs:
if text in file: # only add the line if it passes the filter
QListWidgetItem(file, self.myListWidget1)
(note that I didn't check the validity of the code, minor modifications might be needed)
I am constructing a GUI which displays the contents of a SQL database table and which should allow addition and modification of records through the GUI, using the base PyQt4 classes QTableView and QSqlTableModel. I am having problems with editing the table view.
Here is a Minimal, Complete and Verifiable version of the complete code. It does require changing the login info of whatever SQL database you are using, but besides that the code is sufficient to run the interface:
import sys
import site
from subprocess import Popen
from PyQt4.QtCore import(Qt)
from PyQt4.QtGui import(QMainWindow, QApplication, QWidget, QPushButton, qApp,
QVBoxLayout, QTableView, QApplication)
from PyQt4.QtSql import(QSqlDatabase, QSqlQuery, QSqlQueryModel, QSqlTableModel)
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
# Open and connect to database - this needs to be changed for the particular db you are using
self.__database = QSqlDatabase.addDatabase('QPSQL')
self.__database.setHostName('localhost')
self.__database.setDatabaseName('~/Desktop/H2 testing/TESTING/db/db')
self.__database.setPort(5435)
self.__database.setUserName('grav')
self.__database.setPassword('XXXXXXXXXX')
self.__database.open()
ok = self.__database.open()
if ok == False:
print 'Could not open database'
print 'Text: ', self.__database.lastError().text()
print 'Type: ', str(self.__database.lastError().type())
print 'Number: ', str(self.__database.lastError().number())
print 'Loaded drivers:', str(QSqlDatabase.drivers())
# Create the QSqlTableModel using the database
self.modelDirections = QSqlTableModel(None, self.__database)
self.modelDirections.setTable('PUBLIC.DIRECTIONS')
self.modelDirections.setEditStrategy(QSqlTableModel.OnFieldChange)
self.modelDirections.select()
# Create the QTableView and connect to the QSqlTableModel
self.tableDirections = QTableView()
self.tableDirections.setModel(self.modelDirections)
# Create a QPushButton to add a row to the table
self.buttonAddDir = QPushButton('Add direction')
self.buttonAddDir.clicked.connect(self.createDirection)
# Set up the rest of the window with the QTableView and the QPushButton
vbox = QVBoxLayout()
vbox.addWidget(self.tableDirections)
vbox.addWidget(self.buttonAddDir)
stretchBox = QWidget()
stretchBox.setLayout(vbox)
self.setCentralWidget(stretchBox)
self.show()
def createDirection(self):
# Define and execute query to determine current max direction serial
model = QSqlQueryModel()
query = 'SELECT * FROM directions WHERE id=(SELECT MAX(id) FROM directions)'
model.setQuery(query)
if model.record(0).value('id').toString() == '':
newDirectionSerial = 0
else:
newDirectionSerial = int(model.record(0).value('id').toString()) + 1
# Define queries to insert new direction record
queryText = 'INSERT INTO public.directions (id, text, olddir, opposite) \
VALUES (%s, NULL, 1, NULL)' % (newDirectionSerial)
query = QSqlQuery()
query.exec_(queryText)
if __name__ == "__main__":
app = QApplication(sys.argv)
newWindow = Window()
sys.exit(app.exec_())
On loading the GUI, the table displays correctly in the QTableView. However, I have 2 problems:
I am able to double-click into a field in the table and begin editing, but then when I double-click into another field, anything I entered in the first field disappears.
When I edit a field and then switch to editing a new field, I get the message "Data changed" even though whatever I entered disappears.
My best guess here is that the data is being changed in the QSqlTableModel but then for some reason not translating through to the underlying database field, and when the view refreshes, it does so with an updated model from the still-empty database field.
When I add a new record using the button, the new record doesn't show up. If I close out the script and then start it up again, the new record shows up.
These may well be 2 separate problems but I have a feeling they're related.
I have managed to gather some more information about issue 1 since first posting this problem:
After connecting the QSqlTableModel.dataChanged signal to a reporter function, I'm able to confirm that
The signal is indeed firing when I edit the table-view fields,
The data is not being transferred from the model to the database, because of a problem with the query. The database is returning the following error:
ERROR: Column "text" not found; SQL statement: UPDATE PUBLIC.DIRECTIONS SET "text"='test' WHERE "id" = 1 AND "text" = 'constitutes' AND "dir" = 1 AND "opposite" = 2 [42122-164]
(42S22) QPSQL: Unable to create query
I'm able to access the database through another application and I test-ran variants of this query. Stripping the quotes around the field names and getting rid of the trailing [42122-164] produces a successful query and the behavior I've been looking for.
That's great - but I want the QSqlTableView to handle this, and don't know how. Anyone clues to how to modify the query behavior of the view (without completely rebuilding it) would be immensely appreciated!
Marketo has a limit of 10 most recent opportunities that are searchable, and unfortunately we have a good number of users with more than 10 opportunities.
It appears the foreach loop starts at the least recently updated opportunity, and works its way up the list to the most recently update opportunity. The issue here is that when they have more than 10, the script can't access those opportunities that are the most recently updated. We could get around this by reversing the order the script searches the opportunity list (by reversing the foreach).
This is is the setup we have now (the script looks for a set of conditions within an opportunity, if it doesn't find them it looks for a different set, and so on).
#set($stip_guid = ${StipList.get(0).stip_opp_guid})
#foreach($opportunity in $OpportunityList)
#if($opportunity.o_opportunity_guid == $stip_guid && $opportunity.o_clear_to_close_date)
Display Unique Copy A
#break
#elseif($opportunity.o_opportunity_guid == $stip_guid && $opportunity.o_sent_to_underwriting)
Display Unique Copy B
#break
#elseif($opportunity.o_opportunity_guid == $stip_guid && $opportunity.o_processing_received)
Display Unique Copy C
#break
#else
Default Copy
#break#end#end
Marketo doesn't seem to be providing a tool which would reverse a collection.
But why not look on indices rather than on objects themselves?
#set($max = $opportunityList.size() - 1)
#foreach($i in [ $max .. 0 ])
#set($opportunity = $opportunityList[$i])
...
#end