Best way to edit long text in a QTableWidget cell? - pyqt5

What I want to achieve: an easier way to edit longer text (one or two paragraphs) in a QTableWidget cell.
The problem: resizeRows wraps the text in the cell when the table is displayed. However, if I try to edit the cell, all the text appears on one line which makes it awkward to edit.
What I've tried: I tried implementing a QTextEdit widget for editing:
class EditDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = super().createEditor(parent, option, index)
if isinstance(editor, QLineEdit):
textedit = QTextEdit(parent)
textedit.setPlainText(editor.text())
return textedit
That fixed the editing problem, although I have to rely on the edit box losing focus to save the text. However, after editing, the default QTableWidgetItem now shows HTML in the edited cell. I had thought that using setPlainText() would prevent this. The only text entered was "THE NOTE TEXT", seen in the middle of this screenshot:
If I then try to edit the cell again, the HTML is stripped out and just the original text ("THE NOTE TEXT") remains in the QTextEdit box until again I exit the editor.
Questions:
Is my approach a good way to implement multiline editing in a
QTableWidget?
If so, how can I get rid of the HTML markup that the
QTableWidgetItem is now displaying?

Since you only need a multiple line editor and no formatting (colors, text style, etc.) then use QPlainTextEdit.
The reason behind the returned HTML is that the default property (see the USER attribute) of QTextEdit is html: item delegates use the USER property as default to set the value of the editor and then set it back on the model, meaning that setEditorData() will behave like using setHtml() and setModelData() like toHtml().
The user property for QPlainTextEdit is just plainText, so there will be no conversion in either case.
Also note that your implementation of createEditor() will potentially raise an exception if the default editor is not a QLineEdit (if the data uses numbers or Qt dates/times), since textexit is only declared in the if, but that's a bad way to do so anyway (you're creating a widget that won't be used).
Besides, calling textedit.setPlainText(editor.text()) is useless, because at that point the editor is still empty and it will be filled only by setEditorData() (which would overwrite its contents anyway).
A more correct approach would check the data type and return the editor accordingly, then let the delegate fill the widget in its own.
def createEditor(self, parent, option, index):
if isinstance(index.data(), str):
return QTextEdit(parent)
return super().createEditor(parent, option, index)
Finally, be aware that many have tried to implement multiple line editors in item views, and very few attempts actually achieve a valid result. Many issues are related with the available item geometry, cursor navigation, UX aspects and focus management, and almost any solution has to accept some level of compromise that many users might find annoying. The most common acceptable (and less "hated") solution is to use an external editor, either shown in another part of the UI or through a dialog window.

Related

MagicDraw simplified view

I have used MagicDraw v18.5 to draw a class diagram. My diagram has classes, attributes, stereotypes, and notes.
My business users find my diagram too technical. I would like a way to give them a dumbed-down, business-friendly view that shows only classes and associations, without the attributes or stereotypes.
I tried the one thing I know: select everything in the diagram, edit the compartments, and hide the attributes and stereotypes. But the result looks terrible: the content of the compartments disappears as intended, but the compartments aren't resized. Each class is a box with a big empty Attributes compartment. I could never show this to a business user without manually resizing.
Is there a more straightforward way to do this?
Note this is using MagicDraw 19.0-SP2, so there might be some differences.
I'll use block elements as an example, instead of classes, but they are just "fancy" SysML classes. Your symbols probably look something like this as described:
with my empty compartments being "values" and "operations", instead of "attributes" and "stereotypes". The principle is the same for any compartment.
Suppress/Hide the compartments, rather than remove the properties from them.
Per the comments, it appears you have removed the elements from the compartments, rather than hiding/removing the compartments themselves. If you click on the class element symbol, on the left there should be small minus signs that you can click to "Suppress", or hide, each compartment completely.
Alternatively, you can click the "..." Compartments icon on the left at the top (to the left of the symbol name/header) and then remove the checkmark for any compartment you want to hide.
There is also an entry on the Compartments menu to "Suppress All" which will turn all of them off.
After suppressing the compartments, click the auto-size icon at the bottom right and you should get something with just the name:
Bulk Edit multiple symbols at once
You can select all the class symbols at once, and edit them in bulk, rather than following the above for each symbol on your diagram.
To do this, click on a class symbol, then use ctrl-shift-s to select all other symbols of the same type. If you click on multiple types of objects (e.g. hold shift and select both a class and a comment symbol at the same time), then it will select all of the symbols that match the type any of the elements already selected.
Now you can right-click and select symbol properties and will have access to the common properties of all those symbol types at once. Doing it this way will avoid snagging all element types, such as labels and relationships, which often do not have the same symbol properties, and therefore cannot be bulk edited together.
Alternatively, you can open or select the Properties window, then select the "Symbol" tab, to have access to the same properties for bulk edit. The other tabs in the Properties window provide a power means of editing element, tag, and traceability properties in bulk:
In either window, starting typing "Suppress" in the search bar, and you will get all the options for suppressing things from being visible on your selected elements. In the case of a SysML block, which can be True (checked), False (unchecked), or a black box with <undefined>, which will happen if some symbols are currently set True while others are False.
Setting the "Suppress Operations" and "Suppress Values" in my case are the two items I need to hide those compartments on my test Block symbol.
Bulk Autosize
After hiding the compartments, you can click on the auto-resize button in the bottom right, but I recommend going into the awful keyboard shortcut editor and struggling with it until you have setup a shortcut key for auto-size. Then you can hit ctrl-a to select all symbols at once, or ctrl-shift-s for all elements of a specific type, and use the shortcut key to autosize all of them at once.
They have updated it recently to make it not-quite-so-awful, so it's different than 18.5, so I'll leave the steps as an exercise for the reader. I use this shortcut all the time.
Apply word wrap if needed.
You can use the 'word wrap' symbol attribute to allow you to resize to a smaller size and have long names wrap into multiple rows, allowing you to make a symbol thinner.
Note: Word wrap only shows up under the "All" view, not even under "Expert" in Symbol Properties.
Switch from "Standard" or "Expert" to "All" in the view dropdown on the upper right of the symbol properties window.
Type "wr" in the search to find the "Wrap Words" property, and set it to true.
Now you can resize your symbol to a thinner version with a name or other properties that wrap onto multiple rows. Why they don't have this property set by default is beyond me...
Unfortunately, you will need to resize elements manually to get them to actually wrap the words, but once you have the width the way you want, auto-size will work fine.
You can also make everything really skinny, then use the "Layout -> Make same width" command to make all elements the same width as the widest selected symbol, if you want uniformity.

Programmatic access to Visio shape tooltip

To avoid an XY problem, here's what I'm trying to accomplish: when a shape is selected, I want detail text about that shape to appear on the screen.
I first tried using Shape Data, but it supports only single-line name=value pairs. My detail information is an arbitrary, multiline text blob.
My next thought was to used the shape's ScreenTip (aka tooltip) to hold the text data, then write some VBA code to handle the _SelectionChanged event. When a shape is selected I want to copy it's ScreenTip text into the text of another object (my details panel).
I got the _SelectionChange event-handling working, but poking around the Selection object in the debugger I can't find any property of the selected object that exposes the ScreenTip information.
Is Visio's programming API too anemic to support his kind of thing? Is there another way I might be able to do this? Is there another tool that might be better for this (preferably free)?
Visio's API is capable of doing this, handily.
It seems you're not aware of Visio's shapesheet, which is where the screen tip text is stored, along with pretty much anything you'd want to know about a shape.
To access the screen tip text you simply read the Comment cell from the selected shape's shapesheet:
Application.Selection(1).CellsU("Comment").ResultStr(visNone)
This code will return the comment text.
You're on the right track using the SelectionChange event, though of course you're checking that the selection count = 1, or at least >0.

Changing text with Labview Toggle switch?

I'm new to Labview and was wondering if the following was possible:
Can you switch the same text box using a toggle switch? I have tinkered around and figured that I need the selector when displaying output but can't seem to get anywhere.
ex. When toggle switch is true = show a text on front panel that says on
When toggle switch is false = change the SAME text to off.
While I reckon there is more to your question, without any constraints, this is this the easiest way: use a string indicator and give it a new value when the toggle switch changes position.
Example
VI Snippet
The previous answer is good, but to be more precise consider using select instead of case structure. The select works lighter in LabVIEW.
In situations when you just have to pass this or that data (your case), not perform code (ex: Popup something in true case and enable a counter in false case) you should consider using selector, as in case structure you create additional tunnel, which can be avoided using Select.
If you just want some text next to the switch that changes to show the state of the switch, you can do this without any code:
Right-click on the toggle switch and choose Show > Boolean Text.
You should now see the word ON or OFF in the middle of the switch.
Right-click on it again and choose Advanced > Customize….
LabVIEW will now open the control editor window. The first button in this window's toolbar looks like a spanner (wrench); click it and it should change to a pair of tweezers.
Select the ON or OFF text and move it to where you would like it to be, relative to the switch itself.
Close the control editor window. You can save the control as a separate file if you want to reuse it elsewhere, but if you don't it will still be saved as part of your VI.
You can edit the Boolean Text on your VI's front panel in the normal way. Note that clicking on the boolean text when the VI is running will operate the switch - you may or may not want that to happen. You could get round this by placing a transparent decoration box on top of the text.
All Boolean controls (I think) have a Boolean Text property, but not all of them have it visible by default.

In RealStudio, how can I intercept pasting of rich text?

I'm trying to create a simple text field for WYSIWYG editing. However, I only want to allow certain types of formatting (e.g. Bold, Italic, Underline and a single heading type but no colors or different fonts.)
The issue is if I use an editor that can accept formatting, someone can create or copy formatted text in another program, then simply paste it into the text field and all that formatting goes with it, allowing things I'm not interested in, such as different fonts, colors, etc. I don't want to allow that.
At best, I want to automatically strip out any formatting that I don't support. At worst, I want to simply paste whatever as plain text making them have to reformat it. But in no case do I want to just dump the clipboard to the text area.
Any thoughts on how to do this?
I would recommend creating a new text field/text area class and creating an EditPaste menu handler that (a) does what you're looking for in terms of handling the clipboard's text and (b) returns true to prevent the default pasting from happening. This is safer than using the Key down events because the user might manually select paste from the edit menu.
You can access the text on the clipboard by creating a Clipboard object.
To subclass the textfield and intercept the paste menu command:
With your Project open, go to Project Menu > Add > Class
Select the new class in the project's tab and in the properties panel set the super to TextField
Double-click on the class to edit it
Click the "Add Menu Handler" mid-toolbar button in your class
Change the menu item name to "EditPaste". Put your code in before the "return true" and be sure to leave the return true in there.
Your code can then format and paste the text manually and override the default paste function.
Any command-V or control-V in that text field will cause that menu handler to fire. Any contextual menus would be added by you anyway since real basic does not create the default contextual menus, so you'd have control over that as well.
To add the text field to a window, just change the filter above the objects list to Project controls, and drag the class in from there.
You could intercept the paste yourself by intercepting it in the KeyDown events. Then, you could look to parse it yourself. That could be kind of tricky but I think that's about the only way you could do it.
It might just be easier to parse the resulting StyleRun after the paste and strip out formatting you don't want.
Alternately, you could look at the Formatted Text Control from True North Software and override the paste methods of the control (you get all the source) and just handle it yourself.
Either way, I think it will be a fair amount of work.

How to set value of NSComboBox by KVC?

I have several NSComboBoxes created in Interface Builder, and one created programmatically.
The NSComboBoxes created in Interface Builder are perfect. I have their Value bound to the Shared User Default Controller, and when their view is opened they are sitting there with the correct value. Importantly, this value is shown as "normal" text in the NSComboBox. In other words, the value doesn't appear selected. This is what I want.
For the NSComboBox that is created programmatically, I set the value using selectItemAtIndex:0. When I do this, the correct item is selected--but the text appears selected. I.e., it is highlighted and everything. I don't want this. Here are the workarounds I've attempted:
(i) Get the field editor and set insertion point to the end of the text. This doesn't work although, oddly, the field editor's string is either nil or empty when doing this. I'm not sure if this is correct behavior for the field editor.
(ii) Trying various other ways of setting the combo box's value, such as setObjectValue, takeStringValueFrom, etc.
(iii) Finally, and most frustratingly, I tried to set the value of the NSComboBox using [myComboBox setValue:#"The Default Item" forKey:#"value"]; This fails with objc_exception_throw, presumably because there is no such KVC key. But I know that the value of the combo box can be set by KVO, because it works in interface builder! I'm guessing that I don't know the correct key path. I tried to enumerate all the properties using introspection, but I can't get the code working right (objc_property_t isn't in the expected headers).
So, I have two questions:
First, does anyone know how to set a default value for NSComboBox programmatically, so that the text in the box isn't selected? I will go to any lengths to do this, including a new NSComboBoxCell subclass, if it comes to that.
Second, can somebody tell me what key or key path IB is using to set the value of an NSComboBox? Or alternatively, why my efforts to do this are failing?
I've been working on this for many hours now and I am truly disspirited!
THANK YOU, mustISignUp! So nice to have this fixed. A little bit of followup:
(i) Selection of the text is definitely caused by focus. Calling setRefusesFirstResponder:YES fixes the problem. Unfortunately, the window really wants to focus on this combo box, as setting refusesFirstResponder back to NO (later, after window inititation) causes text selection again (I do want the user to be able to focus on this box if he desires). Therefore, in my case, the definitive solution was to call [window makeFirstResponder:otherControl]. Oddly, though [window makeFirstResponder:nil] doesn't work. Any ideas why?
(ii) Thanks for pointing out the difference between bindings and properties. I learned a lot while looking into this question. For one, I learned that you can get a list of bindings by calling - (NSArray *)exposedBindings, which for NSComboBox returns (fontSize, alignment, toolTip, fontName, enabled, contentValues, fontFamilyName, font, hidden, fontItalic, textColor, value, content, editable, fontBold). Second, I was able to set the value using [myComboBox bind:#"value" toObject:[NSMutableString stringWithString:#"defaultValue"] withKeyPath:#"string" options:nil], where NSMutableString has a category on it that turns "string" into a property. Finally, this actually doesn't fix the text selection "problem". The difference between text selection with this combo box and those in Interface Builder must be their position in the window...I guess that this combo box is just slated to become initialFirstResponder while the others weren't.
So my only remaining question might be why [window makeFirstResponder:nil] doesn't work to take focus off the combo box. Not super-important, but I'd be curious if anybody has an idea.
Firstly, i think the text is selected because calling selectItemAtIndex: has made the comboBox the firstResponder. You could use setRefusesFirstResponder:YES or you could make another item the first responder to make the text not appear selected.
If i have understood correctly and you want to change the selection of the comboBox you are doing it the right way.
Secondly, you are confusing Bindings and KVC. NSComboBox has a binding called 'value', not a property called 'value'. It is meaningless to try to set it with setValue:forKey:, and Interface Builder definitely isn't doing this.
You would be right in thinking this is un-obvious and confusing and maybe better names could have been chosen for the bindings.