PyQt4 - QGIS form error - python

I've to build a form in QGIS to customize data input for each polygon in the shapefile.
I use QtDesigner to create a form (.ui), with some textboxes and comboboxes pointing to the fields of my shapefile.
Then I use the python file from Nathan QGIS Blog to add some logic.
Python code:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
nameField = None
myDialog = None
def formOpen(dialog,layerid,featureid):
global myDialog
myDialog = dialog
global nameField
nameField = dialog.findChild(QTextEdit,"PART")
buttonBox = dialog.findChild(QDialogButtonBox,"buttonBox")
nameField.textChanged.connect(Name_onTextChanged)
# Disconnect the signal that QGIS has wired up for the dialog to the button box.
buttonBox.accepted.disconnect(myDialog.accept)
# Wire up our own signals.
buttonBox.accepted.connect(validate)
buttonBox.rejected.connect(myDialog.reject)
def validate():
# Make sure that the name field isn't empty.
if not nameField.text().length() > 0:
nameField.setStyleSheet("background-color: rgba(255, 107, 107, 150);")
msgBox = QMessageBox()
msgBox.setText("Field PART must not be NULL.")
msgBox.exec_()
else:
# Return the form as accpeted to QGIS.
myDialog.accept()
def Name_onTextChanged(text):
if not nameField.text().length() > 0:
nameField.setStyleSheet("background-color: rgba(255, 107, 107, 150);")
else:
nameField.setStyleSheet("")
So I open an edit session in QGIS and I click on a polygon with Identify tool, but when I clik on OK button on my customized form, regardless field PART is NULL or not, the following error occurs:
ERROR CODE LINE >>>> if not nameField.text().length() > 0:
ERROR MESSAGE >>>> AttributeError: 'str' object has no attribute 'text'
I'm running QGIS 1.7.4, Python 2.7.2, Windows 7 64-bit.
I miss something... Please, anybody can help me?

It looks like you have a Python error more than a problem with QGIS.
You have two instances of if not nameField.text().length() > 0:
def validate():
if not nameField.text().length() > 0:
and
def Name_onTextChanged(text):
if not nameField.text().length() > 0:
Initially, it looks like nameField is not an input for either of these functions. So I guess these are assigned somewhere else and you've reduced the code example. Also, you have text as a variable input for 'Name_onTextChanged' but you also try and use it as a function 'nameField.text().length()'. This might be a problem.
Generally, Python is complaining because it cannot perform the operation 'text()' on the variable nameField, which it believes is a string. There is no text() function available for strings. And it looks like nameField is actually supposed to be a QTextEdit object.
If nameField is a QTextEdit object, then you can use toPlainText() instead which should do what you need it to do. So something like
if not nameField.toPlainText().strip().length() > 0:
In this instance, I have included .strip() as well so that you do not get a positive result if there are white spaces in text field.
Does that help at all?

Related

How to display code in Streamlit based on user answer?

I'm trying to create a tutorial for a library with Streamlit. My overall idea ist to walk through the different functions and classes and explain them together with user based Input, so everything becomes a litte bit more understandable for beginners.
However, I've written 5 Tutorials previously for more experienced users and would like to reuse some of that code by calling it from within my app and to only maintain it once.
Additionally, I'm walking through a lot of functions and classes, example config files e.g. and I'm calling them from a dict.
As Streamlit offers with st.echo an Option to run code and and then display it I've tried this. Also I've tried to use the python inspect Element together with st.write. However, st.echo simply displays the function name, and st.write together with inspect simply displays a string.
display_code = st.radio("Would you like to display the code?", ("Yes", "No"))
if display_code == "Yes":
with st.echo():
example_function_1()
else:
example_function_1()
Basically I'm looking for an option to pass a function and based on user Input simply run it or run it and display the code and comments to it.
So if the user selected "Yes", the Output would be, while also x,y are returned.
def example_function_1():
"""
This is and example functions that is now displayed.
"""
Some Magic
return x, y
And if the user selected No, then only x,y are returned
Here's a modified version of Streamlit's st.echo() that includes a checkbox:
import contextlib
import textwrap
import traceback
import streamlit as st
from streamlit import source_util
#contextlib.contextmanager
def maybe_echo():
if not st.checkbox("Show Code"):
yield
return
code = st.empty()
try:
frame = traceback.extract_stack()[-3]
filename, start_line = frame.filename, frame.lineno
yield
frame = traceback.extract_stack()[-3]
end_line = frame.lineno
lines_to_display = []
with source_util.open_python_file(filename) as source_file:
source_lines = source_file.readlines()
lines_to_display.extend(source_lines[start_line:end_line])
initial_spaces = st._SPACES_RE.match(lines_to_display[0]).end()
for line in source_lines[end_line:]:
indentation = st._SPACES_RE.match(line).end()
# The != 1 is because we want to allow '\n' between sections.
if indentation != 1 and indentation < initial_spaces:
break
lines_to_display.append(line)
lines_to_display = textwrap.dedent("".join(lines_to_display))
code.code(lines_to_display, "python")
except FileNotFoundError as err:
code.warning("Unable to display code. %s" % err)
You can use it exactly as you'd use st.echo. For example:
with maybe_echo():
some_computation = "Hello, world!"
st.write(some_computation)
You can use session state to pass on user input into on-screen actions. A clear example with radio buttons can be found here. Generally speaking, you need to use st.write() to accomplish this. A simplified example with a slider:
import streamlit as st
x = st.slider('Select a value')
st.write(x, 'squared is', x * x)
What you are looking for is not exactly possible, since you have to specify the function within the with st.echo() block. You can see it here:
import inspect
import streamlit as st
radio = st.radio(label="", options=["Yes", "No"])
if radio == "Yes":
with st.echo():
def another_function():
pass
# Print and execute function
another_function()
elif radio == "No":
# another_function is out of scope here..
another_function()
For this task you can use st.code().
import streamlit as st
with open("test.py") as f:
lines_to_display = f.read()
st.code(lines_to_display, "python")

PickObjects with ISelectionFilter will not allow me to select anything, why?

I am trying to enable selecting one single space for use in Revit MEP 2019 by using the GUI and store the selection for further use in scripts. The code is written in pyRevit. The script runs both from the shell and from the addin button, but when entering the selection mode (PickObject method), I am not allowed to select anything at all. I don't get any errors, it's just that nothing is selectable when entering the selection tool in the GUI.
I have commented in the code what I have tried that didn't work.
from Autodesk.Revit import DB,UI
from Autodesk.Revit.DB import BuiltInCategory
from Autodesk.Revit.UI.Selection import ISelectionFilter,ObjectType
# Definitions:
# Define a space selection filter so that only spaces are selectable
class SpaceSelectionFilter(ISelectionFilter):
def AllowElement(element):
#if element.Category.Name == "Spaces":
#if element.ToString() == "Autodesk.Revit.DB.Mechanical.Space":
if element.Category.Id.IntegerValue== int(BuiltInCategory.OST_MEPSpaces):
return True
return False
def AllowReference(reference, point):
return False
# Function that enables using PickObject from the PythonRevitShell
def shell_pickobject():
__window__.Hide()
elementReference = uidoc.Selection.PickObject(UI.Selection.ObjectType.Element,spaceFilter,"Select a space(room)")
__window__.Show()
__window__.TopMost = True
return elementReference
# Procedure:
# Create a selection filter
spaceFilter = SpaceSelectionFilter()
# User picks a space
ref = shell_pickobject()
# The following line works also outside of the shell_pickobject() function when used from the GUI addin-button, but spaces are still not selectable.
# elementReference = uidoc.Selection.PickObject(UI.Selection.ObjectType.Element,spaceFilter,"Select a space(room)")
I don't understand where the problem is, my best guess is inside the filter definition. The help string "Select a space(room)" displays correctly in the bottom left corner, and everything but the viewport turns grey like it should when I am supposed to select something in the view. The mouse turns into some kind of "forbidden" symbol.
I would very much appreciate some help with this. Thank you in advance to anyone who might wish to help!
You can find examples in pyRevitMEP source code. I also did an article explaining how to use ISelectionFilter : [Revit] ISelectionFilter example using python. Here is one example (running with revitpythonshell) :
from Autodesk.Revit.UI.Selection import ISelectionFilter
class CustomISelectionFilter(ISelectionFilter):
def __init__(self, category_name):
self.category_name = category_name
def AllowElement(self, e):
if e.Category.Name == self.category_name:
return True
else:
return False
def AllowReference(self, ref, point):
return true
try:
ductsel = uidoc.Selection.PickObject(ObjectType.Element,
CustomISelectionFilter("Ducts"),
"Select a Duct")
except Exceptions.OperationCanceledException:
TaskDialog.Show("Operation canceled","Canceled by the user")
__window__.Close()
You can find another example running under pyRevit explained here : [pyRevitMEP] ConnectTo : connect MEP elements

Maya 2018 Python+QT: Querying Value of Imported TextField

I've been following This Tutorial on using a .UI file from QT designer in a Maya plugin. It states that, in order to query the value of a QtextEdit field after the UI has been loaded into Maya, I need to do the following:
So now when we load our QT Ui inside of maya we can query the text of
our line edit every time we want to by using the following line of
code:
pm.textField('textFieldName', query = True, text = True)
However I can't seem to get this to function. I'm loading the UI as follows:
# Load our window and put it into a variable.
ebWin = cmds.loadUI(uiFile = self.BE_UIpath)
No issues there, when I try cmds.showWindow(ebWin), everything works and looks exactly as intended. Now, when I try to query the QtextEdit I've named 'exportDirectoryTF', Maya insists it does not exist. I've tried two different approaches:
approach A:
# Connect Functions to the buttons.
exportDir = ebWin.textField('exportDirectoryTF', query = True, text = True)
which outputs:
# Error: 'unicode' object has no attribute 'textField'
# # Traceback (most recent call last):
# # File "C:/Users/Censored/Documents/maya/2018/plug-ins/EB_pi_cmds.py", line 39, in doIt
# # exportDir = ebWin.textField('exportDirectoryTF', query = True, text = True)
# # AttributeError: 'unicode' object has no attribute 'textField'
and approach B:
import maya.cmds as cmds
# Connect Functions to the buttons.
exportDir = cmds.textField('exportDirectoryTF', query = True, text = True)
which returns:
# RuntimeError: Object 'exportDirectoryTF' not found.
# # Traceback (most recent call last):
# # File "C:/Users/Censored/Documents/maya/2018/plug-ins/EB_pi_cmds.py", line 39, in doIt
# # exportDir = cmds.textField('exportDirectoryTF', query = True, text = True)
# # RuntimeError: Object 'exportDirectoryTF' not found. #
The tutorial has 'pm.textField('textFieldName', q = True, text = True)', and I can't figure out where the "pm" came from, if it is supposed to indicate the variable from loading the UI or the maya Python textField command, or neither.
If anyone could point me in the right direction here, it would be greatly appreciated.
From your code it is not visible at which time you try to execute the textField cmd. This code below works fine for me. The test.ui only contains a widget with a lineEdit field called "lineEdit". Querying the text field only works if the window is visible. If you close the window and try to query the text field, you get the "object not found" error.
ui = "D:/temp/test.ui"
qtW = cmds.loadUI(uiFile = ui)
cmds.showWindow(qtW)
cmds.textField("lineEdit", query=True, text=True)

Maya Render Settings Problems

I had 2 questions in which I am not sure if this can by done in-scene using python.
My Maya version is not installed with any Mental Ray. There are times in which when I opened files (that was installed with Mental Ray), I keep getting errors such as:
// Warning: file: /apps/Linux64/aw/maya2014/scripts/others/supportRenderers.mel line 77: The renderer "mentalRay" used by this scene, is not currently available. The Maya Software renderer will be used instead. //
// Error: file: /apps/Linux64/aw/maya2014/scripts/others/supportRenderers.mel line 82: setAttr: The attribute 'defaultRenderGlobals.currentRenderer' is locked or connected and cannot be modified. //
// Error: file: /apps/Linux64/aw/maya2014/scripts/others/unifiedRenderGlobalsWindow.mel line 415: The renderer mentalRay is not registered yet. //
// Error: line 1: The renderer mentalRay is not registered yet. //
I tried using the following code to 'rectify' the issue:
list = cmds.listAttr("defaultRenderGlobals", l=True)
for item in list:
cmds.setAttr("defaultRenderGlobals." + item, l=False)
mel.eval('updateCurrentRendererSel("unifiedRenderGlobalsRendererSelOptionMenu");')
mel.eval('loadPreferredRenderGlobalsPreset("mayaHardware");')
but then I will get another bunch of error if I tried to open up my Render Settings
//Error: Object ‘tabForm’ not found.
And so, are there any ways in which this can be remedied in-scene
Attached is the screenshot:
Note: See the "Update" section below in this answer to find the full solution.
Why don't you just try unlocking and setting the currentRenderer value using setAttr itself.
cmds.setAttr("defaultRenderGlobals.currentRenderer", l=False)
cmds.setAttr("defaultRenderGlobals.currentRenderer", "mayaHardware", type="string")
You are getting the error //Error: Object ‘tabForm’ not found. because the render settings window failed to load, probably because of unregistered mentalRay. So AVOID calling the following until current renderer is changed:
mel.eval('updateCurrentRendererSel("unifiedRenderGlobalsRendererSelOptionMenu");')
mel.eval('loadPreferredRenderGlobalsPreset("mayaHardware");')
Update:
From the updates in the question and the comments below, we come to understand that the problem here is that Maya fails to construct the render settings window's UI properly when it encounters a missing renderer or render settings errors. This leads to parent UI components, like the tabs and frames to not being built. As a result, when the renderer is switched, the render settings UI tries to load the corresponding settings into these tabs but cannot find them and stops.
To work around this, we can just set the render settings we want, delete the render settings window's UI completely and reload it. I wrote a quick function for this. This will fix it.
import maya.cmds as cmds
import maya.mel as mel
def remake_render_settings_ui(renderer="mayaSoftware"):
""" Remakes the render settings window """
# Unlock the render globals' current renderer attribute
cmds.setAttr("defaultRenderGlobals.currentRenderer", l=False)
# Sets the current renderer to given renderer
cmds.setAttr("defaultRenderGlobals.currentRenderer", renderer, type="string")
# Deletes the render settings window UI completely
if cmds.window("unifiedRenderGlobalsWindow", exists=True):
cmds.deleteUI("unifiedRenderGlobalsWindow")
# Remake the render settings UI
mel.eval('unifiedRenderGlobalsWindow;')
if __name__ == "__main__":
remake_render_settings_ui(renderer="mayaHardware")
Caveat: This will not prevent the UI from getting lost again if the faulty renderer is somehow selected again. To prevent that, it is better to unload the renderer's plugin. In any case, if the above method is called again, the window should be fixed.
Hope this was useful.
There are a few problems that arises in the Render Settings while opening a scene file that contains traces of Mental Ray in a machine installed with no Mental Ray plugin
For some reasons, despite unlocking and setting a renderer in the defaultRenderGlobals in the scene, the render settings will continue to have problems as mentioned in the thread post or comments in kartikg3 answer.
I found a workaround which is -
Unlock the defaultRenderGlobals
Save the said file Delete any existing unifiedRenderGlobalsWindow
UI + a few more mel commands
Reload/Re-opening the scene
Seems to me that doing the first 2 steps within the scene, it does not rectify the issues in the Render Settings window unless I either close the current scene file by opening a new file session or reopen the file itself...
import maya.cmds as cmds
import maya.mel as mel
def unlockRenderer(renderer="mayaHardware2"):
print "Unlocking and resetting current renderer"
# Unlock the render globals' current renderer attribute
cmds.setAttr("defaultRenderGlobals.currentRenderer", l=False)
# Sets the current renderer to given renderer
cmds.setAttr("defaultRenderGlobals.currentRenderer", renderer, type="string")
def saveFile():
# Prompts User to resave the file, removing traces of Mental Ray
mel.eval('SaveSceneAs;')
def reloadScene():
recentFiles = []
try:
recentFiles = cmds.optionVar( query = 'RecentFilesList' )
except:
cmds.error("No recent files found!")
curFile = cmds.file(query =True, loc = True)
if curFile == "unknown":
cmds.confirmDialog(title = 'Reload Scene', message = ('Reload Last Opened Scene?\n\n' + recentFiles[len(recentFiles)-1]), button = ['Cancel','OK'], defaultButton = 'OK', cancelButton = 'Cancel', dismissString = 'Cancel' )
cmds.file( str(recentFiles[len(recentFiles)-1]), force = True, open = True)
print "Opening Last Recent File - ", recentFiles[len(recentFiles)-1]
else:
cmds.confirmDialog(title = 'Reload Scene', message = ('Reload Current Scene?\n'), button = ['Cancel','OK'], defaultButton = 'OK', cancelButton = 'Cancel', dismissString = 'Cancel' )
curFileLoc = cmds.file(query = True, location = True)
cmds.file( curFileLoc , force = True, open = True)
print "Re-Opening current file - ", curFileLoc
def main():
unlockRenderer(renderer="mayaHardware2")
saveFile()
if cmds.window("unifiedRenderGlobalsWindow", exists=True):
cmds.deleteUI("unifiedRenderGlobalsWindow")
mel.eval('resetAE()')
mel.eval('buildNewSceneUI;')
reloadScene()
main()
Something to note - At times, some errors such as #// Error: file: /apps/Linux64/aw/maya2014/scripts/others/unifiedRenderGlobalsWindow.mel line 1074: setParent: Object 'unifiedRenderGlobalsWindow' not found. // is still encountered, even after the file is reopened. It may differs accordingly to scene file

How to properly implement tkMessageBox in Python3.4?

I want to launch a warning using tkMessageBox in Python3. This warning is supposed to launch when a user doesn't select an element from a listbox. Unfortunately whenever I try to implement message box it does not launch like it is supposed to. I have code for a script called pietalkgui.py which contains the code where I want to implement the message box:
from tkinter import messagebox
# Gives warning if no user is selected for whisper
def whisperwarning(self):
# show warning to user
showwarning("Select User","Select a user to whisper to!")
# Handles whisper
def whispermessage(self):
# stores element selected in temp variable
temp = self.userslist.get(self.userslist.curselection())
# if no item is selected from userslist (listbox)
if temp == "":
# launch warning to user if no item is selected
self.whisperwarning()
else:
# retrieves usernames from userslist
username = temp
# storing whisper
outwhisper = ' /w "' + username +'" ' + self.messagebox.get("0.0",END)
# handling whisper
self.handler(outwhisper)
# erase message in message box
self.messagebox.delete("0.0",END)
Am I doing something wrong in the implementation of tkMessageBox? Or am I not properly checking if not item is selected from the listbox?
It appears that you are calling the method showwarning, but haven't defined it or imported it. That is the name of a function the messagebox module, so perhaps you need to change this:
showwarning("Select User","Select a user to whisper to!")
... to this:
messagebox.showwarning("Select User","Select a user to whisper to!")
Also, FWIW, this code is slightly incorrect: self.messagebox.delete("0.0",END) -- text indices start at "1.0", not "0.0".

Categories

Resources