Code
-- Utility_belt 1.0
-- Collection of various code snippets to perform some basic functions
-- Thanks to Timo van der Laan, LociOiling and others
-- from whose code some snippets were taken
-- Quicksave9 is odd, seems to store both structs and a pose
-- This recipe will not overwrite Quicksave 9
-- ------------------------------------
-- ---------- GLOBAL VARS -------------
-- ------------------------------------
Recipe = "Utility belt"
Version = "1.0"
ReVersion = Recipe .. " " .. Version
-- --------- OPTIONS --------------
LoadFromQS = 1
SaveToQS = 1
numSegs = structure.GetCount()
OG_FILTER_SETTING = 0
DBG = false
------------------------------------------------------
-- HELPER FUNCTIONS
------------------------------------------------------
function DoesPuzzleHaveFilters()
local names = filter.GetNames()
if names ~= nil and #names > 0 then
return true
else
return false
end --Thnx LociOiling
end
function round ( drie )
return drie - drie % 0.001
end
function nodupes(duptbl)
local hash = {}
local uniques = {}
for _, v in ipairs( duptbl ) do
if not hash[v] then
uniques[#uniques+1] = v
hash[v] = true
end
end
return uniques
end--Thnx vogomatix @ SO
function GetRanges(rTable)
local txt = ''
local rStart = nil
local rangestable={}
for key, value in pairs(rTable) do
if rTable[key + 1] == value + 1 then --are sequential
if not rStart then
rStart = value
end
else
if rStart then
txt = txt .. rStart .. "-" .. value .. ", "
rangestable[#rangestable+1]={rStart,value}
rStart = nil
else
txt = txt .. value .. ", "
rangestable[#rangestable + 1] = { value }
end
end
end
print( txt:sub(1, -3) )
--how to fomat over 2 lines for big table ?
return rangestable
end -- rosettacode.org
function Cleanup( err )
if CLEANUPENTRY ~= nil then
return
end
CLEANUPENTRY = true
--Tx LociOiling
print( 'Cleaning up' )
print( '========================' )
print( ReVersion..' Cleaning up' )
behavior.SetFiltersDisabled ( OG_FILTER_SETTING )
print( err )
end
------------------------------------------------------
-- BIG FUNCTIONS
------------------------------------------------------
function load_from_slot()
local function SetDialog()
local dlg = dialog.CreateDialog ( "Load from Quicksave position" )
dlg.loadquicksave = dialog.AddSlider ( "Load quicksave:", 1, 1, 99, 0 )
dlg.ok = dialog.AddButton ( "OK", 1)
dlg.cancel2 = dialog.AddButton ( "Cancel", 0 )
if dialog.Show( dlg ) > 0 then
LoadFromQS = dlg.loadquicksave.value
return true
else
return ('exit')
end
end
local go = SetDialog()
if go == 'exit' then
print('Load Quicksave cancelled')
return
end
if save.QuicksaveEmpty ( LoadFromQS ) then
print('Quicksave '.. LoadFromQS..' is empty, nothing to load')
else
save.Quickload( LoadFromQS )
print('Loaded pose from Quicksave number: '..LoadFromQS)
end
end
-- ----------------------------------------------------------------------------------
function save_to_slot()--copypasta code is so wrong, merging these 2 functions should be simple
--but then we break clean code rules, "function should do 1 thing and do it well."
local function SetDialog()
local dlg = dialog.CreateDialog ( "Save to Quicksave position" )
dlg.savequicksave = dialog.AddSlider ( "Save in Qsave", 1, 1, 99, 0 )
dlg.ok = dialog.AddButton ( "OK", 1 )
dlg.cancel2 = dialog.AddButton ( "Cancel", 0 )
if dialog.Show( dlg ) > 0 then
SaveToQS = dlg.savequicksave.value
return true
else
return ('exit')
end
end
local function proceedwithqs( SaveToQS )
local stdlg = dialog.CreateDialog ( "Alert Quicksave occupied" )
stdlg.label1 = dialog.AddLabel ('Quicksave '.. SaveToQS .. ' is occupied' )
stdlg.label2 = dialog.AddLabel ('Overwrite with current pose ?')
stdlg.ok = dialog.AddButton ( "Yes", 1 )
stdlg.cancel2 = dialog.AddButton ( "No", 0 )
if dialog.Show( stdlg ) > 0 then
return true
else
return false
end
end
local go = SetDialog()
if go == 'exit' then
print('Save to Quicksave cancelled')
return
end
if save.QuicksaveEmpty ( SaveToQS ) == false then
print('Quicksave '.. SaveToQS .. ' is occupied' )
if proceedwithqs( SaveToQS ) == false then
print('Save to Quicksave '..SaveToQS..' cancelled')
return
end
end
save.Quicksave( SaveToQS )
print('Saved pose in Quicksave number: '..SaveToQS)
print('')
end
-- ---------------------------------------
function Quicksave_purge() --overwrites quicksaves 1-99 with current pose
local ljust = string.rep(" ",12)
local function Warning()
local dlg = dialog.CreateDialog ( "!! WARNING !!" )
dlg.warninglabel = dialog.AddLabel ( ljust.. " Every non-empty Quicksave")
dlg.warninglabel2 = dialog.AddLabel (ljust.. "will be overwritten with current pose" )
dlg.warninglabel3 = dialog.AddLabel ( " ")
dlg.ok = dialog.AddButton ( "OK", 1 )
dlg.cancel2 = dialog.AddButton ( "Cancel", 0 )
if dialog.Show( dlg ) > 0 then
return true
else
return ('exit')
end
end
local go = Warning()
if go == 'exit' then
print('Overwrite cancelled')
return
end
--QS(9) Is for structs
--if 9 contains a struct this api still calls it empty
print('Overwriting all non-empty Quicksave')
print(' slots with current pose')
for i = 1, 99 do
if not save.QuicksaveEmpty( i ) then --Is the quicksave empty.
if i ~=9 then
save.Quicksave( i )
print('saving current pose to Quicksave:', i)
end
if i==9 then
print('Quicksave 9 is non-empty but it is reserved for Structures?')
print('This recipe will not overwrite Quicksave 9')
end
end
end
print('')
end
-- --------------------------------------
-- QS_status
-- Thanks to TimovdL
-- ListToSet and SetToString adapted from TvdL
-- 02-05-2012 TvdL Free to use for non commercial purposes
--
function ListToSet ( list ) -- remove dupes
local result = {}
local ff = 0
local ll = -1
table.sort ( list )
for ii = 1, #list do
if list [ ii ] ~= ll + 1 and list [ ii ] ~= ll then
-- note: duplicates are removed
if ll > 0 then
result [ #result + 1 ] = { ff, ll }
end
ff = list [ ii ]
end
ll = list [ ii ]
end
if ll > 0 then
result [ #result + 1 ] = { ff, ll }
end
return result
end
function SetToString ( set ) -- to print
local line = ""
for ii = 1, #set do
if ii ~= 1 then
line = line .. ", "
end
line = line .. set [ ii ] [ 1 ] .. "-" .. set [ ii ] [ 2 ]
end
return line
end
-- This code completely ripped from
-- Slot Grabber 1.0 by LociOiling
-- Thanks to LociOiling
function QS_status()
local firstempty = 0
local quickz = {}
for ii = 1, 99 do
if not save.QuicksaveEmpty ( ii ) then --if quicksave has something
quickz [ #quickz + 1 ] = ii --add its number to list
elseif firstempty == 0 then
firstempty = ii --record the 1st empty one
end
end
set_of_used_quickz = ListToSet ( quickz )
print( "Quicksave slots are from 1 to 99")
print( "Amount of occupied Quicksave slots:", #quickz)
print( "Ranges of occupied Quicksave slots:", SetToString ( set_of_used_quickz ) )
if firstempty ~= 0 then
print ( "First empty Quicksave is:", firstempty )
else
print ( "All Quicksave slots are occupied !" )
end
print('')
end
-- ---------------------------------------
function convert_to_loop() --autostructures will return it to normal.
print('Converting to all loops')
local MakeLoop = false
for i = 1, numSegs do
if structure.GetSecondaryStructure( i ) ~= "L" then
MakeLoop = true
break
end
end
if MakeLoop then
save.SaveSecondaryStructure( )
--Use Ctrl+Shift+9 or save.LoadSecondaryStructure() to recover saved struct
print('Secondary structure assignment saved to QS(9).')
print('Use Ctrl+9 to reload Structures')
selection.SelectAll( )
structure.SetSecondaryStructureSelected( "L" )
else
print('Structure is already all Loops.')
end
print('')
selection.DeselectAll( )
end --thnx to TvdL
-- ---------------------------------------
function SecondaryStructureData()
print(' Secondary structure Data')
print('Protein has this many residues:', numSegs)
print()
local StructDict =
{
["L"] = "loop",
["H"] = "helix",
["E"] = "sheet",
["M"] = "molecule"
}
--SecStructTable = {"L", "E", "H","M"} --how often is there a molecule ?? not often
local SecStructTable = { "L", "E", "H" }
local init = -1
for _, whatstruct in ipairs( SecStructTable ) do
local sstruct = StructDict[whatstruct]
print('Searching for', whatstruct, sstruct,'data.')
print('Secondary structure Data for '..sstruct..':')
local count = 0
local maxcount = 0
local realcount = 0
local seqq = {}
local seggys={}
for i = 1, numSegs do
local foo = structure.GetSecondaryStructure( i )
if foo == whatstruct then
seggys[#seggys+1] = i
count = count + 1
realcount = realcount + 1
if count > maxcount then
maxcount = count
end
else
if count ~= 0 then
seqq[#seqq+1] = tostring(count)
end
count = 0
end
if i==numSegs and count ~=0 then seqq[#seqq+1]=tostring(count) end --fix for misses last sequence
end
local percent= ( realcount / numSegs ) * 100
print('How many segs are '..sstruct..': '..realcount)
print('Percent of protein is '..sstruct..': '..round(percent)..' %')
print('Longest sequence:', maxcount)
print(sstruct..' sequences:', #seqq)
print('Sequence lengths:', table.concat( seqq, "," ))
print('Segment range of sequences:')
GetRanges(seggys)
print('')
end
end
-- -----------------------------------------------------
function delete_cuts( )
print('Deleting any cuts...')
for i = 1, numSegs - 1 do
if not structure.IsLocked( i ) then
structure.DeleteCut( i )
end
end
print('deleting cuts..done')
print('')
end
-- -----------------------------------------------------
--Thnx to Frutchy
--for idea of selected outside the loop
function freezySideChains()
print("Freezing sidechains (unlocked segments only)")
for idx = 1, numSegs do
if not structure.IsLocked( idx ) then
selection.Select( idx )
end
end
freeze.FreezeSelected( false, true )
selection.DeselectAll()
end
-- -----------------------------------------------------
function freezyBackBone()
print("Freezing backbone (unlocked segments only)")
for idx = 1, numSegs do
if not structure.IsLocked( idx ) then
selection.Select( idx )
end
end
freeze.FreezeSelected( true, false )
selection.DeselectAll()
end
-- ---------------------------------------------------
function unfreezy()
print('Unfreezing all segments, backbone and sidechains')
freeze.UnfreezeAll()
end
-- --------------------------------------------------
function part_totals_in_worst_order()
print('Scoreparts totals')
--here attempt to get the used partscores for this puzzle
local SubScores = {}
local scoreparts = {}
scoreparts = puzzle.GetPuzzleSubscoreNames( ) --foldit API returns a table for us to use
for i = 1, structure.GetCount( ) do
for j = 1, #scoreparts do
local subscore = current.GetSegmentEnergySubscore ( i, scoreparts [ j ] )
if subscore ~= 0 then --if its exactly zero we assume its an invalid partscore ( It may actually be zero ! )
SubScores[#SubScores+1] = scoreparts [j]
end
end
end--foldit wiki
--calculate partscore totals
local ActiveSubScores = nodupes( SubScores )
local worst_table = {}
local currentPartscore --forward declaration needed here
local worst = 99999999
local wpart = ''
for i in ipairs( ActiveSubScores ) do
local subtot = 0 --this is locally scoped
for j = 1, numSegs do
currentPartscore = ActiveSubScores[i]
local subb = round ( current.GetSegmentEnergySubscore ( j, currentPartscore ) )
subtot = subtot + subb
end
if subtot < worst then
worst = subtot
wpart = currentPartscore
end
worst_table[#worst_table+1] = { currentPartscore, subtot}
end
table.sort( worst_table, function( a, b ) return a[2] < b[2] end )
if next( worst_table ) == nil then
print('Error : Table is empty')
return false --perhaps should call cleanup since this should never happen
end
for k,v in ipairs( worst_table ) do
print('Total ', v[1], ' ', v[2])
end
print('Worst score part:', wpart, 'Score:', worst)
print('')
end
-- ------------------------------------------------------------------
function SetDialog()
local dlg = dialog.CreateDialog ( ReVersion )
dlg.loadqs= dialog.AddCheckbox ( "Load from Quicksave" , false )
dlg.saveqs = dialog.AddCheckbox ( "Save to Quicksave" , false )
dlg.qsempty = dialog.AddCheckbox ( "List non-empty Quicksaves" , false )
if DBG then
-- Overwrite all non-empty Quicksave slots with current pose, for debugging purposes mostly
dlg.purgeqs= dialog.AddCheckbox ( "Overwrite all non-empty Quicksaves" , false )
end
dlg.changetoloop = dialog.AddCheckbox ( "Convert to loop" , false )
dlg.sidchainfreeze = dialog.AddCheckbox ( "Freeze sidechains", false )
dlg.bkbonefreeze = dialog.AddCheckbox ( "Freeze backbone", false )
dlg.unfrzall = dialog.AddCheckbox( "Unfreeze All", false)
dlg.secstrdat = dialog.AddCheckbox ( "Secondary Structure Data" , false )
dlg.remcuts = dialog.AddCheckbox ( "Delete cuts" , false )
dlg.ptsubtot = dialog.AddCheckbox ( "Show partscore totals" , false )
dlg.ok = dialog.AddButton ( "OK", 1)
dlg.cancel2 = dialog.AddButton ( "Cancel", 0 )
if (dialog.Show(dlg) > 0) then
LoadFromQS = dlg.loadqs.value
SaveToQS = dlg.saveqs.value
QStak = dlg.qsempty.value
if DBG then
prgqs = dlg.purgeqs.value
end
chtlp = dlg.changetoloop.value
sesda = dlg.secstrdat.value
rcuts = dlg.remcuts.value
ptsub = dlg.ptsubtot.value
frzdc = dlg.sidchainfreeze.value
frzbb = dlg.bkbonefreeze.value
unfrz = dlg.unfrzall.value
return true
else
print("Dialog cancelled")
return false
end
end
function Main()
print( ReVersion )
print( puzzle.GetName () )
print( "Track:", ui.GetTrackName() )
OG_FILTER_SETTING = behavior.GetFiltersDisabled()
while SetDialog() do
if LoadFromQS then
load_from_slot()
end
if SaveToQS then
save_to_slot()
end
if QStak then
QS_status()
end
if prgqs then
Quicksave_purge()
end
if chtlp then
convert_to_loop()
end
if frzdc then
freezySideChains()
selection.DeselectAll()
end
if frzbb then
freezyBackBone()
selection.DeselectAll()
end
if unfrz then
unfreezy()
end
if sesda then
SecondaryStructureData()
end
if rcuts then
if DoesPuzzleHaveFilters() then
behavior.SetFiltersDisabled ( true )
delete_cuts()
behavior.SetFiltersDisabled ( false )
else
delete_cuts()
end
end
if ptsub then
part_totals_in_worst_order()
end
end
behavior.SetFiltersDisabled ( OG_FILTER_SETTING )
end
xpcall( Main, Cleanup )
--utility belt 1.0