Python AttributeError : Object has no attribute in QGIS Plugin - python

I'm trying to create a GUI with QT Designer. I've converted my .ui designer file to a .py.
Here is my code:
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon
from qgis.core import *
import resources
from delete_feature_dialog import DeleteFeatureDialog
import os.path
class DeleteFeature:
def __init__(self, iface):
# Save reference to the QGIS interface
self.iface = iface
# Declare instance attributes
self.actions = []
self.menu = self.tr(u'&DeleteFeature')
self.toolbar = self.iface.addToolBar(u'DeleteFeature')
self.toolbar.setObjectName(u'DeleteFeature')
def add_action(
self,
icon_path,
text,
callback,
enabled_flag=True,
add_to_menu=True,
add_to_toolbar=True,
status_tip=None,
whats_this=None,
parent=None):
# Create the dialog (after translation) and keep reference
self.dlg = DeleteFeatureDialog()
....
return action
def initGui(self):
icon_path = ':/plugins/DeleteFeature/icon.png'
self.add_action(
icon_path,
text=self.tr(u''),
callback=self.run,
parent=self.iface.mainWindow())
def run(self):
#this code will populate the combo box with all vector layer
self.dlg.layerListCombo.clear()
layers = self.iface.legendInterface().layers()
layer_list = []
for layer in layers:
layerType = layer.type()
if layerType == QgsMapLayer.VectorLayer:
layer_list.append(layer.name())
self.dlg.layerListCombo.addItems(layer_list)
# show the dialog
self.dlg.show()
# Run the dialog event loop
result = self.dlg.exec_()
# See if OK was pressed
if result:
# Do something useful here - delete the line containing pass and
# substitute with your code.
selectedLayerIndex = self.dlg.layerlistcombo.currentIndex()
selectedLayer = layers [selectedLayerIndex]
.....
Then when I open the plugin, I get the following error:
'DeleteFeatureDialog' objectObject has no attribute
'layerlistcombo'in QGIS Plugin
Any suggestion for this.

Seems that you wrote:
selectedLayerIndex = self.dlg.layerlistcombo.currentIndex()
but you should have written:
selectedLayerIndex = self.dlg.layerListCombo.currentIndex()
Like you did previously in your code (notice the Camel Notation when writing, not just lower-case letters), which is probably causing the No Attribute error you get.

Related

PyQgis: item is not the expected type on a QgsMapCanvas

I am currently working on a PyQgis based standalone application and I need to add various QgsRubberBand to my Canvas.
I made a subclass of it : LineAnnotation.
The problem is that when I use the method "QgsMapCanvas.itemAt(event.pos() )" on a "canvasPressEvent", it returns a "qgis._gui.QgsRubberBand" object, not a "LineAnnotation".
Did I do something wrong ? The rest of my program can't work if it doesn't recognize that it's a LineAnnotation as it contains several new methods that I need to use.
Also I can't interact with the item at all, if I try to use one of the methods from QgsRubberBand, the application crashes.
Here is the code with the problem:
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge, QgsRubberBand
from qgis.core import QgsApplication, QgsProject, QgsPointXY, QgsGeometry
from qgis.PyQt.QtGui import QColor
import sys
class LineAnnotation(QgsRubberBand):
def __init__(self, canvas):
QgsRubberBand.__init__(self, canvas)
self.setColor(QColor("red") )
self.setWidth(10)
class Interface(QgsMapCanvas):
def __init__(self):
QgsMapCanvas.__init__(self)
self.setCanvasColor(QColor("#182F36") )
project_path = "project_path"
project = QgsProject.instance()
project.read(project_path)
layer_tree = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas=self)
layer_tree.setAutoSetupOnFirstLayer(False)
self.zoomToFeatureExtent(project.mapLayersByName('layer_name')[0].extent() )
self.enableAntiAliasing(True)
self.setAcceptDrops(True)
self.setParallelRenderingEnabled(True)
p1 = QgsPointXY(524670.46860305720474571, 5470375.41737424582242966)
p2 = QgsPointXY(589864.10151600651443005, 5487531.63656186405569315)
r = LineAnnotation(self)
r.setToGeometry(QgsGeometry.fromPolylineXY([p1, p2]) )
def mousePressEvent(self, event) -> None:
item = self.itemAt(event.pos() )
print(type(item) )
# Output is "<class 'qgis._gui.QgsRubberBand'>"
# Expected: "<class 'LineAnnotation'>"
class StackOverflow:
def __init__(self):
qgs = QgsApplication([], True)
qgs.setDoubleClickInterval(250)
qgs.initQgis()
graphicUI = Interface()
graphicUI.showMaximized()
sys.exit(qgs.exec_() )
if __name__ == '__main__':
app = StackOverflow()
> output: \<class 'qgis.\_gui.QgsRubberBand'\>
> Desired output: \<class 'lineAnnotation.LineAnnotation'\>
Problem seems to occur in versions prior to Qgis 3.26, my problem was solved after updating to latest version (3.28).

File dialog always returns alpha order

I'm creating an application which plots data in files. The first file plotted defines the origin to which all other data is transformed relative to. AFAICT, the QFileDialog always returns files in alphabetical order, regardless of selection order.
Is there a way to return the data ordered by selection?
To illustrate, create a folder with files named something A, B, C or 1, 2, 3. Regardless of the manner they're selected or appear in File Name line edit, the list of paths returned is in alphabetical order.
import os
import sys
from PyQt5 import QtCore, QtWidgets
MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")
def on_button_pressed():
paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
directory = MYDIR,
caption='Open',
filter=(
'All (*.*)'
))
for i, path in enumerate(paths):
print(i, path, flush=True)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushButton("Open")
button.pressed.connect(on_button_pressed)
button.show()
sys.exit(app.exec_())
EDIT An implementation of #musicamate's response which may hang:
import os
import sys
from PyQt5 import QtCore, QtWidgets
MYDIR = (os.environ['USERPROFILE'] + '/Desktop/numbered').replace("\\", "/")
class SelectionOrderFileDialog(QtWidgets.QFileDialog):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog)
self.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
list_view = self.findChild(QtWidgets.QListView, 'listView')
self.selection_model = list_view.selectionModel()
self.selection_model.selectionChanged.connect(self.check_selection)
self.current_selection = []
def check_selection(self):
active_selection = []
for index in self.selection_model.selectedRows():
path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
active_selection.append(path)
updated_current_selection = []
for path in self.current_selection:
if path in active_selection:
updated_current_selection.append(path)
active_selection.remove(path)
updated_current_selection.extend(active_selection)
self.current_selection[:] = updated_current_selection
print(self.current_selection, flush=True)
def on_button_pressed():
# Works fine when called as...
# dialog = SelectionOrderFileDialog()
# Causes hangs on Open
dialog = SelectionOrderFileDialog(
directory = MYDIR,
caption='Open',
filter=(
'text (*.txt)'
';;python (*.py)'
';;All (*.*)'
))
dialog.exec_()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
button = QtWidgets.QPushButton("Open")
button.resize(300, 25)
button.pressed.connect(on_button_pressed)
button.show()
sys.exit(app.exec_())
Such level of control cannot be achieved by using static methods, but it can be done by using an instance of QFileDialog.
The only requirement is to avoid the native dialog, so that we can access one of the file views (a non-native QFileDialog has a list view and a tree view), and then connect to the selectionChanged signal of its selection model.
Then, everytime the signal is emitted, we check with the current ordered selection, remove paths that already exist and build a new list by extending with the new elements.
def showDialog(self):
def checkSelection():
selection = []
for index in selectionModel.selectedRows():
path = index.data(QtWidgets.QFileSystemModel.FilePathRole)
selection.append(path)
newOrderedPaths = []
for path in orderedPaths:
if path in selection:
newOrderedPaths.append(path)
selection.remove(path)
newOrderedPaths.extend(selection)
orderedPaths[:] = newOrderedPaths
dialog = QtWidgets.QFileDialog(self)
dialog.setOption(dialog.DontUseNativeDialog)
dialog.setFileMode(dialog.ExistingFiles)
listView = dialog.findChild(QtWidgets.QListView, 'listView')
selectionModel = listView.selectionModel()
selectionModel.selectionChanged.connect(checkSelection)
orderedPaths = []
dialog.exec_()
print(orderedPaths)
Obviously, the same can be done using a subclass.

Add a large shapefile to map in python using folium

I am displaying a folium map in my application using python, PyQt5 and Qt designer. Since there is no map widget in Qt designer, I add a general widget and then promote it to my custom map widget. It all works fine. Here is the python code for my promoted widget:
import io
import folium
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
data = io.BytesIO()
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
This works fine and I can see the map in my application.
I am also trying to display a GIS shapefile on top of this map. I have done some research and it seems like I cannot add GIS shapefile (.shp) directly to a folium map. So, I try to convert it to json first and then add the json on top of the map. I modified my code as below to add the .shp file to map:
import io
import folium
import os.path
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
import geopandas as gpd
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
# converting shp to geojson
shp_file = gpd.read_file('input/2015_loaded_NoCC.shp')
shp_file.to_file('myshpfile.json', driver='GeoJSON')
shp = os.path.join('', 'myshpfile.json')
data = io.BytesIO()
folium.GeoJson(shp).add_to(m)
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
but now my map doesn't show up at all. It's just an empty space with no errors in the console or error log. If I save the map as an HTML file using "m.save('map.html')" though, it does save the file and when I open it, it displays the json file on the map, but for some reason, the way I am doing it to show the map in my application is not working after adding the shp-->json file. What am I doing wrong?
As already pointed out in these questions(1 and 2) and in the official docs:
void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl
= QUrl()) Sets the content of this page to html. baseUrl is optional and used to resolve relative URLs in the document, such as referenced
images or stylesheets.
The html is loaded immediately; external objects are loaded
asynchronously.
If a script in the html runs longer than the default script timeout
(currently 10 seconds), for example due to being blocked by a modal
JavaScript alert dialog, this method will return as soon as possible
after the timeout and any subsequent html will be loaded
asynchronously.
When using this method, the web engine assumes that external
resources, such as JavaScript programs or style sheets, are encoded in
UTF-8 unless otherwise specified. For example, the encoding of an
external script can be specified through the charset attribute of the
HTML script tag. It is also possible for the encoding to be specified
by the web server.
This is a convenience function equivalent to setContent(html,
"text/html", baseUrl).
Note: This method will not affect session or global history for the
page.
Warning: This function works only for HTML, for other mime types (such
as XHTML and SVG) setContent() should be used instead.
Warning: The content will be percent encoded before being sent to the
renderer via IPC. This may increase its size. The maximum size of the
percent encoded content is 2 megabytes minus 30 bytes.
(emphasis mine)
setHtml() does not support content greater than 2MB, so in your particular case there are 2 solutions:
Save the folium map in an html file:
import io
import os
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(location=[40, -120], zoom_start=10)
folium.GeoJson(shp_file_json_str).add_to(m)
tmp_file = QtCore.QTemporaryFile("XXXXXX.html", self)
if tmp_file.open():
m.save(tmp_file.fileName())
url = QtCore.QUrl.fromLocalFile(tmp_file.fileName())
self.view.load(url)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
def main():
app = QtWidgets.QApplication([])
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()
Use a QWebEngineUrlSchemeHandler to return the html:
qfolium.py
import json
import io
from PyQt5 import QtCore, QtWebEngineCore, QtWebEngineWidgets
class FoliumSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
self.m_app = app
def requestStarted(self, request):
url = request.requestUrl()
name = url.host()
m = self.m_app.process(name, url.query())
if m is None:
request.fail(QtWebEngineCore.QWebEngineUrlRequestJob.UrlNotFound)
return
data = io.BytesIO()
m.save(data, close_file=False)
raw_html = data.getvalue()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"text/html", buf)
class FoliumApplication(QtCore.QObject):
scheme = b"folium"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QtWebEngineCore.QWebEngineUrlScheme(self.scheme)
QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(self.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.m_handler = FoliumSchemeHandler(self)
profile.installUrlSchemeHandler(self.scheme, self.m_handler)
def register(self, name):
def decorator(f):
self.m_functions[name] = f
return f
return decorator
def process(self, name, query):
f = self.m_functions.get(name)
if f is None:
print("not found")
return
items = QtCore.QUrlQuery(query).queryItems()
params_json = dict(items).get("json", None)
if params_json is not None:
return f(**json.loads(params_json))
return f()
def create_url(self, name, params=None):
url = QtCore.QUrl()
url.setScheme(self.scheme.decode())
url.setHost(name)
if params is not None:
params_json = json.dumps(params)
query = QtCore.QUrlQuery()
query.addQueryItem("json", params_json)
url.setQuery(query)
return url
main.py
import io
import os
import folium
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
import geopandas as gpd
from qfolium import FoliumApplication
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
folium_app = FoliumApplication()
#folium_app.register("load_shapefile")
def load_shapefile(latitude, longitude, zoom_start, shp_filename):
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(
location=[latitude, longitude], zoom_start=zoom_start
)
folium.GeoJson(shp_file_json_str).add_to(m)
print(m)
return m
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
self.resize(640, 480)
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
params = {
"shp_filename": shp_filename,
"latitude": 40,
"longitude": -120,
"zoom_start": 5,
}
url = folium_app.create_url("load_shapefile", params=params)
self.view.load(url)
def main():
app = QtWidgets.QApplication([])
folium_app.init_handler()
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()

Why function defined in class is not visible in other fragments of code?

I am playing with python, being completely new at that. I wrote my first "serious" piece of code using tkinter and beautifulsoup and stuff and it worked. Now, trying to expand my knowledge I am re-writing it using pyqt5 and trying to use classes instead of "spaghetti" code.
My program in general works, it reads website, parses the html code with BeautifulSoup, gets required lines, etc. I used some "calculator" tutorial to base it on and with many trials and errors I made it work. Code below:
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout, QPushButton, QFileDialog, QMainWindow
from PyQt5.QtCore import Qt, QDate, QTimer, QEventLoop
from PyQt5.QtGui import QIcon, QPixmap
import sys
import os
import requests
from bs4 import BeautifulSoup
from operator import itemgetter
ERROR_MSG = 'ERROR'
class BinColUI(QMainWindow):
def createLabelTop(self):
self.label_top = QLabel('PLEASE WAIT')
self.label_top.setAlignment(Qt.AlignCenter)
self.label_top.setStyleSheet("font: 14pt Bahnschrift; color: yellow")
self.generalLayout.addWidget(self.label_top, alignment=Qt.AlignCenter)
def createLabelBot(self):
self.label_bot = QLabel('PLEASE WAIT')
self.label_bot.setAlignment(Qt.AlignCenter)
self.label_bot.setStyleSheet("font: 14pt Bahnschrift; color: yellow")
self.generalLayout.addWidget(self.label_bot, alignment=Qt.AlignCenter)
def setLabels(self, texttop, textbot):
self.label_top.setText(texttop)
self.label_bot.setText(textbot)
def createLabelImg(self):
label_img = QLabel()
label_img.setFixedSize(self.window().width(), 300)
label_img.setAlignment(Qt.AlignCenter)
image = 'img\pleasewait'
pixmap = QPixmap(resource_path(image+'.png'))
pixmap = pixmap.scaledToHeight(label_img.height(), Qt.SmoothTransformation)
label_img.setPixmap(pixmap)
self.generalLayout.addWidget(label_img, alignment=Qt.AlignCenter)
def setLabelImg(self, bin_color):
image = 'img\'+bin_color'
pixmap = QPixmap(resource_path(image + '.png'))
pixmap = pixmap.scaledToHeight(self.label_img.height(), Qt.SmoothTransformation)
self.label_img.setPixmap(pixmap)
def __init__(self):
super().__init__()
self.setWindowTitle('Bin Collection')
self.setFixedSize(500, 500)
self.setStyleSheet('background-color: #7C7D7B')
self.generalLayout = QVBoxLayout()
self._centralWidget = QWidget(self)
self.setCentralWidget(self._centralWidget)
self._centralWidget.setLayout(self.generalLayout)
self.createLabelTop()
self.createLabelImg()
self.createLabelBot()
class BinColCtrl:
def __init__(self, model, view):
self._evaluate = model
self._view = view
self.calculateResult()
def calculateResult(self):
line_top = parseGoodLines(0)
line_bottom = parseGoodLines(1)
self._view.setLabels(line_top, line_bottom)
self._view.
'''
Why the function setLabelImg from class BinColUi is not visible here?
I can call setLabel (as shown above) but no setLabelImg.
'''
def parseGoodLines(linia_number):
global bin_color
try:
if linia_number==0:
start_text='Your next collection is '
else:
start_text='After that: '
kosz_name = good_lines[linia_number][0]
kosz_date = good_lines[linia_number][1]
kosz_date_str = QDate.toString(kosz_date, 'dd MMMM yyyy')
ile_dni=QDate.currentDate().daysTo(kosz_date)
result = '%s%s\nYou need to put it outside before %s\nIt\'s in %s days' \
%(start_text, str.upper(kosz_name), kosz_date_str, str(ile_dni))
except Exception:
result = ERROR_MSG
return result
def resource_path(relative_path):
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
class MakeSoup:
def getDataFromWebsite(self):
URL = 'http://mydurham.durham.gov.uk/article/12690?uprn=100110375827'
page = requests.get(URL)
soup = BeautifulSoup(page.content, 'html.parser')
results = soup.find(id='page_PageContentHolder_template_pnlArticleBody')
return results
def mixSoup(self, dane):
linie_ze_strony = dane.find_all('p')
global good_lines
good_lines=[]
for kosz in linie_ze_strony:
linia_bez_p = str(kosz).replace('<p>', "")
linia_bez_p = str(linia_bez_p).replace('</p>', "")
kosz = linia_bez_p
if 'Your next ' in str(kosz):
if 'rubbish' in str(kosz):
rubbish_len = len(str(kosz)) - 1
date_rubbish = str(kosz)[33:rubbish_len]
if 'recycling' in str(kosz):
recycle_len = len(str(kosz)) - 1
date_recycle = str(kosz)[35:recycle_len]
qdate_rubbish = QDate.fromString(date_rubbish, 'dd MMMM yyyy')
qdate_recycle = QDate.fromString(date_recycle, 'dd MMMM yyyy')
good_lines.append(['Rubbish', qdate_rubbish])
good_lines.append(['Recycling', qdate_recycle])
good_lines.sort(key=itemgetter(1))
return good_lines
def __init__(self):
self.mixSoup(self.getDataFromWebsite())
def main():
bincol = QApplication(sys.argv)
view = BinColUI()
view.show()
MakeSoup()
model = parseGoodLines
BinColCtrl(model=model, view=view)
sys.exit(bincol.exec_())
if __name__ == '__main__':
main()
In class BinColUi I have some functions which I'm using later on to build and change some visual elements. In class BinColCtrl I change text on the labels using function setLabels form BinColUi class and it works alright. But when I try to call function setLabelImg from the same class it's not visible (see attaached pic) and I can't figure why. In fact only function setLabels is available.
Your IDE does not know the type of self._view. It only knows that self._view has a setLabels attribute because you just used that.
Annotate the variable with the correct type and your IDE can discover the method.
class BinColCtrl:
# ``: BinColUI`` tells the IDE the type of ``view`` and ``self._view``
def __init__(self, model, view: BinColUI):
self._evaluate = model
self._view = view
self.calculateResult()
It seems that your IDE doesn't know type of self._view object. It sees setLabels because it was used in the line above.
Try adding type annotation like following:
class BinColCtrl:
def __init__(self, model, view):
self._evaluate = model
self._view: BinColUi = view # <-- this type annotation might help
self.calculateResult()
You can find more about type annotations here

RuntimeError: deleteUI: Object 'Animation_Copy_Tool' not found

Getting this error when pressing the "Close Window" button in my UI. This button should delete the UI window but isn't. Full traceback:
Error: deleteUI: Object 'Animation_Copy_Tool' not found.
Traceback (most recent call last):
File "", line 36, in closeBtnCmd
RuntimeError: deleteUI: Object 'Animation_Copy_Tool' not found. #
# Animation Copy Tool
# Bakari Holmes 5/7/2015
# This is designed to copy and existing animation
# from one rig to another and make the process easier
# with a simple UI
import maya.cmds as mc
import functools
import maya.mel as mm
import pprint
class AnimCopyWindow(object):
##classmethod
def showUI(cls):
win = cls()
win.create()
return win
def __init__(self):
self.window = "Animation Copy Tool"
self.title = "Animation Copier"
self.size = (546,350)
def pasteTheseKeys(self, *args):
self.offsetVal = mc.intFieldGrp(self.int_offset, q=True, value1=True)
self.selObj_pasteKeys = mc.ls(sl=True)
for objectQuant in self.selObj_pasteKeys:
print objectQuant
self.ct = mc.currentTime(query = True)
self.t = self.ct + self.offsetVal
mc.currentTime(self.t)
# mc.selectKey(selObj_pasteKeys[objectQuant])
mc.pasteKey(time=(self.t,self.t), f=(1.0,1.0), option="merge", copies=1, to=0, fo=0, vo=0)
def closeBtnCmd(self,*args):
mc.deleteUI(self.window,window=True)
def create(self):
# check to see if window exists already
if mc.window(self.window,exists=True):
mc.deleteUI(self.window,window=True)
self.window = mc.window(self.window, title=self.title,widthHeight=self.size,menuBar=True)
self.copyAnim = mc.window(title="Transfer Animation Tool", backgroundColor=[0.3,0.3,0.3],sizeable=False,resizeToFitChildren=True)
#set the layout for UI
mc.columnLayout(adjustableColumn=True)
self.tx_src = mc.textFieldGrp(label="Source Object", editable=False, text=sel[0])
self.int_offset = mc.intFieldGrp(label="Frame Offset Amount", value1=0)
#add paste animation button
self.btn1 = mc.button(label="PASTE ANIMATION", command=self.pasteTheseKeys, bgc=[0.1,0.1,0.5])
#add close button window
self.btn2 = mc.button(label="CLOSE WINDOW", command=self.closeBtnCmd, bgc=[0.2,0.2,0.2])
mc.showWindow()
#################################
#####end of class definition#####
#################################
def keys_as_dictionary(channel):
"""return a dictionay of times:values for <channel>"""
keys = mc.keyframe(channel, q=True, tc=True) or []
values = mc.keyframe(channel, q=True, vc=True) or []
return dict(zip(keys, values))
def channels():
"""return a dictionary of <plug>:<channel_dict> for each animated plug selected"""
keys = mc.keyframe(sl=True, n=True, q=True)
result = {}
for k in keys:
plugs = mc.listConnections(k, p=True)[0]
result[plugs]= keys_as_dictionary(k)
return result
#store selected object info
sel = mc.ls(selection=True)
if (len(sel) != 1):
mm.eval("warning Must select one animated object;")
else:
mc.copyKey()
win = AnimCopyWindow()
win.create()
pprint.pprint(channels())
This error almost always means your UI element is not named what you think it is: Maya will automatically rename the items to make sure that no two siblings have the same name -- you can ask for "my_window" and get back "my_window123" . So you need to capture the actual name that is returned from cmds.window() or whatever ui command you use and delete that. Hard coded names are never reliable

Categories