Fanfiction – Taylor Inverse

Taylor Inverse by Racke

Crossover: Worm/Slayers
Length: 4,497
Status: Oneshot

…and another Worm one. This time, a funny little Slayers crossover crackfic.

Since it’s just a oneshot, there’s not much to say about it. The gist is that Taylor is the reincarnation of Lina Inverse and, after the incident at Winslow, she assumes she needs to go on the run and winds up wandering around Brockton Bay, frustrating heroes and villains alike by not acting like a cape should.

What do I mean by that? She acts like Lina Inverse, but with a baseball bat and a wariness of inviting Mazoku in if she tries anything less drastic than a spell sourced from L-sama. Hero? Villain? Pshaw. She’s just wandering around, beating up any villains she encounters effectively enough that they assume she’s some kind of combat Thinker, and then blowing the money at the nearest good restaurant.

Here’s Coil’s opinion on it:

Coil very emphatically didn’t like Taylor Hebert.

The girl was a blind-spot in his plans, seeming to be somehow partially immune to his powers, which didn’t make any sense unless he started to try figuring out exactly how his powers worked.

He could split a time-line between two options, for however long he wanted, and then pick the time-line that benefited him the most. In other words, as long as there was an opportunity to win, he could be expected to grasp it. As long as he only needed to pick between two options.

However, any time that he gave an order directly related to Taylor Hebert, one of his time-lines would destabilize. Sometimes it would be the one where he told his mercenaries to stand down, other times it would be the one where he told them to attack.

There didn’t seem to be any rhyme or reason to which one collapsed, and it was driving him spare.

The fact that the girl had somehow recruited the Thinker that Coil had plans for, and was now squandering the potential asset by meandering around the city with a baseball-bat? That was just salt in the wound.

I won’t spoil the punchline at the end when she’s invited to an endbringer fight, but I will say it’s funny.

5 out of 5. Everything you want in a oneshot that’s of the “crackfic that takes itself seriously” variety.

…Just don’t post reviews on the fic. I recognize the author from another fic I read, they made it very clear in their author’s notes that they don’t want constructive criticism, and I don’t see the point in posting commentary at all if it’s just vapid “This was great! Moar!” stuff that a bot could post.

I’m honestly surprised they’re on Fanfiction.net rather than AO3, since their attitude seems more in line with AO3’s “don’t critique unless requested” culture and AO3 has an upvote-like “Give Kudos” button for “This was great! Moar!”.

Posted in Fanfiction | Leave a comment

The Out-of-Touch Autism-Spectrum Shut-in Social Survival Guide For People Who Are Paranoid About People, Parties, And Other P Words

I don’t think I’ve mentioned it, but I tend to panic when I badly misjudge how something I said will be taken and then fail to recover. I recently learned that this is apparently called Rejection Sensitive Dysphoria. and I certainly have at least some of the “overcompensate and bend over backward in a desperate attempt to keep themselves in others’ good graces” part.

…so, while I work toward solving that, I thought some good might as well come of it, so here are my tips for when you really need to stay in people’s good graces with minimal stress:

  1. Socialize in non-realtime, textual media where you have time to think through what you want to say, it’s more excusable to take a break if you’re feeling impulsive, and it may help to keep you emotionally distanced. (This also protects you from being attacked for interrupting or talking over someone, whether because you have problems with impulsive behaviour or because they’re not giving you a chance to speak.)
  2. It’s OK to not have an opinion on something. (There’s supposedly an excellent Marcus Aurelius quote about this, but I’m having trouble confirming the cited source.)
  3. Don’t imply an opinion of someone’s knowledge/competence before you know. (“What have you tried so far?”, not “Have you tried [thing they might have been asked a million times before]?”)
  4. Find the most “give them the benefit of the doubt” way to interpret everything they say. If you’re wrong, they’ll demonstrate it soon enough but, if you’re right, you help to keep the conversation from falling apart. (This is actually something I picked up from a textbook for a university course on constructive discourse and debate.)
  5. If your perspective differs from someone else’s, express it as a “I was under the impression that… What am I missing?” question. (I also recently saw the phrase “help me understand” suggested as being very useful for keeping the conversation from taking a dive. (The example given being to ask someone to explain the behaviour of some code in a case that you think is buggy, rather than starting by accusing it of being buggy.)
  6. Double-check your facts against sufficiently reputable sources before stating them. (i.e. No matter how certain you are, at least search Wikipedia. It’ll take 30 seconds and you’ll surprise yourself sooner or later. If it’s a quote, turn to Wikiquote. They care about proper citations. If it’s not there, check the Talk page to see if it’s a common misattribution.)
  7. Don’t get defensive if a group you belong to is stereotyped or attacked… especially if they’re accusations of tribalism, over-sensitivity, immaturity, or something else that getting defensive will just be evidence of. (Look for some kind of “Yeah. They really suck.” response that sympathizes, de-escalates, and implicitly indicates that you don’t see yourself as part of the same sub-group as the people they’re basing their generalization on.)
  8. If you are guilty of a mistake that’s called out because you didn’t know any better, apologetically admit you’re trying to change. (People are more forgiving of people who are trying to change and as long as you are genuinely trying to change now, they don’t need to know whether you started trying a moment ago or a year ago.)
  9. Focus on what you agree on, not what you disagree on. (They’re unlikely to change their opinion based on the views of some random stranger, so it’s better to part on peaceful terms rather than stirring up needless strife. If you continue to interact, a non-hostile first impression will help to build their opinion of you as someone whose opinion may be worth considering.)
  10. If it’s a community you are going to be interacting with on an ongoing basis, rather than a one-time interaction, keep an eye out for in-jokes and slang to be learned, but don’t overestimate your ability to use them correctly. (i.e. Try to have a good sense of what their intent is so you can react appropriately, but err on the side of caution in which acceptable reaction you choose.)
  11. Don’t use language which can be taken as accusatory. (“I just can’t seem to find the right words”, not “You keep misunderstanding”.)
  12. Watch out for phrasing that comes across as “changing the subject to make it about you”. (i.e. Look for opportunities and phrasings that make it feel like you’re contributing to the existing direction of the topic.)

I don’t always remember to follow those rules, but I try. Heck, the last two were instrumental in leaving one probable troll over on Reddit very frustrated.

…now watch as some troll tries to use this as a roadmap for dismantling my armour and attacking me more successfully. People online suck. (See also Penny Arcade’s March 19, 2004 strip if you’re OK with a bit of profanity)

P.S. If anyone wants to try tracking down the source themselves, the supposed Marcus Aurelius quote is “You are not compelled to form any opinion about this matter before you, nor to disturb your peace of mind at all. Things in themselves have no power to extort a verdict from you.” but I’m having trouble confirming it to be from Meditations, Book 6 because Project Gutenberg’s copy uses a very different translation.

UPDATE 2022-10-18: I also like NYKevin’s strategy laid out here.

Posted in Web Wandering & Opinion | Leave a comment

Fanfiction – A Wand for Skitter

A Wand for Skitter by ShayneT

Crossover: Worm/Harry Potter
Length: 359,078
Status: Complete

Continuing on the Worm crossover kick I seem to be on, how about an “adult Taylor Hebert reborn in the body of an 11-year-old muggleborn” story that is unusually good at focusing on the power dynamics of school children and portraying non-antagonistic dynamics between Snape and the focus character?

I think the best way to convey how this story works is to look at how she interacts with various different groups and, since I don’t feel like waiting until I’m didn’t wake up tired to re-draft this review, I’ll throw in more quotes than usual to cover up how choppy and repetitive the wordsmithing feels to me.

In Hermione’s case, Taylor’s background is used to to justify her recognizing Hermione as similar to who she used to be… even if she initially sees interacting with her to steer her in a more socially aware direction as a bit of a necessary chore. Taylor’s presence also has the side-effect of Hermione not being in the bathroom and, thus, not winding up with Harry and Ron.

I also like that the author understands Hermione well enough to recognize the implications of her choice of consequences for breaking the DA contract in fifth year:

“It’s not quite as important that we keep it secret as it was before,” I said. “That’s not to say I want anybody blabbing. But if he does start bragging to people, we’ll find ways to deal with it.”

“He won’t like how you deal with it, will he?” she asked. Hermione sounded almost gleeful.

She had a mean streak buried deep down; possibly it was the reason that we got along as well as we did. For all that she liked to pretend to be a good girl, she had a ruthless nature that I sometimes felt a little guilty for exploiting.

A Wand for Skitter, Chapter 54

Harry actually isn’t introduced to Taylor quickly though. This is one of those satisfyingly rare stories where the author sees the canon timeline as something to be shaped to their vision, rather than something to re-enact. Yes, she is in the same year, but the story doesn’t give any hint of that until she’s just about to actually go to Hogwarts and he’s kept completely off-screen until the winter holidays a third of the way through, aside from when a black-haired gryffindor boy with glasses is visibly paying attention to this unusual muggleborn Slytherin who steps in to head off trouble after Madam Hooch leaves during their first flying class.

As for Draco, she manages to maintain civil relations with him and exist as a walking indicator that he should think more carefully about his preconceptions.

“You know, Professor Snape has given orders for the prefects to give a speech about you this year.”

“What?”

“Most of them have likely heard about you, but there are always some that think they can make comments because they come from a good family.”

He sounded so superior to all of that.

“And what are they going to say?”

“The truth,” he said. “That your boggart is that you are going to murder us all and that bothering you will do the rest of us a favor.”

“What?”

“Professor Snape says that it’s good to cull the dunderheads early, lest they breed stupidity into the next generation.”

“Isn’t it a little late for that?” I asked.

Draco shrugged. “I wouldn’t know. Does that mean I’m stupid?”

I stared at him.

Making fun of himself? That showed a level of self confidence that I wouldn’t have expected from him.

A Wand for Skitter, Chapter 101

Among other reasons, because a post-canon Taylor who was shot from behind just after defeating Scion, who has been both hero and villain, and who woke up in the Harry Potter world as a survivor of an attempt to kill muggleborns on the Hogwarts roster does not take attempts by Slytherin students to bully her out of Slytherin lying down.

“If he was smart he’d have gotten a Pansy… I mean patsy to check his mail.”

Pansy gave me the finger from all the way across the table. She’d moved as far away from me as she could manage. She used the American gesture; apparently she’d gone to the trouble to learn that just for me. Apparently she’d been straining to listen in on our conversation as well.

A Wand for Skitter, Chapter 55

That said, it does escalate. This is a story where murder attempts eventually happen and named antagonists eventually die. However, it’s actually not one where I’d say that feels detrimental to the story… and I don’t mind, since it’s not going full “kill a very plot-significant character to prove that ‘no one is safe'”. No matter what story I’m reading, my emotional armour is on a hair trigger, so that’s pretty much a guaranteed emotional disengagement even in professional fiction.)

However, it is a 350,078-word completed story that covers the entire arc of winning against Voldemort, and it does do a pretty good job of getting the tone and scale for such an epic endeavour right, so that helps both Taylor’s approach to problems and the eventual character death fit in.

“So what do you intend to do about it?” I asked. “They just tried to murder possibly a quarter of the students remaining in Hogwarts. Whatever you think of muggles, do you think they’ll leave their children in this school once they found out what happened?”

“Nobody was hurt,” he said.

“Wizards think like that,” I asked. “Because they can heal from almost anything. Muggles are a lot more fragile, which means they ware even more protective of their children. If you don’t believe me, just ask professor…”

He waved his hands.

“I’ll think of something. The important thing is that you don’t spread rumors that I had anything to do with this.”

A Wand for Skitter, Chapter 69

Snape’s role in this is as a reluctant reformer at Dumbledore’s behest, including teaching her how to make dangerous potions safely after she re-creates Neville’s accidental boil-creating potion and almost kills Avery with an overdose of it when retaliating against an envelope full of Bubotuber pus.

“Giving people the benefit of the doubt will be the death of you,” Snape said.

“Not everyone can be redeemed.”

“And if I’d felt that way about you?”

Snape was silent for a moment. “It’s not normal for a child to be thinking about how to make me bleed to death.”

“I’d suspect that some of your students might disagree.”

There was a sound of expelled air, almost like Snape was trying to suppress a chuckle.

A Wand for Skitter, Chapter 7

As hinted at by that quote, Snape’s character and interactions with Taylor are, in and of themselves, worth reading for. For example:

“So you don’t think you need to check in on me?”

“I know I do,” he said. “But not because you are afraid. Compared to the hole I found you in, this must seem like a castle.”

“It is a castle.”

“Precisely.”

I stared at him, giving him a flat, unamused look. One should not encourage bad jokes; I’d learned my lesson with Clockblocker.

A Wand for Skitter, Chapter 8

All in all, it’s hard to make solid statements about characters’ roles because it uses that good kind of character writing where, if a character isn’t growing and changing, then the focus character’s perception of them is. (For example, it’s hard to sum up Snape succinctly, because, aside from being a complex character, Taylor keeps getting into situations that cause her perception of his opinion of her to shift.)

It also has some satisfyingly novel world-building and changes to the plot.

For example, Taylor gets detention in the Forbidden Forest, not because something is killing unicorns (because, in this timeline, Voldemort returned the year before Harry and Taylor went to Hogwarts), but because they’ve been keeping an eye on a unicorn mare who may need help with a difficult birth and that blend of threat (the forbidden forest) and peace (the Unicorn herd) is seen as suitable for students who have been caught fighting.

…or the castle having to be cleared of an infestation of new boggarts after Taylor’s class got traumatized by what the boggart turned into for some of them. This is returned to on several occasions, partly for plot reasons such as this:

The mercenaries in the front were backing away, green light flashing.

“You can’t kill me,” one of the Taylors said. “You think a Dark Lord would send you to kill one little girl? He’s sent you to die!”

Why were the Boggarts here, all of them?

It was the fear. The fear of over a hundred men, so thick that even my insects could smell it. It had to attract them like bees to nectar.

A Wand for Skitter, Chapter 114

…and partly for humour reasons. Both because it’s an amusing concept to read about teachers hunting down reports of monstrous versions of her loose in the castle and for moments like this:

As I went to bed that night, I saw the bodies of everyone I’d ever known piled in the corner.

“Riddikulus,” I said, pointing my wand.

The boggart skittered away and I went to sleep. My dreams weren’t good.

A Wand for Skitter, Chapter 79

All that said, the concept and how it’s implemented have a certain unavoidable “Author self-insert goes to Hogwarts” flavour to them. No matter how much an author dresses it up by finding or extrapolating an existing character who would agree with their perspective, that’s somewhat unavoidable when you have a crossover character rising to the challenge of steering history in a Harry Dresden-esque fashion… and it’s not helped by being written in first-person perspective.

(It’s also reminiscent of “adult Harry Potter” stories where Harry is an Auror or an unspeakable or something else and gets thrown into a situation. “Teen character, but aged up to adulthood” is a common technique fanfiction authors use to try to build an author surrogate or achieve OC-like results without accepting the reputation original characters have for poor quality writing.)

Good stories always suffer more from doing that than mediocre ones because there are so many stories that feel like that, unusually well written or not, and it becomes more noticeable as the story gets better.

I’m not sure whether it helps that her “jaded old soldier in a child’s body” personality makes many others see her as legitimately a potential threat if she snaps or whether that’s also close enough to certain “edgy” tropes that show up in various stories for its overall effect to balance out to zero.

It’s also interesting in that it starts to dip into Harry Potter Book 7 territory at around the half-way point, but then climbs back out of it fairly quickly… and, like in many other moments, it turns to clever original world-building to be familiar to canon events, but different. (eg. Their raid on the Ministry to destroy the source of the Trace… though I’ll caution that is a bit of a dropped plot thread, not really coming to anything.)

A moment later we were inside. Two more doors, and we reached what I thought had to be the source of the Trace.

I’d come across a historical record of the Ministry stealing an Analytical Engine; it had been created by Charles Babbage in the late 1830s as one of the world’s first computers. The official, muggle record was that it had never been completed by funding issues, but the truth was that he had been confunded and obliviated, and the engine had been stolen.

The Hogwarts Express had been acquired in a similar way, stolen from the very muggles that the Wizards determined to be inferior.

The engine was really nothing more than a primitive calculator, but my guess was that they wouldn’t have kept it here for that. They wouldn’t have enchanted it if they didn’t need it, and the timing… it had been stolen shortly before the Trace had been implemented was suspicious.

A Wand for Skitter, Chapter 77

(It’s nice to see Taylor recognizing that fraud or not, Gilderoy Lockhart knows PR, and going to ask his advice. Funny enough, in this timeline, thanks to spending time with Taylor, Hermione recognizes that Lockhart is a fraud early on.)

I do think it felt a bit surprising in an “author straining suspension of disbelief” way to have the Triwizard Tournament still crop up as an event, but at least playing with the timeline meant that Viktor Krum was too young to be part of the Durmstrang group, so that changes things up a bit, and the most novel part is probably that there isn’t a fourth champion.

I won’t spoil it, but there’s also an elegantly crafted point at the end of one chapter when I honestly started to worry that some accidentally discovered dark magic was going to make her walk the same path that turned Anakin Skywalker into Darth Vader… so desperate to gain the power to change things that they can’t see the road they’re walking.

Taylor IS sometimes overpowered, but, as mentioned, it reminds me more of Harry Dresden as I know him from the first Dresden Files book and a bunch of fanfiction. Yes, overpowered, but the narrative is engaging enough and satisfying enough to justify it as a means to an end.

The tone is darker than a lot of Harry Potter fanfiction, but it is a Worm crossover.

That said, there are occasionally moments of humour. For example, when Taylor decides to use exploding snap cards as the detonator for a barrel of gunpowder to take out a squad of Death Eaters.

The bugs had to slap the cards down with a certain amount of violence, which meant that I’d had to use nightmarishly large bugs. There had been some in the bowels of the castle that I hadn’t even been able to identify, including some spiders the size of a grown man’s fist.

In an effort to confuse the people watching, I’d set up a little green felt on the top of the barrel, and I’d dressed the spiders up in cute little gamblers outfits. I’d hoped that the incongruity of the scene would slow whoever saw it down for long enough.

A Wand for Skitter, Chapter 87

The way Voldemort goes down is very fitting and the epilogue is also surprisingly novel and satisfying. (Maybe it’s because it has that classic sci-fi flavour that I really need to get more of.)

…oh, and it does have (very) infrequent typos. I noticed a “Sevarus” and a “styill” (still), among others.

In the end, I’ll give it a 4.4 out of 5. It’s a satisfying and enjoyable story.

Posted in Fanfiction | Leave a comment

Tooltips for syntax highlights in QTextEdit/QPlainTextEdit

While I was experimenting with adding nlprule-based grammar-checking to my spell-checked QPlainTextEdit subclass (not yet polished enough to share, but there’s a cut-down proof of concept for the nlprule integration on StackOverflow if you want it), I ran into a little problem.

Apparently, there’s an unresolved bug (QTBUG-80524 and duplicates) where QTextEdit and QPlainTextEdit don’t take syntax highlighter-set changes into account when figuring out whether to display a tooltip and what to show.

The solution? Override the lookup behaviour. Here’s how you do it:

class TooltipPlainTextEdit(QPlainTextEdit):
    def __init__(self, *args):
        QPlainTextEdit.__init__(self, *args)

        # Opt in to the events we need to override
        self.setMouseTracking(True)

    def event(self, event) -> bool:
        """Reimplement tooltip lookup to get nlprule messages working"""
        if event.type() == QEvent.ToolTip:
            # Get a secondary text cursor, taking margins into account
            pos = event.pos()
            pos.setX(pos.x() - self.viewportMargins().left())
            pos.setY(pos.y() - self.viewportMargins().top())
            cursor = self.cursorForPosition(pos)

            # QTextCursor doesn't have a quicker way to get
            # highlighter-applied formats, so find the one that our
            # cursor is inside
            for fmt in cursor.block().layout().formats():
                if (fmt.start <= cursor.position() and
                        fmt.start + fmt.length >= cursor.position()):

                    # Calculate the bounding box for the text and feed
                    # it as the rectangle for requesting that the
                    # tooltip auto-hide when the cursor exits
                    cursor.setPosition(fmt.start)
                    cursor.setPosition(fmt.start + fmt.length,
                                       QTextCursor.KeepAnchor)
                    QToolTip.showText(event.globalPos(), fmt.format.toolTip(),
                        self, self.cursorRect(cursor))
                    return True

        # Fall back to the default behaviour
        return super(TooltipPlainTextEdit, self).event(event)

…and that’s it. Your QTextCharFormat.setToolTip calls should now Just Work™.

Posted in Geek Stuff | Leave a comment

Fanfiction – When Life Gives You Lemons

When Life Gives You Lemons by Baked The Author

Crossover: Worm/Portal
Length: 77,208
Status: Complete

What if Taylor Hebert triggered as a Tinker and then cobbled together a teleportation device that brought her to Aperture Science? What if GlaDOS and the other cores were more sane?

What do you get?

This is mostly a story about Taylor working her way through abandoned Old Aperture (Act 1), continuing to do so while interacting with the Aperture Science cast via radio (Act 2), and then staying alive in the face of a cleanly incorporated crisis and antagonist (Act 3).

However, it also constructs an alternate universe that is at once familiar, yet coherent, with details like Caroline being just as much a monster as Cave Johnson and GlaDOS actually originating as a digital upload of one of their department heads, a woman named Gladys Emerson.

It’s also got a nice sense of humour that’s very fitting for a Portal story, with segments like this:

but there were a few other useful pieces of trash lying around, along with a box of 15 Aperture Science Blueberry Granola Bars (“Tastes like real blueberries! Will never go bad, or else!”)

…as well as various other fun things that I’ll leave you to discover, because the setup is a little too long to be comfortably quoted.

A very satisfying “read it for the characters” story with some nice world-building… just don’t go into this expecting Taylor to meet them face-to-face or contact to be re-established with Earth Bet until the last couple of chapters.

At least 4.7 out of 5.

P.S. Look forward to seeing how, in this case, the cake is not a lie.

Posted in Fanfiction | Leave a comment

A QLineEdit Replacement with Spell-Checking

TL;DR: Here’s the code

Today, I recevied a question which prompted me to revisit my old An Improved QPlainTextEdit for PyQt5 post and, along the way, I got a little nerd-sniped on the idea of adding spell-checking to QLineEdit too. (After all, I will need that for my own use sooner or later.)

Long story short, since QLineEdit doesn’t support syntax highlighting, you either have to re-implement the paint event yourself (which is a little too low-level for me to want to be responsible for shaking the bugs out of it) or you can just reconfigure QPlainTextEdit to look and act like QLineEdit.

It’s not perfect, since it can’t be used in situations like setting a custom QLineEdit subclass for a QComboBox… but since I already have a QPlainTextEdit that does spell-checking…

The process basically breaks down into two roughly equal parts: Working around the warts in how the QWidget APIs expect you to customize your widget sizing heuristics and everything else.

Start by turning off all word-wrapping and forcing the scrollbars to be hidden:

self.setLineWrapMode(QPlainTextEdit.NoWrap)
self.setWordWrapMode(QTextOption.NoWrap)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

Then, actually force the contents to stay on a single line. You’d think we could just use self.document().setMaximumBlockCount(1), but that disables Undo/Redo, makes Ctrl+V paste the last line when I think it’s more natural to preserve the first, and requires you to override keyPressEvent to prevent Enter/Key_Enter from erasing the contents of the field, so we have to go in manually:

def cb_textChanged(self):
    if self.document().blockCount() > 1:
        self.document().setPlainText(self.document().firstBlock().text())

self.textChanged.connect(self.cb_textChanged)

Going this route also makes it really easy to implement things like converting one-per-line lists of tags/keywords into comma-separated lists. Just replace self.document().firstBlock().text() with document().toRawText() or document().toPlainText() (depending on whether you want a little bit of Unicode normalization as described in the Qt docs) and do something like .replace('\n', ', ') before feeding it to setPlainText. (Though I’d probably use a QRegularExpression since it makes it easier to normalize whitespace while doing the conversion.)

Finally, the last difference I noticed and accounted for was the behaviour of the Tab key. That can be fixed as follows:

self.setTabChangesFocus(True)

def focusInEvent(self, e: QFocusEvent):
    """Override focusInEvent to mimic QLineEdit behaviour"""
    super(OneLineSpellTextEdit, self).focusInEvent(e)

    # TODO: Are there any other things I'm supposed to be checking for?
    if e.reason() in (Qt.BacktabFocusReason, Qt.ShortcutFocusReason,
                      Qt.TabFocusReason):
        self.selectAll()

def focusOutEvent(self, e: QFocusEvent):
    """Override focusOutEvent to mimic QLineEdit behaviour"""
    super(OneLineSpellTextEdit, self).focusOutEvent(e)

    # TODO: Are there any other things I'm supposed to be checking for?
    if e.reason() in (Qt.BacktabFocusReason, Qt.MouseFocusReason,
                      Qt.ShortcutFocusReason, Qt.TabFocusReason):
        # De-select everything and move the cursor to the end
        cur = self.textCursor()
        cur.movePosition(QTextCursor.End)
        self.setTextCursor(cur)

Now for the big hassle… redefining the widget sizing behaviour. You’d think that self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) would be enough, but QPlainTextEdit defines unhelpful sizeHint and minimumSizeHint values and I don’t know where it’s getting them from.

I’ve seen other people doing various hacks, but I saw problems in all of them:

  1. Some people set the fixedHeight on the widget to the lineSpacing() given by the QFontMetrics from its font() … but that doesn’t properly account for the other various bits in the system like padding and borders from the QTextDocument and QStyle which become relevant when a widget is so short.
  2. Some people create a hidden QLineEdit with the same font settings, trigger layout calculations for it, and then harvest the values… but that’s just unacceptably hacky for me.

…so, here’s what I cobbled together from various sources. I don’t know if it’s 100% correct, but it seems to work:

def minimumSizeHint(self):
    """Redefine minimum size hint to match QLineEdit"""
    block_fmt = self.document().firstBlock().blockFormat()
    width = super(OneLineSpellTextEdit, self).minimumSizeHint().width()
    height = int(
        QFontMetricsF(self.font()).lineSpacing() +  # noqa
        block_fmt.topMargin() + block_fmt.bottomMargin() +  # noqa
        self.document().documentMargin() +  # noqa
        2 * self.frameWidth()
    )

    style_opts = QStyleOptionFrame()
    style_opts.initFrom(self)
    style_opts.lineWidth = self.frameWidth()
    # TODO: Is it correct that I'm achieving the correct content height
    #       under test conditions by feeding self.frameWidth() to both
    #       QStyleOptionFrame.lineWidth and the sizeFromContents height
    #       calculation?

    return self.style().sizeFromContents(
        QStyle.CT_LineEdit,
        style_opts,
        QSize(width, height),
        self
    )

def sizeHint(self):
    """Reuse minimumSizeHint for sizeHint"""
    return self.minimumSizeHint()

So… how well does it work? Well, here’s the full code. Try running it yourself. The demonstration will present it side-by-side with a regular QLineEdit.

(Let me know about any divergences in their behaviour that I missed so I can either fix them or document them as intentionally retained.)

Posted in Geek Stuff | Leave a comment

NT_STATUS_ACCOUNT_EXPIRED

TL:DR: Check that your system clock is set correctly …and use pdbedit -u accountname -K 0 to disable expiry for the account if that’s what it’s supposed to be in the first place.

…because I just spent an hour tearing my hair out before realizing the solution.

If you’ve just created an account named accountname with sudo smbpasswd -a accountname, under what should be the default Ubuntu settings for Samba, and you can’t log into your shares, and sudo pdbedit -vu accountname looks like this…

Unix username:        accountname
NT username:          
Account Flags:        [U          ]
User SID:             S-1-5-21-4147919797-2373757126-213116460-1000
Primary Group SID:    S-1-5-21-4147919797-2373757126-213116460-513
Full Name:            Your Name
Home Directory:       \\YOURPC\accountname
HomeDir Drive:        
Logon Script:         
Profile Path:         \\YOURPC\accountname\profile
Domain:               YOURPC
Account desc:         
Workstations:         
Munged dial:          
Logon time:           0
Logoff time:          Wed, 06 Feb 2036 15:06:39 UTC
Kickoff time:         Wed, 06 Feb 2036 15:06:39 UTC
Password last set:    Tue, 28 Sep 2088 08:03:11 UTC
Password can change:  Tue, 28 Sep 2088 08:03:11 UTC
Password must change: never
Last bad password   : 0
Bad password count  : 0
Logon hours         : FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

…and smbclient --user=accountname '\\YOURPC\sharename' is saying NT_STATUS_ACCOUNT_EXPIRED, do two things:

First, figure out why the heck your system clock thinks the year is 2088. Second, if you don’t want the account to ever expire, actually ask for it:

sudo pdbedit -u accountname -K 0

I was so fixated on the assumption that I had NTP time synchronization set up properly that I didn’t notice the date in this log line and went straight to investigating check_samsec.c:206:

[2088/09/28 08:31:20.028105,  1] ../../source3/auth/check_samsec.c:206(sam_account_ok)
  sam_account_ok: Account for user 'ssokolow' has expired.

…of course, it really didn’t help that, when I actually tried some suggested ways to list expired accounts, nothing showed up.

Today I learned about a weird quirk of using the “try it out without installing” mode of a Lubuntu Linux LiveUSB stick as a hacky way to turn a small form factor PC with only one hard drive bay into a temporary network-attached external drive housing.

Posted in Geek Stuff | Leave a comment